嵌入式 Linux 如何操作 GPIO ?

是有個頭文件已經定義好晶元的GPIO地址,在驅動程序中直接調用么?還是要自己定義GOPIO地址?


如果是在已經適配好的linux內核上,那麼相信已經有了完成的gpiochip,可以在用戶空間/sys/class/gpio目錄下看到,如:

export

gpiochip0/

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_request_one(54, GPIOF_INIT_HIGH, "gpio1_20")

這裡是配置成輸出,默認高電平,別名(label)為gpio1_20——就是給你的IO口取個名字。

gpio_request_one(54, GPIOF_IN, "gpio1_20")

這個就是配置成輸入。

使用完後別忘了free

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)

下面就可以直接對rGPIO 賦值,可以查看datasheet 高電瓶還是低電瓶有效多少位,然後進行給定值,由於現在用的手機,實際代碼在電腦,有需要可以後面再給你補。


直接讀寫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。


推薦閱讀:

開源硬體的優勢在哪裡?
我想問一下,計算機專業 特別是嵌入式方向以後的發展前景好嗎?(如果是相對於自動化專業呢?),
一個合格的嵌入式軟體開發工程師要掌握哪些知識?
為什麼晶元同一組的引腳不在同一側,而是四側都有?

TAG:Linux | 嵌入式系統 | Linux開發 | 嵌入式開發 |