生活随笔
收集整理的這篇文章主要介紹了
树莓派linux驱动学习之LED控制
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ? ? ? 前面我們編寫了hello world的程序,接下來繼續研究GPIO功能,通過GPIO來控制LED的亮滅,這在單片機中應該算是十分簡單的一個程序了,但是在Linux系統中控制GPIO沒有那么簡單,難點就在于GPIO地址的獲取,也是我一直在糾結的問題。
一、GPIO地址
? ? ? ? 我看了中嵌的嵌入式開發視頻,里面使用三星2440控制LED的亮滅,但是驅動程序中沒有寫清楚具體的底層是如何實現的,這也是我查找的重點。我首先翻閱了樹莓派CPU(bcm2835)的芯片手冊,查到了GPIO的物理地址:
? ? ? ? 但是在芯片資料的最開始,有提到芯片內部已經把上圖中的物理總線地址抽象到了面對操作系統的物理地址:
? ? ? ? 所以,我們在編寫驅動程序的時候,IO空間的起始地址是0x20000000,加上GPIO的偏移量2000000,所以GPIO的物理地址應該是從0x20200000開始的,然后在這個基礎上進行Linux系統的MMU內存虛擬化管理,銀蛇到虛擬地址上。
二、硬件平臺
? ? ? ? 我在樹莓派的擴展口的GPIO 17上接上了一個LED:
三、編寫驅動代碼
? ? ? ? 一般的設備驅動我們需要設置主設備號和次設備號,在編寫應用程序的時候還要生成設備文件,比較麻煩。Linux針對像LED這樣的操作,有一種設備叫做混雜設備:是一種特殊的字符設備,雜設備早已經存在,是為了給開發者一個較為簡單的操作方式,因為不用再重新申請一個設備號了(misc就是混雜設備的意思)。
? ? ? ? 驅動代碼:
[cpp]?view plaincopy
#include?<linux/miscdevice.h>?? #include?<linux/delay.h>?? #include?<asm/irq.h>?? #include?<linux/kernel.h>?? #include?<linux/module.h>?? #include?<linux/init.h>?? #include?<linux/mm.h>?? #include?<linux/fs.h>?? #include?<linux/types.h>?? #include?<linux/delay.h>?? #include?<linux/moduleparam.h>?? #include?<linux/slab.h>?? #include?<linux/errno.h>?? #include?<linux/ioctl.h>?? #include?<linux/cdev.h>?? #include?<linux/string.h>?? #include?<linux/list.h>?? #include?<linux/pci.h>?? #include?<asm/uaccess.h>?? #include?<asm/atomic.h>?? #include?<asm/unistd.h>?? #include?<asm/io.h>?? #include?<asm/uaccess.h>?? #include?<linux/ioport.h>?? ?? #include?"bcm2835.h"?? ?? ?? #define?PIN?RPI_GPIO_P1_11?? ?? int?open_state?=?0;??????????? ?? static?int?leds_open(struct?inode?*inode,?struct?file?*filp)?? {?? ????if(open_state?==?0)???? ????{???? ????????open_state?=??1;???? ????????printk("Open?file?suc!\n");???? ????????return?0;???? ????}???? ????else???? ????{???? ????????printk("The?file?has?opened!\n");???? ????????return?-1;???? ????}???? }?? ?? static?int?leds_ioctl(struct?file*filp,?unsigned?int?cmd,?unsigned?long?arg)?? {?? ????switch(cmd)???? ????{???? ????????case?0:???? ????????????bcm2835_gpio_clr(PIN);?? ????????????printk("LED?OFF!\n");?? ????????????break;???? ????????case?1:???? ????????????bcm2835_gpio_set(PIN);?? ????????????printk("LED?ON!\n");?? ????????????break;???? ?? ????????default:???? ????????????return-EINVAL;???? ????}???? ?? ????return?0;?? }?? ?? static?int?leds_release(struct?inode?*inode,?struct?file?*filp)?? {?? ????if(open_state?==?1)???? ????{???? ????????open_state?=??0;???? ????????printk("close?file?suc!\n");???? ????????return?0;???? ????}???? ????else???? ????{???? ????????printk("The?file?has?closed!\n");???? ????????return?-1;???? ????}???? }?? ?? static?const?struct?file_operations?leds_fops?=?{?? ????.owner?=?THIS_MODULE,?? ????.open?=?leds_open,?? ????.unlocked_ioctl?=?leds_ioctl,?? ????.release?=?leds_release,?? };?? ?? static?struct?miscdevice?misc?=?{?? ????.minor?=MISC_DYNAMIC_MINOR,?? ????.name?="my_leds",?? ????.fops?=&leds_fops,?? };?? ?? ?? static?int?__init?leds_init(void)?? {?? ????int?ret;?? ?? ?????? ????ret?=misc_register(&misc);?? ?? ?????? ????bcm2835_gpio_fsel(PIN,?BCM2835_GPIO_FSEL_OUTP);?? ?? ?????? ????bcm2835_gpio_set(PIN);?? ?? ????printk("ledsinit.\n");?? ????return?ret;?? }?? ?? static?void?leds_exit(void)?? {?? ?????? ????bcm2835_gpio_clr(PIN);?? ?? ????misc_deregister(&misc);?????????? ?? ????printk("leds_exit\n");?? }?? ?? module_init(leds_init);?? module_exit(leds_exit);?? ?? MODULE_AUTHOR("Hu?Chunxu");?? MODULE_LICENSE("GPL");?? ? ? 硬件相關操作:
[cpp]?view plaincopy
#include?<linux/miscdevice.h>?? #include?<linux/delay.h>?? #include?<asm/irq.h>?? #include?<linux/kernel.h>?? #include?<linux/module.h>?? #include?<linux/init.h>?? #include?<linux/mm.h>?? #include?<linux/fs.h>?? #include?<linux/types.h>?? #include?<linux/delay.h>?? #include?<linux/moduleparam.h>?? #include?<linux/slab.h>?? #include?<linux/errno.h>?? #include?<linux/ioctl.h>?? #include?<linux/cdev.h>?? #include?<linux/string.h>?? #include?<linux/list.h>?? #include?<linux/pci.h>?? #include?<asm/uaccess.h>?? #include?<asm/atomic.h>?? #include?<asm/unistd.h>?? #include?<asm/io.h>?? #include?<asm/uaccess.h>?? #include?<linux/ioport.h>?? ?? #include?"bcm2835.h"?? ?? int?bcm2835_gpio_fsel(uint8_t?pin,?uint8_t?mode)?? {?? ?????? ????volatile?uint32_t?*?bcm2835_gpio?=?(volatile?uint32_t?*)ioremap(BCM2835_GPIO_BASE,?16);?? ????volatile?uint32_t?*?bcm2835_gpio_fsel?=?bcm2835_gpio?+?BCM2835_GPFSEL0/4?+?(pin/10);?? ????uint8_t???shift?=?(pin?%?10)?*?3;?? ????uint32_t??value?=?mode?<<?shift;?? ????*bcm2835_gpio_fsel?=?*bcm2835_gpio_fsel?|?value;?? ?? ????printk("fsel?address:?0x%lx?:?%x\n",?bcm2835_gpio_fsel,?*bcm2835_gpio_fsel);?? ?? ????return?0;?? }?? ?? int?bcm2835_gpio_set(uint8_t?pin)?? {?? ?????? ????volatile?uint32_t?*?bcm2835_gpio?=?(volatile?uint32_t?*)ioremap(BCM2835_GPIO_BASE,?16);?? ????volatile?uint32_t?*?bcm2835_gpio_set?=?bcm2835_gpio?+?BCM2835_GPSET0/4?+?pin/32;?? ????uint8_t???shift?=?pin?%?32;?? ????uint32_t??value?=?1?<<?shift;?? ????*bcm2835_gpio_set?=?*bcm2835_gpio_set?|?value;?? ?? ????printk("set?address:??0x%lx?:?%x\n",?bcm2835_gpio_set,?*bcm2835_gpio_set);?? ?? ????return?0;?? }?? ?? int?bcm2835_gpio_clr(uint8_t?pin)?? {?? ????? ????volatile?uint32_t?*?bcm2835_gpio?=?(volatile?uint32_t?*)ioremap(BCM2835_GPIO_BASE,?16);?? ????volatile?uint32_t?*?bcm2835_gpio_clr?=?bcm2835_gpio?+?BCM2835_GPCLR0/4?+?pin/32;?? ????uint8_t???shift?=?pin?%?32;?? ????uint32_t??value?=?1?<<?shift;?? ????*bcm2835_gpio_clr?=?*bcm2835_gpio_clr?|?value;?? ?????? ????printk("clr?address:??0x%lx?:?%x\n",?bcm2835_gpio_clr,?*bcm2835_gpio_clr);?? ?? ????return?0;?? }?? ? ? ? ? 應用測試程序:
[cpp]?view plaincopy
#include?<stdio.h>?? #include?<stdlib.h>?? #include?<unistd.h>?? #include?<sys/ioctl.h>?? ?? int?main(int?argc,?char?**argv)?? {?? ????int?on;?? ????int?fd;?? ????if?(argc?!=?2?||?sscanf(argv[1],"%d",?&on)?!=?1?||on?<?0?||?on?>?1?)?{?? ????????fprintf(stderr,?"Usage:%s?0|1\n",argv[0]);?? ????????exit(1);?? ????}?? ????fd?=?open("/dev/my_leds",?0);?? ????if?(fd?<?0)?{?? ????????perror("open?device?leds");?? ????????exit(1);?? ????}?? ?????? ????if(on){?? ????????printf("turn?on?leds!\n");?? ????????ioctl(fd,?1);?? ????}?? ????else?{?? ????????printf("turn?off?leds!\n");?? ????????ioctl(fd,?0);?? ????}?? ????close(fd);?? ????return?0;?? }?? ? ? ? 分別編譯,插入模塊,然后運行測試程序,可以控制LED的亮滅了。
----------------------------------------------------------------
歡迎大家轉載我的文章。
總結
以上是生活随笔為你收集整理的树莓派linux驱动学习之LED控制的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。