??作者:一只大喵咪1201
??專欄:《Linux驅(qū)動》
??格言:你只管努力,剩下的交給時間!
??在設(shè)備樹中指定中斷
繼續(xù)拿這個中斷流程圖來說話。
在硬件上,中斷控制器只有GIC
這一個,但是我們在軟件上可以把GPIO
也歸類為中斷控制器。
芯片會有多個GPIO模塊,所以軟件上的中斷控制器就會有很多個:GIC,GPIO1,GPIO2,GPIO3…等等。
其中GPIO1、GPIO2、GPIO3這些中斷控制器模塊都連接匯集到GIC模塊,所以GIC模塊就是GPIOx模塊的父親。
假設(shè)GPIO1有32個中斷源,但是它把其中的16個匯聚起來向GIC發(fā)出一 個中斷,把另外16個匯聚起來向GIC發(fā)出另一個中斷。這就意味著GPIO1會用到 GIC 的兩個中斷,會涉及 GIC 里的 2 個 hwirq
。
- 這些層級關(guān)系、中斷號(hwirq),都會在設(shè)備樹中有所體現(xiàn)。
dtsi:
如上圖所示由BSP工程師提供的dtsi
設(shè)備樹文件:
-
intc
:表示GIC中斷控制器。 -
gpio1
:表示GPIO1中斷控制器。 -
gpio2
:表示GPIO2中斷控制器。
GIC是頂層中斷控制器,所以它沒有父親,而GPIO1和GPIO2都是soc
的子節(jié)點,子節(jié)點會繼承父節(jié)點的屬性:
-
interrupt-parent = <&gpc>
:表示父親節(jié)點,soc
節(jié)點的interrupt-parent = <&gpc>
。 - GPIO1和GPIO2繼承
soc
的interrupt-parent = <&gpc>
屬性,所以它們的父節(jié)點也是gpc
。
本喵沒有列出來,gpc
的父節(jié)點是GIC中斷控制器,所以從設(shè)備樹反推出IMX6ULL
的中斷框圖,它比之前多了一個GPC INTC
:
如上圖所示新的中斷框圖,GPC INTC的功能是提供中斷屏蔽、中斷狀態(tài)查詢等功能。
- 實際上這些功能在GIC中也實現(xiàn)了。
- 之所以保留它是因為它還能提供喚醒功能。
繼續(xù)回到dtsi
設(shè)備樹文件來看,每一個中斷控制器中都有兩個必須的屬性:
-
interrupt-controller
:該屬性表明它是中斷控制器。 -
#interrupt-cells
:該屬性表明引用這個中斷控制器的話需要多少個cell
。-
#interrupt-cells=<1>
:別的節(jié)點要使用這個中斷控制器時,只需要一個cell
來表明使用哪一個中斷。 -
#interrupt-cells=<2>
:別的節(jié)點要使用這個中斷控制器時,需要兩個cell
,其中第一個來表明使用哪一個中斷,第二個一般來描述中斷的觸發(fā)類型。
-
由于GIC規(guī)定了要引用該中斷控制器需要使用3個cell
,所以GPIO1和GPIO2就使用了<GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>
這樣的3個cell
:
-
GIC_SPI
:GPIO模塊向GIC發(fā)出中斷的類型。 -
66
:GPIO模塊向GIC發(fā)出中斷的硬件中斷號hirq
。 -
IRQ_TYPE_LEVEL_HIGH
:GPIO模塊向GIC發(fā)出中斷的觸發(fā)類型。
除了引用GIC中斷控制器外,GPIOx模塊也有自己的interrupt-controller
屬性和#interrupt-cells = <2>
屬性:
- GPIO1和GPIO2中斷控制器模塊規(guī)定引用該控制器時需要使用兩個
cell
。
這是都是芯片廠家寫好的,提供給我們使用的,我們只需要知道怎么去用它們就行。
dts:
如上圖我們寫的dts
設(shè)備樹文件中,這是一個spidev
節(jié)點:
-
interrupt-parent = <&gpio1>
:該設(shè)備的父節(jié)點是gpio1
,表明該外部設(shè)備引用GPIO1控制器。 -
interrupts = <1 1>
:表示該設(shè)備使用的硬件中斷號hirq
是1,觸發(fā)類型是1。- 新寫法
interrupts-extend
:一個這樣的屬性就可以既指定interrupt-parent
又指定interrupts
。 - 比如
interrupts-extend = <&gpio1 1 1>
。
- 新寫法
觸發(fā)類型這里寫的1,代表什么意思呢?有寫什么類型呢?
如上圖所示,1就代表著low-to-high edge triggered
上升沿觸發(fā)。
如此一來,在設(shè)備樹中指定了該外部設(shè)備的中斷,在驅(qū)動程序中就可以直接使用了。
??代碼中獲得中斷
我們知道,設(shè)備樹中的節(jié)點有的會被內(nèi)核轉(zhuǎn)化為platform_device
結(jié)構(gòu)體,有些則不會。
對于能轉(zhuǎn)化為platform_device
結(jié)構(gòu)體的節(jié)點,如果它在設(shè)備樹里指定了中斷屬性,那么可以從platform_device
中獲得中斷資源:
如上圖所示platform_get_resource
函數(shù),用來獲取中斷資源:
-
dev
:轉(zhuǎn)化后的platform_device
結(jié)構(gòu)體指針。 -
type
:獲取哪類資源。-
IORESOURCE_MEM
、IORESOURCE_REG
、IORESOURCE_IRQ
等。
-
-
num
:這類資源中的哪一個。- 一個節(jié)點中使用不止一個中斷源。
對于I2C設(shè)備、SPI設(shè)備,總線驅(qū)動在處理設(shè)備樹里的I2C子節(jié)點時,也會處理其中的中斷信息。
- 一個I2C設(shè)備會被轉(zhuǎn)換為一個
i2c_client
結(jié)構(gòu)體,中斷號會保存在i2c_client
的irq
成員里。
如上圖代碼所示,當I2C總線和驅(qū)動程序匹配以后,會自動調(diào)用probe
函數(shù),在該函數(shù)中使用of_irq_get
函數(shù),從它的子節(jié)點I2C設(shè)備中解析出中斷號irq
。
- 一個 SPI 設(shè)備會被轉(zhuǎn)換為一個
spi_device
結(jié)構(gòu)體,中斷號會保存在spi_device
的irq
成員里。
如上圖,當SPI總線和驅(qū)動程序匹配后,在它的probe
函數(shù)中,也會調(diào)用of_irq_get
從子節(jié)點SPI設(shè)備中解析出中斷號irq
。
如果設(shè)備節(jié)點既不能轉(zhuǎn)換為platform_device
,也不是I2C和SPI設(shè)備,那么在驅(qū)動程序中,可以自行調(diào)用of_irq_get
函數(shù)去解析得到中斷號。
對于GPIO引腳,芯片廠家提供了專門的接口函數(shù)來獲取中斷:
-
of_get_gpio_flags
:使用該函數(shù)獲取GPIO引腳,此時獲取到的是使用老的方式描述GPIO引腳信息的那個整數(shù)。 -
gpio_to_desc
:使用該函數(shù)獲取GPIO引腳,獲得是使用新方式描述GPIO引腳信息的那個desc
結(jié)構(gòu)體。 -
gpiod_to_irq
:最后使用該函數(shù)從得到的GPIO引腳信息中獲得軟件中斷號。
??按鍵中斷
對于 GPIO 按鍵,我們并不需要去寫驅(qū)動程序,使用內(nèi)核自帶的驅(qū)動程序 drivers/input/keyboard/gpio_keys.c
就可以,然后需要做的只是修改設(shè)備樹指定引腳及鍵值。
但是本喵還是要從頭寫一遍按鍵驅(qū)動程序,特別是如何使用中斷,因為中斷是其他基礎(chǔ)知識的前提,以后的休眠-喚醒,POLL機制,異步通知,定時器,中斷的線程化處理等內(nèi)容都離不開中斷。
如上圖所示本喵使用的IMX6ULL
開發(fā)板原理圖,按鍵KEY1
和按鍵KEY2
使用的是GPIO5_1
和GPIO4_14
兩個引腳,并且是低電平有效。
同樣的,按鍵驅(qū)動程序也要分為驅(qū)動程序和設(shè)備樹兩部分。
?驅(qū)動程序
如上圖所示gpio_key_drv.c
文件中的驅(qū)動程序:
- 創(chuàng)建
platform_driver
結(jié)構(gòu)體gpio_keys_driver
,并且進行初始化。- 使用
gpio_key_probe
初始化probe
函數(shù)。 - 使用
gpio_key_remove
初始化remove
函數(shù)。 - 使用
of_device_id
數(shù)組Big_Miaomi_keys
初始化driver.of_match_table
。- 數(shù)組中
compatible
屬性的值是"Big_Miaomi,gpio_keys"
,用來和設(shè)備節(jié)點匹配。
- 數(shù)組中
- 使用
- 在入口函數(shù)
gpio_key_init
中使用platform_driver_register
函數(shù)向內(nèi)核注冊驅(qū)動程序。 - 在出口函數(shù)
gpio_key_exit
中使用platform_driver_unregister
從內(nèi)核中取消驅(qū)動程序注冊。 - 完善設(shè)備驅(qū)動信息。
- 由于這是按鍵中斷,不需要應(yīng)用層來調(diào)用,所以不用在
/dev
下創(chuàng)建設(shè)備節(jié)點,也不用注冊file_operations
結(jié)構(gòu)體。
probe函數(shù):
如上圖所示probe
函數(shù)的實現(xiàn):
- 定義全局一個結(jié)構(gòu)體指針
gpio_keys_Big_Miaomi
,該結(jié)構(gòu)體是struct gpio_key
類型,用來存放中斷的信息。 - 使用
of_gpio_count
獲得中斷源個數(shù),因為一個設(shè)備節(jié)點可能有多個中斷源。- 按鍵設(shè)備可能有多個按鍵,此時就有多個中斷源。
- 使用內(nèi)核的
kzalloc
函數(shù)在堆區(qū)上開辟一段堆空間,來存放中斷信息。 - 每一個中斷源,都需要獲取它的詳細信息:
- 使用
of_get_gpio_flags
獲取中斷的引腳信息gpio
和有效標志。 - 將用整數(shù)描述的引腳信息轉(zhuǎn)換成使用
gpio_desc
類型描述的引腳信息。 - 將中斷引腳設(shè)置成低電平有效,與
OF_GPIO_ACTIVE_LOW
相與。- 因為電路中按鍵是低電平有效,原本的引腳標志是輸入
GPIOF_IN
。
- 因為電路中按鍵是低電平有效,原本的引腳標志是輸入
- 再使用
devm_gpio_request_one
將引腳狀態(tài)設(shè)置成邏輯值。- 按鍵按下后,讀取到的引腳值是1,盡管物理值是0。
- 使用
gpio_to_irq
獲取中斷引腳的軟件中斷號irq
。
- 使用
將所有中斷信息獲取到以后,再為每一個中斷注冊中斷函數(shù):
- 使用
request_irq
注冊中斷服務(wù)函數(shù):- 第一個參數(shù)傳入中斷的軟件中斷號
gpio_keys_Big_Miaomi[i].irq
。 - 第二個參數(shù)傳入中斷服務(wù)函數(shù)
gpio_key_isr
。 - 第三個參數(shù)傳入中斷觸發(fā)方式
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
雙邊沿觸發(fā)。 - 第四個參數(shù)傳入中斷名稱
"100ask_gpio_key"
,該參數(shù)不重要。 - 第五個參數(shù)傳入
dev_id
,也就是中斷信息所在的結(jié)構(gòu)體指針&gpio_keys_Big_Miaomi[i]
。
- 第一個參數(shù)傳入中斷的軟件中斷號
此時中斷函數(shù)就注冊完成了,接下來就是實現(xiàn)中斷服務(wù)函數(shù)中要做什么:
如上圖所示中斷服務(wù)函數(shù),在里面僅獲取引腳電平的邏輯值,并且打印出描述引腳的那個整數(shù)編號和引腳狀態(tài)。
remove函數(shù):
如上圖所示remove
函數(shù),在卸載驅(qū)動程序時:
- 使用
free_irq
將所有前面注冊的中斷釋放掉。 - 使用
kfree
將存放引腳信息的堆空間釋放掉。
?設(shè)備樹
如上圖所示藍色框中代碼,使用圖形化工具生成GPIO5_1
和GPIO4_14
的兩個pin-controller
節(jié)點代碼。
如上圖所示,將生成的pin-controller
節(jié)點代碼復(fù)制到我們要寫的dts
設(shè)備樹文件中:
-
GPIO5_1
:節(jié)點名稱為Big_Miaomi_key1
,表示按鍵1。 -
GPIO4_14
:節(jié)點名稱為Big_Miaomi_key2
,表示按鍵2。
如上圖所示,在dts
設(shè)備樹文件的根節(jié)點下,增加按鍵外部設(shè)備節(jié)點gpio_keys_Big_Miaomi
:
- 使用
GPIO
子系統(tǒng)指定按鍵引腳和有效電平。 - 使用
Pinctrl
子系統(tǒng)將引腳復(fù)用為通用GPIO功能。 - 在原本的
gpio-keys
節(jié)點中,使用status = "disabled"
屬性讓該節(jié)點失能,防止影響我們自己創(chuàng)建的按鍵節(jié)點。
- 這里并沒有在設(shè)備樹中指定按鍵的
interrupts-extend
。- 因為對于GPIO,芯片廠家提供了驅(qū)動程序中的一些列接口,可以直接獲取GPIO的中斷信息,包括中斷號以及中斷控制器等。
?上機實驗
如上圖所示,使用上面的makefile
文件編譯驅(qū)動程序,生成gpio_key_drv.ko
驅(qū)動模塊。
如上圖所示,將寫好的gpio_key_drv.c
驅(qū)動程序和設(shè)備樹文件,以及Makefile
上傳到服務(wù)器上,分別進行編譯,編程成功后將生成的gpio_key.ko
和dtb
文件都拷貝到網(wǎng)絡(luò)文件系統(tǒng)中供開發(fā)板使用。
如上圖所示,先讓開發(fā)板使用新的dtb
設(shè)備樹文件,再使用insmod
安裝gpio_key_drv.ko
驅(qū)動模塊,可以看到安裝成功后輸出匹配porbe
函數(shù)的調(diào)試信息。
如上圖所示,按下開發(fā)板上的KEY1
和KEY2
時:
- 按鍵按下,按鍵值是1,松開按鍵值是0。
- 按鍵值前面的整數(shù)就是描述按鍵引腳的編號。
??總結(jié)
要知道在設(shè)備樹中是如何描述一個設(shè)備的中斷的,包括父節(jié)點interrupt-parent
屬性,以及描述中斷引腳的interrupts
屬性的用法。
還要知道在中斷程序中是如何獲取設(shè)備樹中的中斷信息的,以及如何使用這些中斷信息。文章來源:http://www.zghlxwxcb.cn/news/detail-796706.html
最后要會實現(xiàn)按鍵中斷程序。文章來源地址http://www.zghlxwxcb.cn/news/detail-796706.html
到了這里,關(guān)于【Linux驅(qū)動】設(shè)備樹中指定中斷 | 驅(qū)動中獲得中斷 | 按鍵中斷實驗的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!