一篇帶給你GPIO 軟件框架
GPIO 八種工作模式詳解
接著上一篇的講,我們上一篇研究了 GPIO 的硬件結(jié)構(gòu),其來源于 STM32 官方手冊,研究了 GPIO 的八種工作模式和推挽輸出及開漏輸出原理,接下來我們研究 GPIO 的軟件部分,分別從單片機平臺和 Linux 平臺來研究。
1、單片機平臺
單片機平臺編寫 GPIO 口程序,以 STM32F103 為例,有三種模式:庫函數(shù)、HAL庫、寄存器。
使用庫函數(shù)的方式操控 GPIO 方式如下:
- void LED_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能 PB 端口時鐘
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
- //PB5 端口配置
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度
- GPIO_Init(GPIOB, &GPIO_InitStructure); //根據(jù)設(shè)定參數(shù)初始化 GPIOB.5
- GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 輸出高
- }
上述代碼就是使用庫函數(shù)來初始化 STM32 的一個 IO 為輸出功能,可以看出上述初始化代碼中重點要做的事情有一下幾個:
①、使能指定 GPIO 的時鐘。
②、初始化 GPIO,比如輸出功能、上拉、速度等等。
③、STM32 有的 IO 可以作為其它外設(shè)引腳,也就是 IO 復(fù)用,如果要將 IO 作為其它外設(shè)引腳使用的話就需要設(shè)置 IO 的復(fù)用功能。
④、最后設(shè)置 GPIO 輸出高電平或者低電平。
STM32 的 GPIO 初始化就是以上四步,使用庫函數(shù)操作 GPIO 還是很簡單的。但是我們知道 STM32F1 系列是有庫函數(shù)的,但是 STM32F7 系列就沒有庫函數(shù)了,ST 公司沒有出,STM32F7 只有 HAL 庫和寄存器兩種操作方式。
2、嵌入式 Linux 平臺
先總結(jié)一句:不管是單片機還是高端 ARM 平臺,最底層都是寄存器,硬件之上就是寄存器,任何封裝形式到最底層就是操作寄存器。
對于上了 Linux 系統(tǒng)的平臺,我們有其他方法,讓它可以像單片機一樣簡單的操作 IO 口,這得益于各路 Linux 大神對系統(tǒng)底層的封裝。
在 Linux 中有 pinctrl 和 gpio 子系統(tǒng),它們提供了 API 接口給你使用,讓你方便的操控 GPIO 口。
Linux 內(nèi)核針對 PIN 的配置推出了 pinctrl 子系統(tǒng),對 GPIO 的配置推出了 gpio 子系統(tǒng)。
上面這句話很重要,我詳細解釋一下:這里是將 pin 腳和控制 IO 口輸入輸出分離。
pinctrl 子系統(tǒng)管理 200 個 IO 口的上拉下拉電阻,電流驅(qū)動能力,是硬件底層的存在。如果 pinctrl 將某個 pin 腳初始化成了普通 GPIO 而不是 IIC 或者 SPI,那么接下來我們就可以使用 gpio 子系統(tǒng)的 API 去操作 IO 口輸出高低電平。
傳統(tǒng)的配置 pin 的方式就是直接操作相應(yīng)的寄存器,但是這種配置 方式比較繁瑣、而且容易出問題(比如 pin 功能沖突)。pinctrl 子系統(tǒng)就是為了解決這個問題而引入的,pinctrl 子系統(tǒng)主要工作內(nèi)容如下:
①、獲取設(shè)備樹中 pin 信息。
②、根據(jù)獲取到的 pin 信息來設(shè)置 pin 的復(fù)用功能
③、根據(jù)獲取到的 pin 信息來設(shè)置 pin 的電氣特性,比如上/下拉、速度、驅(qū)動能力等。
對于我們使用者來講,只需要在設(shè)備樹里面設(shè)置好某個 pin 的相關(guān)屬性即可,其他的初始化工作均由 pinctrl 子系統(tǒng)來完成,pinctrl 子系統(tǒng)源碼目錄為 drivers/pinctrl。
注意,pinctrl 子系統(tǒng)也是一個標準的 platform 驅(qū)動,當設(shè)備和驅(qū)動匹配的時候,probe 函數(shù)會執(zhí)行,只是 pinctrl 子系統(tǒng)采用的 arch_initcall 去聲明,而不是 module_init(device_initcall),所以在系統(tǒng)起來的時候它會先加載。(具體原因看下面這篇文章)
pinctrl 和 gpio 子系統(tǒng)軟件框架如下:
pinctrl
gpio
可以看出其實兩者軟件框架一樣的,主要是 HW Abstract layer 具體實現(xiàn)不一樣。
你以為兩者是分離的,實際上不是的,gpio 子系統(tǒng)是基于 pinctrl 子系統(tǒng)的,gpio 的 API 接口的實現(xiàn)很多都是基于 pinctrl 子系統(tǒng)的函數(shù)。