嵌入式 Linux 如何操作 GPIO ?
是有個頭文件已經定義好晶元的GPIO地址,在驅動程序中直接調用么?還是要自己定義GOPIO地址?
如果是在已經適配好的linux內核上,那麼相信已經有了完成的gpiochip,可以在用戶空間/sys/class/gpio目錄下看到,如:
exportgpiochip0/gpiochip32/gpiochip64/
gpiochip96/unexport然後對照手冊看下需要用到哪個GPIO,舉個例子:
如果使想用GPIO1_20
那麼GPIO Number就是 1 x 32 + 20 = 54使用分兩種情況:
1. 用戶空間:echo 54 &> export這樣在這個/sys/class/gpio目錄下就會產生gpio54文件夾在文件夾下需要用到的有兩個文件:
direction 用來配置輸入(in)還是輸出(out) value 如果這個GPIO配置成了輸入,那麼通過cat value可以查看當前這個GPIO是什麼電位;如果配置成了輸出,那麼可以通過echo 1/0 &> value給這個GPIO口指定輸出電平。2. 內核空間(驅動):
#include &gpio_free(54);
具體GPIO介面詳見:Linux/Documentation/gpio.txt希望能夠幫到你。先將GPIO源文件交叉編譯成與嵌入式設備內核相對應的驅動模塊,再載入進嵌入式設備中。成功後,/dev目錄下應該有類似GPIO-Control的設備文件。這時就可以調用了。 調用方法大致如下:
int fd;
fd = ::open("/dev/GPIO-Control", 0);if (fd &< 0) { perror("open device leds"); exit(1); }ioctl(fd, 1, 0);
::close(fd);
//ps:ioctl(fd, 1, 0); 函數里參數1表示低電平,和GPIO相連的燈亮。0表示GPIO引腳號。
很不幸,Linux只是強制了用戶程序介面,對於底層GPIO的完全由驅動或CPU 的MACHINE自行處理。
不同CPU的處理GPIO的流程,寄存器完全不一樣,差別還很大。有興趣你可以比較一個Intel Xscale t 和S3C6410 CPU的GPIO讀寫流程,完全不一樣。 所以做出以一個通用 GPIO的驅動不可能的。而且往往不止一個頭文件。幸運的是,GPIO又最常見操作,所以在產家往往在自己的CPU的arch的源碼和常用驅動實現, 你抄代碼就行了。
至於前面那個說用read ,ioctl之類,那是開發人員已經封裝這個驅動情況,才能這樣用。嵌入式系統對GPIO的操作通常可以分為BSP中驅動程序對GPIO的操作與用戶層程序對GPIO的操作,其實本質上來說用戶層程序需要通過GPIO的驅動程序,才可以對GPIO進行操作,所以嵌入式系統對GPIO的操作本質來說應該是需要一個GPIO的驅動來對硬體進行操作,其他的程序,包括用戶層程序和其他的驅動程序,都需要和GPIO的驅動程序進行交互,才可以操作GPIO 當然也有一種變通的辦法,讓用戶層程序直接控制GPIO,那就是由BSP將GPIO的所有控制寄存器映射到用戶程序空間,由此用戶應用程序可以直接訪問GPIO的控制寄存器,同樣達到控制GPIO的目的。
基本步驟是
1'映射GPIO相關的物理地址為虛擬的虛擬地址
2'配置映射後的GPIO寄存器3'根據功能提供(非)阻塞'非同步/同步的方式以及mmap等介面4'載入玩ko模塊之後,在userspace的posix r/w 介面操作即可我說下ARM中的一般用法吧:1.查datasheet上GPIO的物理地址2.ioremap 把物理地址映射到虛擬地址3.然後直接操作映射出來的地址
Linux從2.6.21開始提供gpiolib作為GPIO操作的基礎組件。這是gpiolib的中文文檔:https://www.kernel.org/doc/Documentation/zh_CN/gpio.txt,英語在這裡:https://www.kernel.org/doc/Documentation/gpio.txt對於SoC來說,很多時候GPIO都是復用的,文中有提到gpiolib中沒有為GPIO復用提供介面。另外gpiolib也沒有設置GPIO的上拉和下拉的函數介面。所以在操作某一個GPIO的時候,我們會看到這樣的代碼:
gpio_request(S5PV210_GPB(2), "GPB");
s3c_gpio_cfgpin(S5PV210_GPB(2),S3C_GPIO_OUTPUT); s3c_gpio_setpull(S5PV210_GPB(2), S3C_GPIO_PULL_UP); gpio_direction_output(S5PV210_GPB(2), 1); gpio_free(S5PV210_GPB(2)); 上面的gpio_request,gpio_direction_output,gpio_free都是gpiolib中提供的函數,但是s3c_gpio_cfgpin,s3c_gpio_setpull就是三星另外實現的設置復用和設置上拉的函數。設置復用和設置上拉的函數廠商不同也不同。另外並不是每一個廠商的BSP都會按照gpiolib來實現GPIO的驅動,有的廠商像Ralink,就是自己單獨實現,提供不同於其他BSP的函數介面。無論怎麼實現GPIO驅動,本質也是操作寄存器。所以樓中直接對寄存器地址讀寫也可以達到目的,但代碼的可維護性和可移植性就會變差。要知道具體晶元Linux內核裡頭怎麼操作GPIO,還是要看一下代碼的。看看對應的SoC的GPIO驅動是如何實現的,才能知道具體的介面。如果要實現GPIO驅動的話,可以看看內核源碼裡頭的drivers/gpio下的源碼。提問者是不是寫單片機程序?寫單片機C程序的時候會include一個頭文件,那個頭文件里會定義特殊功能寄存器地址等等,在其他嵌入式處理器里(ARM,DSP等)同樣有那些文件,在處理器複位後還沒有進入到C程序main()之前做好一切準備工作。比如S3C2410就有2410init.s,2410addr.s,memcfg.s,option.s等等一堆文件。
在未用操作系統的項目里,這些文件都是要添加到工程里的,這部分代碼就是所說的「啟動代碼」。而若你要移植上操作系統,則在操作系統啟動時,會先有一個引導系統啟動的程序Bootloader啟動,相當於X86PC的BIOS程序,而Bootloader里,就包含了前面所說的啟動代碼文件。
只要有了那些文件,無論你是寫在操作系統下的驅動,還是寫裸機程序,都可以不用像彙編里那樣非要寫地址或者定義地址才能操作IO,而是直接用定義好的名字,比如rGPFDAT=0x0f(S3C2410舉例)。
不知道你明白了沒有?一開始寫的答案很簡單,但是知乎上的同學都很認真的回答問題,我就靜下心來回答一下。我不是高手,好久沒接觸這方面東西了,回答之中不恰當之處,歡迎大家指正。直接操作吧,只不過LINUX下的話要按照LINUX提供的「框」寫,比如open,read,ioctl等函數都要寫。
要想操作gpio就需要知道他在內存的那個地址(可以在datasheet裡面查找),然後看你選這是彙編還是C語言,如果是彙編就需要知道它的助記符,如果是C語言,我給你一個例子,假如,我要操作LED他的地址為,0x0056
#define rGPIO (*(unsigned int *)0X0056)
直接讀寫GPIO就可以了。因為之前你已經在頭文件里定義過了。
查看晶元文檔,配置gpio工作方式,採用讀修改寫的方式操作設備地址
分為兩種:驅動使用內核提供的GPIOLIB;用戶程序可通過sysfs來讀寫最方便。如果用戶程序需要以事件形式讀取gpio,那麼內核有個gpiokey驅動將gpio轉為input事件,通過/dev/event來讀取。
補充:前面劉凱回答比較詳細,不懂可以去看他的答案,但input方法用(知道)的人不多,大概3.0後版本內核才加入。GPIO本來就是晶元引腳,本來你是可以直接用的,但是因為某一天該引腳有第二個功能,比如高阻態。才有對引腳封裝成GPIO功能也就輸入輸出功能,第二種功能。
iowrite32(目的地址,你要設置的值)
看datasheet. 一組GPIO口本質上是對應著一個物理寄存器的,在地址空間里映射為某個地址值,往該地址值直接寫數據就是響應地把對應的電平拉高或者拉低,驅動里要做的就是看如何用這個GPIO口做什麼功能,並為上層應用程序提供好用的介面。內核里有GPIO lib試圖統一操作GPIO的方法。
系統操作GPIO的方式與WIN差不多 底層都是彙編語言所編寫,而且LINUX的GCC會支持一些拓展的標準以達到操作底層的目的。作為程序員,想要操作GPIO只能遵循POXIS規範,使用linux的模塊機制進行操作 具體可以買linux驅動相關書籍
就我個人的理解,在操作系統中一般都是用提供的庫函數來操作GPIO的,一般庫函數應該自己分析GPIO的基地址是否定義。我認為從用戶角度而言,應該不需要用戶去定義GPIO的基地址,一般在bootloader(或者BIOS)或者Linux啟動過程中就把GPIO的基地址初始化了。當然,不同的晶元,不同的板塊設計都是不同的,有可能把GPIO映射到I/O上,也可能把GPIO映射到MMIO。
推薦閱讀:
※開源硬體的優勢在哪裡?
※我想問一下,計算機專業 特別是嵌入式方向以後的發展前景好嗎?(如果是相對於自動化專業呢?),
※一個合格的嵌入式軟體開發工程師要掌握哪些知識?
※為什麼晶元同一組的引腳不在同一側,而是四側都有?