【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)實驗平臺:正點(diǎn)原子STM32MP157開發(fā)板
2)購買鏈接:https://item.taobao.com/item.htm?&id=629270721801
3)全套實驗源碼+手冊+視頻下載地址:http://www.openedv.com/thread-318813-1-1.html
4)正點(diǎn)原子官方B站:https://space.bilibili.com/394620890
5)正點(diǎn)原子STM32MP157技術(shù)交流群:691905614
第二十五章 pinctrl和gpio子系統(tǒng)實驗
上一章我們編寫了基于設(shè)備樹的LED驅(qū)動,但是驅(qū)動的本質(zhì)還是沒變,都是配置LED燈所使用的GPIO寄存器,驅(qū)動開發(fā)方式和裸機(jī)基本沒啥區(qū)別。Linux是一個龐大而完善的系統(tǒng),尤其是驅(qū)動框架,像GPIO這種最基本的驅(qū)動不可能采用“原始”的裸機(jī)驅(qū)動開發(fā)方式,否則就相當(dāng)于你買了一輛車,結(jié)果每天推著車去上班。Linux內(nèi)核提供了pinctrl和gpio子系統(tǒng)用于GPIO驅(qū)動,本章我們就來學(xué)習(xí)一下如何借助pinctrl和gpio子系統(tǒng)來簡化GPIO驅(qū)動開發(fā)。
25.1 pinctrl子系統(tǒng)
25.1.1 pinctrl子系統(tǒng)簡介
Linux驅(qū)動講究驅(qū)動分離與分層,pinctrl和gpio子系統(tǒng)就是驅(qū)動分離與分層思想下的產(chǎn)物,驅(qū)動分離與分層其實就是按照面向?qū)ο缶幊痰脑O(shè)計思想而設(shè)計的設(shè)備驅(qū)動框架,關(guān)于驅(qū)動的分離與分層我們后面會講。本來pinctrl和gpio子系統(tǒng)應(yīng)該放到驅(qū)動分離與分層章節(jié)后面講解,但是不管什么外設(shè)驅(qū)動,GPIO驅(qū)動基本都是必須的,而pinctrl和gpio子系統(tǒng)又是GPIO驅(qū)動必須使用的,所以就將pintrcl和gpio子系統(tǒng)這一章節(jié)提前了。
我們先來回顧一下上一章是怎么初始化LED燈所使用的GPIO,步驟如下:
①、修改設(shè)備樹,添加相應(yīng)的節(jié)點(diǎn),節(jié)點(diǎn)里面重點(diǎn)是設(shè)置reg屬性,reg屬性包括了GPIO相關(guān)寄存器。
②、 獲取reg屬性中GPIOI_MODER、GPIOI_OTYPER、GPIOI_OSPEEDR、GPIOI_PUPDR和GPIOI_BSRR這些寄存器的地址,并且初始化它們,這些寄存器用于設(shè)置PI0這個PIN的復(fù)用功能、上下拉、速度等。
③、 在②里面將PI0這個PIN設(shè)置為通用輸出功能,因此需要設(shè)置PI0這個GPIO相關(guān)的寄存器,也就是設(shè)置GPIOI_MODER寄存器。
④、在②里面將PI0這個PIN設(shè)置為高速、上拉和推挽模式,就要需要設(shè)置PI0的GPIOI_OTYPER、GPIOI_OSPEEDR和GPIOI_PUPDR這些寄存器。
總結(jié)一下,②中完成對PI0這個PIN的獲取相關(guān)的寄存器地址,③和④設(shè)置這個PIN的復(fù)用功能、上下拉等,比如將GPIOI_0這個PIN設(shè)置為通用輸出功能。如果使用過STM32單片機(jī)的話應(yīng)該都記得,STM32單片機(jī)也是要先設(shè)置某個PIN的復(fù)用功能、速度、上下拉等,然后再設(shè)置PIN所對應(yīng)的GPIO,STM32MP1是從STM32單片機(jī)發(fā)展而來的,設(shè)置GPIO是一樣。其實對于大多數(shù)的32位SOC而言,引腳的設(shè)置基本都是這兩方面,因此Linux內(nèi)核針對PIN的復(fù)用配置推出了pinctrl子系統(tǒng),對于GPIO的電氣屬性配置推出了gpio子系統(tǒng)。本小節(jié)我們來學(xué)習(xí)pinctrl子系統(tǒng),下一小節(jié)再學(xué)習(xí)gpio子系統(tǒng)。
大多數(shù)SOC的pin都是支持復(fù)用的,比如STM32MP1的PI0既可以作為普通的GPIO使用,也可以作為SPI2的NSS引腳、TIM5的CH4引腳等等。此外我們還需要配置pin的電氣特性,比如上/下拉、速度、驅(qū)動能力等等。傳統(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。
25.1.2 STM32MP1的pinctrl子系統(tǒng)驅(qū)動
1、PIN配置信息詳解
要使用pinctrl子系統(tǒng),我們需要在設(shè)備樹里面設(shè)置PIN的配置信息,畢竟pinctrl子系統(tǒng)要根據(jù)你提供的信息來配置PIN功能,一般會在設(shè)備樹里面創(chuàng)建一個節(jié)點(diǎn)來描述PIN的配置信息。打開stm32mp151.dtsi文件,找到一個叫做pinctrl的節(jié)點(diǎn),如下所示:
第1815~1816行,#address-cells屬性值為1和#size-cells屬性值為1,也就是說pinctrl下的所有子節(jié)點(diǎn)的reg第一位是起始地址,第二位為長度。
第1818行,ranges屬性表示STM32MP1的GPIO相關(guān)寄存器起始地址,STM32MP1系列芯片最多擁有176個通用GPIO,分為12組,分別為:PA0PA15、PB0PB15、PC0PC15、PD0PD15、PE0PE15、PF0PF15、PG0PG15、PH0PH15、PJ0PJ15、PK0PK7、PZ0~PZ7。
其中PAPK這9組GPIO的寄存器都在一起,起始地址為0X50002000,終止地址為0X5000C3FF。這個可以在《STM32MP157參考手冊》里面找到。PZ組寄存器起始地址為0X54004000,終止地址為0X540043FF,所以stm32mp151.dtsi文件里面還有個名為“pinctrl_z”的子節(jié)點(diǎn)來描述PZ組IO。pinctrl節(jié)點(diǎn)用來描述PAPK這11組IO,因此ranges屬性中的0x50002000表示起始地址,0xa400表示寄存器地址范圍。
第1819行,interrupt-parent屬性值為“&exti”,父中斷為exti。
后面的gpiox子節(jié)點(diǎn)先不分析,這些子節(jié)點(diǎn)都是gpio子系統(tǒng)的內(nèi)容,到后面在去分析。這個節(jié)點(diǎn)看起來,根本沒有PIN先關(guān)的配置,別急!先打開stm32mp15-pinctrl.dtsi文件,你們能找到如下內(nèi)容:
示例代碼25.1.2.2就是向pinctrl節(jié)點(diǎn)追加數(shù)據(jù),不同的外設(shè)使用的PIN不同、其配置也不同,因此一個蘿卜一個坑,將某個外設(shè)所使用的所有PIN都組織在一個子節(jié)點(diǎn)里面。示例代碼25.1.2.2中m_can1_pins_a子節(jié)點(diǎn)就是CAN1的所有相關(guān)IO的PIN集合,pwm1_pins_a子節(jié)點(diǎn)就是PWM1相關(guān)IO的PIN集合。綁定文檔Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.yaml描述了如何在設(shè)備樹中設(shè)置STM32的PIN信息:
每個pincrtl節(jié)點(diǎn)必須至少包含一個子節(jié)點(diǎn)來存放pincrtl相關(guān)信息,也就是pinctrl集,這個集合里面存放當(dāng)前外設(shè)用到哪些引腳(PIN)、這些引腳應(yīng)該怎么配置、復(fù)用相關(guān)的配置、上下拉、默認(rèn)輸出高電平還是低電平。一般這個存放pincrtl集的子節(jié)點(diǎn)名字是“pins”,如果某個外設(shè)用到多種配置不同的引腳那么就需要多個pins子節(jié)點(diǎn),比如示例代碼25.1.2.2中第535行和541行的pins1和pins2這兩個子節(jié)點(diǎn)分別描述PH13和PI9的配置方法,由于PH13和PI9這兩個IO的配置不同,因此需要兩個pins子節(jié)點(diǎn)來分別描述。第555~562行的pins子節(jié)點(diǎn)是描述PWM1的相關(guān)引腳,包括PE9、PE11、PE14,由于這三個引腳的配置是一模一樣的,因此只需要一個pins子節(jié)點(diǎn)就可以了。
上面講了,在pins子節(jié)點(diǎn)里面存放外設(shè)的引腳描述信息,這些信息包括:
1、pinmux屬性
此屬性用來存放外設(shè)所要使用的所有IO,示例代碼25.1.2.2中第536行的pinmux屬性內(nèi)容如下:
pinmux = <STM32_PINMUX(‘H’, 13, AF9)>;
可以看出,這里使用STM32_PINMUX這宏來配置引腳和引腳的復(fù)用功能,此宏定義在include/dt-bindings/pinctrl/stm32-pinfunc.h文件里面,內(nèi)容如下:
line:表示這組GPIO的第幾個引腳(例:13表示為GPIOH_13,也就是PH13)。
mode:表示當(dāng)前引腳要做那種復(fù)用功能(例:AF9表示為用第9個復(fù)用功能),這個需要查閱STM32MP1數(shù)據(jù)手冊來確定使用哪個復(fù)用功能。打開《STM32MP157A&D數(shù)據(jù)手冊》,一個IO最大有16種復(fù)用方法:AF0~AF15,打開第4章“Pinouts, pin description and alternate functions”,其中“Table 8. Alternate function AF0 to AF7”描述的是AF0~AF7這8種復(fù)用方式,“Table 9. Alternate function AF8 to AF15”描述的是AF8~AF15這8中復(fù)用方式。找到圖25.1.2.1所示內(nèi)容:
圖25.1.2.1 PH13引腳復(fù)用功能
可以看出,PH13要設(shè)置為FDCAN1的TX引腳,就要使用AF9這個復(fù)用配置,這個就是如何在pinmux屬性中添加一個引腳的復(fù)用配置。從圖25.1.2.1中可以看出,如果PH13也可以復(fù)用為UART4的TX引腳,只要設(shè)置為AF8即可,此時相關(guān)的引腳配置如下所示:
接下來了解一下PIN的電氣特性如何設(shè)置,電氣特性在pinctrl子系統(tǒng)里不是必須的,可以不配置,但是pinmux屬性是必須要設(shè)置的。stm32-pinctrl.yaml文件里面也描述了如何設(shè)置STM32的電氣屬性,如表25.1.2.1所示:
表25.1.2.1 pinctrl的電氣特性
表25.1.2.1里的bootlean類型表示了在pinctrl子系統(tǒng)只要定義這個電氣屬性就行了,例如:我要禁用內(nèi)部電壓,只要在PIN的配置集里添加“bias-disable”即可,這個時候bias-pull-down和bias-pull-up都不能使用了,因為已經(jīng)禁用了內(nèi)部電壓,所以不能配置上下拉。enum類型使用方法更簡單跟C語言的一樣,比如要設(shè)置PIN速度為最低就可以使用“slew-rate=<0>”。在示例代碼25.1.2.3里添加引腳的電氣特性組合成,如示例代碼25.1.2.6所示:
2、PIN驅(qū)動程序講解
本小節(jié)會涉及到Linux驅(qū)動分層與分離、平臺設(shè)備驅(qū)動等還未講解的知識,所以本小節(jié)教程可以不用看,不會影響后續(xù)的實驗。如果對Linux內(nèi)核的pinctrl子系統(tǒng)實現(xiàn)原理感興趣的話可以看本小節(jié)。
所有的東西都已經(jīng)準(zhǔn)備好了,包括寄存器地址和寄存器值,Linux內(nèi)核相應(yīng)的驅(qū)動文件就會根據(jù)這些值來做相應(yīng)的初始化。接下來就找一下哪個驅(qū)動文件來做這一件事情,pinctrl節(jié)點(diǎn)中compatible屬性的值為“st,stm32mp157-pinctrl”,在Linux內(nèi)核中全局搜索“pinctrl”字符串就會找到對應(yīng)的驅(qū)動文件。在文件drivers/pinctrl/stm32/pinctrl-stm32mp157.c中有如下內(nèi)容:
第2334~2344行, of_device_id結(jié)構(gòu)體類型的數(shù)組,在第二十三章講解設(shè)備樹的時候說過,of_device_id里面保存著這個驅(qū)動文件的兼容性值,設(shè)備樹中的compatible屬性值會和of_device_id中的所有兼容性字符串比較,查看是否可以使用此驅(qū)動。stm32mp157_pctrl_match結(jié)構(gòu)體數(shù)組一共有兩個兼容,分別為“st,stm32mp157-pinctrl”和“st,stm32mp157-z-pinctrl”,設(shè)備樹也定義了這兩個兼容性值,因此pinctrl和pinctrl_z節(jié)點(diǎn)都會和此驅(qū)動匹配,所以pinctrl-stm32mp157.c會完成STM32MP1的PIN配置工作。
第2350~2357行,platform_driver是平臺設(shè)備驅(qū)動,這個是我們后面章節(jié)要講解的內(nèi)容,platform_driver是個結(jié)構(gòu)體,有個probe成員變量。在這里大家只需要知道,當(dāng)設(shè)備和驅(qū)動匹配成功以后platform_driver的probe成員變量所代表的函數(shù)就會執(zhí)行,在2351行設(shè)置probe成員變量為stm32_pctl_probe函數(shù),因此在本章實驗中stm32_pctl_probe這個函數(shù)就會執(zhí)行,可以認(rèn)為stm32_pctl_probe函數(shù)就是STM32MP157這個SOC的PIN配置入口函數(shù)。
第2359~2362行,就是一個簡單的驅(qū)動入口函數(shù),platform_driver_register函數(shù)是一個標(biāo)準(zhǔn)的平臺設(shè)備驅(qū)動注冊函數(shù),用于向Linux內(nèi)核注冊一個platform_driver,這里就是將stm32mp157_pinctrl_driver注冊到Linux內(nèi)核總,關(guān)于平臺設(shè)備驅(qū)動后面章節(jié)會詳細(xì)講解。
我們重點(diǎn)來分析一下stm32_pctl_probe函數(shù),函數(shù)定義在drivers/pinctrl/stm32/pinctrl-stm32.c里面,函數(shù)內(nèi)容如下所示:
第1458行,看它的結(jié)構(gòu)體的名字就知道是ST官方自定義的一個結(jié)構(gòu)體類型,用于存放STM32相關(guān)PIN屬性集合。第22.5.1小節(jié)我們驅(qū)動代碼也添加了自己的結(jié)構(gòu)體,它們都有自己的結(jié)構(gòu)體這樣做有啥好處?可以實現(xiàn)一個驅(qū)動代碼“通殺”多個設(shè)備。要想驅(qū)動能通用,就要用結(jié)構(gòu)體來保存數(shù)據(jù)和驅(qū)動里面盡量不要使用全局變量(在pinctrl驅(qū)動里就沒有一個全局變量,全部使用結(jié)構(gòu)體來描述一個物體,物體的所有屬性都作為結(jié)構(gòu)體成員變量)。接著我們?nèi)タ聪聅tm32_pinctrl結(jié)構(gòu)體是如何定義的,如示例代碼25.1.2.9所示:
示例代碼25.1.2.9 stm32_pinctrl結(jié)構(gòu)體代碼段 100 struct stm32_pinctrl { ...... 103 struct pinctrl_desc pctl_desc; ...... 107 struct stm32_gpio_bank *banks; ...... 120 };第103行,pinctrl_desc結(jié)構(gòu)體用來描述PIN控制器,PIN控制器,PIN控制器用于配置SOC的PIN復(fù)用功能和電氣特性。
第107行,這個stm32_gpio_bank結(jié)構(gòu)體,是用來注冊GPIO驅(qū)動。到后面GPIO子系統(tǒng)在說。
pinctrl_desc結(jié)構(gòu)體內(nèi)容如下所示:
第134~136行,這三個“_ops”結(jié)構(gòu)體指針非常重要!因為這三個結(jié)構(gòu)體就是PIN控制器的“工具”,這三個結(jié)構(gòu)體里面包含了很多操作函數(shù),Linux內(nèi)核初始化PIN最終使用的就是這些操作函數(shù)。因此編寫一個SOC的PIN控制器驅(qū)動的核心就是實現(xiàn)pinctrl_desc里面的pctlops、pmxops和confops,pinctrl_desc結(jié)構(gòu)體需要由用戶提供,結(jié)構(gòu)體里面的成員變量也是用戶編寫的。但是這個用戶并不是我們這些使用芯片的程序員,而是半導(dǎo)體廠商,半導(dǎo)體廠商發(fā)布的Linux內(nèi)核源碼中已經(jīng)把這些工作做完了。
示例代碼25.1.2.8里,第1536~1538行,給這三個結(jié)構(gòu)體賦值分別對應(yīng)stm32_pconf_ops、stm32_prtrl_ops和stm32_pmx_ops,三個結(jié)構(gòu)體如下:
1、定義pinctrl_desc結(jié)構(gòu)體。
2、初始化結(jié)構(gòu)體,重點(diǎn)是pinconf_ops、pinmux_ops和pinctrl_ops這三個結(jié)構(gòu)體成員變量,但是這部分半導(dǎo)體廠商幫我們搞定。
3、調(diào)用devm_pinctrl_register函數(shù)完成PIN控制器注冊。
25.1.3 設(shè)備樹中添加pinctrl節(jié)點(diǎn)模板
我們已經(jīng)對pinctrl有了比較深入的了解,接下來我們學(xué)習(xí)一下如何在設(shè)備樹中添加某個外設(shè)的PIN信息。比如我們需要將PG11這個PIN復(fù)用為UART4_TX引腳,pinctrl節(jié)點(diǎn)添加過程如下:
1、創(chuàng)建對應(yīng)的節(jié)點(diǎn)
在pinctrl節(jié)點(diǎn)下添加一個“uart4_pins”節(jié)點(diǎn):
2、添加“pins”屬性
添加一個“pins”子節(jié)點(diǎn),這個子節(jié)點(diǎn)是真正用來描述PIN配置信,要注意,同一個pins子節(jié)點(diǎn)下的所有PIN電氣屬性要一樣。如果某個外設(shè)所用的PIN可能有不同的配置,那么就需要多個pins子節(jié)點(diǎn),例如UART4的TX和RX引腳配置不同,因此就有pins1和pins2兩個子節(jié)點(diǎn)。這里我們只添加UART4的TX引腳,所以添加完pins1子節(jié)點(diǎn)以后如下所示:
3、在“pins”節(jié)點(diǎn)中添加PIN配置信息
最后在“pins”節(jié)點(diǎn)中添加具體的PIN配置信息,完成以后如下所示:
25.2 gpio子系統(tǒng)
25.2.1 gpio子系統(tǒng)簡介
上一小節(jié)講解了pinctrl子系統(tǒng),pinctrl子系統(tǒng)重點(diǎn)是設(shè)置PIN(有的SOC叫做PAD)的復(fù)用和電氣屬性,如果pinctrl子系統(tǒng)將一個PIN復(fù)用為GPIO的話,那么接下來就要用到gpio子系統(tǒng)了。gpio子系統(tǒng)顧名思義,就是用于初始化GPIO并且提供相應(yīng)的API函數(shù),比如設(shè)置GPIO為輸入輸出,讀取GPIO的值等。gpio子系統(tǒng)的主要目的就是方便驅(qū)動開發(fā)者使用gpio,驅(qū)動開發(fā)者在設(shè)備樹中添加gpio相關(guān)信息,然后就可以在驅(qū)動程序中使用gpio子系統(tǒng)提供的API函數(shù)來操作GPIO,Linux內(nèi)核向驅(qū)動開發(fā)者屏蔽掉了GPIO的設(shè)置過程,極大的方便了驅(qū)動開發(fā)者使用GPIO。
25.2.2 STM32MP1的gpio子系統(tǒng)驅(qū)動
1、設(shè)備樹中的gpio信息
首先肯定是GPIO控制器的節(jié)點(diǎn)信息,以PI0這個引腳所在的GPIOI為例,打開stm32mp151.dtsi,在里面找到如下所示內(nèi)容:
圖25.2.2.1 GPIOI寄存器表
從圖25.2.2.1可以看出,GPIOI控制器的基地址就是0X5000A000,這個地址是基于pinctrl的地址0X50002000+0x8000 = 0X5000A000。
第1918行,clocks屬性指定這個GPIOI控制器的時鐘。
示例代碼25.2.2.1中的是GPIOI控制器節(jié)點(diǎn),當(dāng)某個具體的引腳作為GPIO使用的時候還需要進(jìn)一步設(shè)置。ST官方EVK開發(fā)板將PG1用作SD卡的檢測(CD)引腳,PG1復(fù)用為GPIO功能,通過讀取這個GPIO的高低電平就可以知道SD卡有沒有插入。但是,SD卡驅(qū)動程序怎么知道CD引腳連接的PG1呢?這里肯定需要設(shè)備樹來告訴驅(qū)動,在設(shè)備樹中的SD卡節(jié)點(diǎn)下添加一個屬性來描述SD卡的CD引腳就行了,SD卡驅(qū)動直接讀取這個屬性值就知道SD卡的CD引腳使用的是哪個GPIO了。ST官方EVK開飯的SD卡連接在STM32MP157的sdmmc1接口上,在stm32mp15xx-edx.dtsi中找到名為“sdmmc1”的節(jié)點(diǎn),這個節(jié)點(diǎn)就是SD卡設(shè)備節(jié)點(diǎn),如下所示:
2、GPIO驅(qū)動程序簡介
在25.1.2小節(jié)里已經(jīng)分析過pinctrl驅(qū)動代碼了,前面一小節(jié)說過了STM32MP1的pinctrl驅(qū)動和gpio驅(qū)動是同一個驅(qū)動文件,都為pinctrl-stm32mp157.c,所以他們的入口函數(shù)都是stm32_pctl_probe,找到如下代碼所示:
第1587行,stm32_gpiolib_register_bank函數(shù)用來注冊GPIO驅(qū)動,包括生成回調(diào)函數(shù),注冊的過程是跟pinctrl驅(qū)動注冊是一樣的。都是創(chuàng)建自己的結(jié)構(gòu)體,然后初始化結(jié)構(gòu)體,調(diào)用內(nèi)核的注冊函數(shù),這樣把自己的結(jié)構(gòu)體注冊到內(nèi)核。這邊就不去分析了,大家可以試下分析GPIO驅(qū)動代碼。
25.2.3 gpio子系統(tǒng)API函數(shù)
對于驅(qū)動開發(fā)人員,設(shè)置好設(shè)備樹以后就可以使用gpio子系統(tǒng)提供的API函數(shù)來操作指定的GPIO,gpio子系統(tǒng)向驅(qū)動開發(fā)人員屏蔽了具體的讀寫寄存器過程。這就是驅(qū)動分層與分離的好處,大家各司其職,做好自己的本職工作即可。gpio子系統(tǒng)提供的常用的API函數(shù)有下面幾個:
1、gpio_request函數(shù)
gpio_request函數(shù)用于申請一個GPIO管腳,在使用一個GPIO之前一定要使用gpio_request進(jìn)行申請,函數(shù)原型如下:
int gpio_request(unsigned gpio, const char *label)
函數(shù)參數(shù)和返回值含義如下:
gpio:要申請的gpio標(biāo)號,使用of_get_named_gpio函數(shù)從設(shè)備樹獲取指定GPIO屬性信息,此函數(shù)會返回這個GPIO的標(biāo)號。
label:給gpio設(shè)置個名字。
返回值:0,申請成功;其他值,申請失敗。
2、gpio_free函數(shù)
如果不使用某個GPIO了,那么就可以調(diào)用gpio_free函數(shù)進(jìn)行釋放。函數(shù)原型如下:
void gpio_free(unsigned gpio)
函數(shù)參數(shù)和返回值含義如下:
gpio:要釋放的gpio標(biāo)號。
返回值:無。
3、gpio_direction_input函數(shù)
此函數(shù)用于設(shè)置某個GPIO為輸入,函數(shù)原型如下所示:
int gpio_direction_input(unsigned gpio)
函數(shù)參數(shù)和返回值含義如下:
gpio:要設(shè)置為輸入的GPIO標(biāo)號。
返回值:0,設(shè)置成功;負(fù)值,設(shè)置失敗。
4、gpio_direction_output函數(shù)
此函數(shù)用于設(shè)置某個GPIO為輸出,并且設(shè)置默認(rèn)輸出值,函數(shù)原型如下:
int gpio_direction_output(unsigned gpio, int value)
函數(shù)參數(shù)和返回值含義如下:
gpio:要設(shè)置為輸出的GPIO標(biāo)號。
value:GPIO默認(rèn)輸出值。
返回值:0,設(shè)置成功;負(fù)值,設(shè)置失敗。
5、gpio_get_value函數(shù)
此函數(shù)用于獲取某個GPIO的值(0或1),此函數(shù)是個宏,定義所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
函數(shù)參數(shù)和返回值含義如下:
gpio:要獲取的GPIO標(biāo)號。
返回值:非負(fù)值,得到的GPIO值;負(fù)值,獲取失敗。
6、gpio_set_value函數(shù)
此函數(shù)用于設(shè)置某個GPIO的值,此函數(shù)是個宏,定義如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
函數(shù)參數(shù)和返回值含義如下:
gpio:要設(shè)置的GPIO標(biāo)號。
value:要設(shè)置的值。
返回值:無
關(guān)于gpio子系統(tǒng)常用的API函數(shù)就講這些,這些是我們用的最多的。
25.2.4 設(shè)備樹中添加gpio節(jié)點(diǎn)模板
本節(jié)我們以正點(diǎn)原子STM32MP157開發(fā)板上的LED0為例,學(xué)習(xí)一下如何創(chuàng)建GPIO節(jié)點(diǎn)。LED0連接到了PI0引腳上,首先創(chuàng)建一個“l(fā)ed”設(shè)備節(jié)點(diǎn)。
1、創(chuàng)建led設(shè)備節(jié)點(diǎn)
在根節(jié)點(diǎn)“/”下創(chuàng)建test設(shè)備子節(jié)點(diǎn),如下所示:
關(guān)于pinctrl子系統(tǒng)和gpio子系統(tǒng)就講解到這里,接下來就使用pinctrl和gpio子系統(tǒng)來驅(qū)動STM32MP1開發(fā)板上的LED燈。
25.2.5 與gpio相關(guān)的OF函數(shù)
在示例代碼25.2.4.2中,我們定義了一個名為“gpio”的屬性,gpio屬性描述了led這個設(shè)備所使用的GPIO。在驅(qū)動程序中需要讀取gpio屬性內(nèi)容,Linux內(nèi)核提供了幾個與GPIO有關(guān)的OF函數(shù),常用的幾個OF函數(shù)如下所示:
1、of_gpio_named_count函數(shù)
of_gpio_named_count函數(shù)用于獲取設(shè)備樹某個屬性里面定義了幾個GPIO信息,要注意的是空的GPIO信息也會被統(tǒng)計到,比如:
gpios = <0
&gpio1 1 2
0
&gpio2 3 4>;
上述代碼的“gpios”節(jié)點(diǎn)一共定義了4個GPIO,但是有2個是空的,沒有實際的含義。通過of_gpio_named_count函數(shù)統(tǒng)計出來的GPIO數(shù)量就是4個,此函數(shù)原型如下:
int of_gpio_named_count(struct device_node *np, const char *propname)
函數(shù)參數(shù)和返回值含義如下:
np:設(shè)備節(jié)點(diǎn)。
propname:要統(tǒng)計的GPIO屬性。
返回值:正值,統(tǒng)計到的GPIO數(shù)量;負(fù)值,失敗。
2、of_gpio_count函數(shù)
和of_gpio_named_count函數(shù)一樣,但是不同的地方在于,此函數(shù)統(tǒng)計的是“gpios”這個屬性的GPIO數(shù)量,而of_gpio_named_count函數(shù)可以統(tǒng)計任意屬性的GPIO信息,函數(shù)原型如下所示:
int of_gpio_count(struct device_node *np)
函數(shù)參數(shù)和返回值含義如下:
np:設(shè)備節(jié)點(diǎn)。
返回值:正值,統(tǒng)計到的GPIO數(shù)量;負(fù)值,失敗。
3、of_get_named_gpio函數(shù)
此函數(shù)獲取GPIO編號,因為Linux內(nèi)核中關(guān)于GPIO的API函數(shù)都要使用GPIO編號,此函數(shù)會將設(shè)備樹中類似<&gpioi 0(GPIO_ACTIVE_LOW | GPIO_PULL_UP)>的屬性信息轉(zhuǎn)換為對應(yīng)的GPIO編號,此函數(shù)在驅(qū)動中使用很頻繁!函數(shù)原型如下:
int of_get_named_gpio(struct device_node *np,
const char *propname,
int index)
函數(shù)參數(shù)和返回值含義如下:
np:設(shè)備節(jié)點(diǎn)。
propname:包含要獲取GPIO信息的屬性名。
index:GPIO索引,因為一個屬性里面可能包含多個GPIO,此參數(shù)指定要獲取哪個GPIO的編號,如果只有一個GPIO信息的話此參數(shù)為0。
返回值:正值,獲取到的GPIO編號;負(fù)值,失敗。
25.3 硬件原理圖分析
本實驗的硬件原理參考21.2小節(jié)即可。
25.4 實驗程序編寫
本實驗對應(yīng)的例程路徑為:開發(fā)板光盤 1、程序源碼2、Linux驅(qū)動例程5_gpioled。
本章實驗我們繼續(xù)研究LED燈,在第二十四章實驗中我們通過設(shè)備樹向dtsled.c文件傳遞相應(yīng)的寄存器物理地址,然后在驅(qū)動文件中配置寄存器。本章實驗我們使用gpio子系統(tǒng)來完成LED燈驅(qū)動。
25.4.1 修改設(shè)備樹文件
在stm32mp157d-atk.dts文件的根節(jié)點(diǎn)“/”下創(chuàng)建LED燈節(jié)點(diǎn),節(jié)點(diǎn)名為“gpioled”,節(jié)點(diǎn)內(nèi)容如下:
圖25.4.1.1 gpioled子節(jié)點(diǎn)
25.4.2 LED燈驅(qū)動程序編寫
設(shè)備樹準(zhǔn)備好以后就可以編寫驅(qū)動程序了,本章實驗在第四十四章實驗驅(qū)動文件dtsled.c的基礎(chǔ)上修改而來。新建名為“5_gpioled”文件夾,然后在5_gpioled文件夾里面創(chuàng)建vscode工程,工作區(qū)命名為“gpioled”。工程創(chuàng)建好以后新建gpioled.c文件,在gpioled.c里面輸入如下內(nèi)容:
第41行,在設(shè)備結(jié)構(gòu)體gpioled_dev中加入led_gpio這個成員變量,此成員變量保存LED等所使用的GPIO編號。
第55行,將設(shè)備結(jié)構(gòu)體變量gpioled設(shè)置為filp的私有數(shù)據(jù)private_data。
第85行,通過讀取filp的private_data成員變量來得到設(shè)備結(jié)構(gòu)體變量,也就是gpioled。這種將設(shè)備結(jié)構(gòu)體設(shè)置為filp私有數(shù)據(jù)的方法在Linux內(nèi)核驅(qū)動里面非常常見。
第96、98行,直接調(diào)用gpio_set_value函數(shù)來向GPIO寫入數(shù)據(jù),實現(xiàn)開/關(guān)LED的效果。不需要我們直接操作相應(yīng)的寄存器。
第134行,獲取節(jié)點(diǎn)“/gpioled”。
第141~146行,獲取“status”屬性的值,判斷屬性是否“okay”。
第149~153行,獲取compatible屬性值并進(jìn)行匹配。
第161行,通過函數(shù)of_get_named_gpio函數(shù)獲取LED所使用的LED編號。相當(dāng)于將gpioled節(jié)點(diǎn)中的“l(fā)ed-gpio”屬性值轉(zhuǎn)換為對應(yīng)的LED編號。
第169行,通過函數(shù)gpio_request向GPIO子系統(tǒng)申請使用PI0。
第176行,調(diào)用函數(shù)gpio_direction_output設(shè)置PI0這個GPIO為輸出,并且默認(rèn)高電平,這樣默認(rèn)就會關(guān)閉LED燈。
可以看出gpioled.c文件中的內(nèi)容和第二十四章的dtsled.c差不多,只是取消掉了配置寄存器的過程,改為使用Linux內(nèi)核提供的API函數(shù)。在GPIO操作上更加的規(guī)范化,符合Linux代碼框架,而且也簡化了GPIO驅(qū)動開發(fā)的難度,以后我們所有例程用到GPIO的地方都采用此方法。
25.4.3 編寫測試APP
本章直接使用第四十二章的測試APP,將上一章的ledApp.c文件復(fù)制到本章實驗工程下即可。
25.5 運(yùn)行測試
25.5.1 編譯驅(qū)動程序和測試APP
1、編譯驅(qū)動程序
編寫Makefile文件,本章實驗的Makefile文件和第二十章實驗基本一樣,只是將obj-m變量的值改為gpioled.o,Makefile內(nèi)容如下所示:
make -j32
編譯成功以后就會生成一個名為“gpioled.ko”的驅(qū)動模塊文件。
2、編譯測試APP
輸入如下命令編譯測試ledApp.c這個測試程序:
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp
編譯成功以后就會生成ledApp這個應(yīng)用程序。
25.5.2 運(yùn)行測試
將上一小節(jié)編譯出來的gpioled.ko和ledApp這兩個文件拷貝到rootfs/lib/modules/5.4.31目錄中,重啟開發(fā)板,進(jìn)入到目錄lib/modules/5.4.31中,輸入如下命令加載gpioled.ko驅(qū)動模塊:
depmod //第一次加載驅(qū)動的時候需要運(yùn)行此命令
modprobe gpioled //加載驅(qū)動
驅(qū)動加載成功以后會在終端中輸出一些信息,如圖25.5.2.1所示:
圖25.5.2.1 驅(qū)動加載成功以后輸出的信息
從圖25.5.2.1可以看出,gpioled這個節(jié)點(diǎn)找到了,并且PI0這個GPIO的編號為128。驅(qū)動加載成功以后就可以使用ledApp軟件來測試驅(qū)動是否工作正常,輸入如下命令打開LED燈:
./ledApp /dev/gpioled 1 //打開LED燈
輸入上述命令以后觀察開發(fā)板上的紅色LED燈是否點(diǎn)亮,如果點(diǎn)亮的話說明驅(qū)動工作正常。在輸入如下命令關(guān)閉LED燈:
./ledApp /dev/gpioled 0 //關(guān)閉LED燈
輸入上述命令以后觀察開發(fā)板上的紅色LED燈是否熄滅。如果要卸載驅(qū)動的話輸入如下命令即可:
rmmod gpioled.ko
總結(jié)
以上是生活随笔為你收集整理的【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 主题阅读卡片0005:电子邮件[emai
- 下一篇: Windows 系统下搭建 WAMP 环