Linux的gpio子系統

GPIO其實也是某個pin的功能之一。

上一小節講解了 pinctrl 子系統,pinctrl 子系統重點是設置 PIN(有的 SOC 叫做 PAD)的復用和電氣屬性,如果 pinctrl 子系統將一個 PIN 復用為 GPIO 的話,那么接下來就要用到 gpio 子系統了。gpio 子系統顧名思義,就是用于初始化 GPIO 并且提供相應的 API 函數,比如設置 GPIO為輸入輸出,讀取 GPIO 的值等。gpio 子系統的主要目的就是方便驅動開發者使用 gpio,驅動開發者在設備樹中添加 gpio 相關信息,然后就可以在驅動程序中使用 gpio 子系統提供的 API函數來操作 GPIO,Linux 內核向驅動開發者屏蔽掉了 GPIO 的設置過程,也就是說,不用我們再去初始化GPIO了,極大的方便了驅動開發者使用 GPIO。 類似于庫函數。

I.MX6ULL 的 gpio 子系統驅動

設備樹中的 gpio 信息

I.MX6ULL-ALPHA 開發板上的 UART1_RTS_B 做為 SD 卡的檢測引腳,UART1_RTS_B復用為 GPIO1_IO19,通過讀取這個 GPIO 的高低電平就可以知道 SD 卡有沒有插入。首先肯定是將 UART1_RTS_B 這個 PIN 復用為GPIO1_IO19,并且設置電氣屬性,也就是上一小節講的pinctrl 節點。打開 imx6ull-alientek-emmc.dts,UART1_RTS_B 這個 PIN 的 pincrtl 設置如下:

可見,pinctrl使用起來還是蠻簡單的,雖然配置引腳本身就比較簡單,可是現在更加簡單了。

pinctrl 配置好以后就是設置 gpio 了,SD 卡驅動程序通過讀取 GPIO1_IO19 的值來判斷 SD卡有沒有插入,但是 SD 卡驅動程序怎么知道 CD 引腳連接的 GPIO1_IO19 呢?也就是說,怎么把這個IO關聯到SD卡中去?肯定是需要通過設備樹告訴SD的驅動啊!也就是,需要在SD卡對應的設備樹信息中添加這個IO信息。在設備樹中 SD 卡節點下添加一個屬性來描述 SD 卡的 CD 引腳就行了,SD卡驅動直接讀取這個屬性值就知道 SD 卡的 CD 引腳使用的是哪個 GPIO 了。SD 卡連接在 I.MX6ULL 的 usdhc1 接口上,在 imx6ull-alientek-emmc.dts 中找到名為“usdhc1”的節點,這個節點就是 SD 卡設備節點,如下所示:

這里的有些屬性是自定義名稱的,沒有特別的要求,后續引用時使用相同名稱即可。

第 765 行,此行本來沒有,是作者添加的,usdhc1 節點作為 SD 卡設備總節點,usdhc1 節點需要描述 SD 卡所有的信息,因為驅動要使用。本行就是描述 SD 卡的 CD 引腳 pinctrl 信息所在的子節點,因為 SD 卡驅動需要根據 pincrtl 節點信息來設置 CD 引腳的復用功能等。762~764行的 pinctrl-0~2 都是 SD 卡其他 PIN 的 pincrtl 節點信息。但是大家會發現,其實在 usdhc1 節點中“pinctrl-3 = <&pinctrl_hog_1>”這一行是被注釋掉的,也就是說并沒有指定 CD 引腳的 pinctrl 信息,那么 SD 卡驅動就沒法設置 CD 引腳的復用功能啊?這個不用擔心,因為在“iomuxc”節點下引用了 pinctrl_hog_1 這個節點,所以 Linux 內核中的 iomuxc 驅動就會自動初始化 pinctrl_hog_1節點下的所有 PIN。

注意,到這里,只是通過pinctrl子系統設置了引腳的復用、上下拉和驅動功能,還沒有關聯CD和該引腳。

繼續看第 766 行,屬性“cd-gpios”描述了 SD 卡的 CD 引腳使用的哪個 IO。屬性值一共有三個, 我們來看一下這三個屬性值的含義,“&gpio1”表示 CD 引腳所使用的 IO 屬于 GPIO1 組,“19” 表示 GPIO1 組的第 19 號 IO,通過這兩個值 SD 卡驅動程序就知道 CD 引腳使用了 GPIO1_IO19這 GPIO。“GPIO_ACTIVE_LOW”表示低電平有效,如果改為“GPIO_ACTIVE_HIGH”就表示高電平有效。 這里的低電平高電平有效沒明白,待解決。

根據上面這些信息,SD 卡驅動程序就可以使用 GPIO1_IO19 來檢測 SD 卡的 CD 信號了,打開 imx6ull.dtsi,在里面找到如下所示內容:

gpio1 節點信息描述了 GPIO1 控制器的所有信息,重點就是 GPIO1 外設寄存器基地址以及兼 容 屬 性 。 關 于 I.MX 系 列 SOC 的 GPIO 控 制 器 綁 定 信 息 請 查 看 文 檔Documentation/devicetree/bindings/gpio/ fsl-imx-gpio.txt。

第 505 行,設置 gpio1 節點的 compatible 屬性有兩個,分別為“fsl,imx6ul-gpio”和“fsl,imx35-gpio”,在 Linux 內核中搜索這兩個字符串就可以找到 I.MX6UL 的 GPIO 驅動程序。

第 506 行,reg 屬性設置了 GPIO1 控制器的寄存器基地址為 0X0209C000,大家可以打開《I.MX6ULL 參考手冊》找到“Chapter 28:General Purpose Input/Output(GPIO)”章節第 28.5 小節,有如圖 45.2.2.1 所示的寄存器地址表:

從圖 45.2.2.1 可以看出,GPIO1 控制器的基地址就是 0X0209C000。

第 509 行,“gpio-controller”表示 gpio1 節點是個 GPIO 控制器。

第 510 行,“#gpio-cells”屬性和“#address-cells”類似,#gpio-cells 應該為 2,表示一共有兩個 cell,第一個 cell 為 GPIO 編號,比如“&gpio1 3”就表示 GPIO1_IO03。第二個 cell 表示GPIO 極 性 , 如 果 為 0(GPIO_ACTIVE_HIGH) 的 話 表 示 高 電 平 有 效 , 如 果 為1(GPIO_ACTIVE_LOW)的話表示低電平有效。

GPIO 驅動程序簡介

gpio1 節點的 compatible 屬性描述了兼容性,在 Linux 內核中搜索“fsl,imx6ul-gpio”和 “fsl,imx35-gpio”這兩個字符串,查找 GPIO 驅動文件。drivers/gpio/gpio-mxc.c 就是 I.MX6ULL的 GPIO 驅動文件,在此文件中有如下所示 of_device_id 匹配表:

第 156 行的 compatible 值為“fsl,imx35-gpio”,和 gpio1 的 compatible 屬性匹配,因此 gpio-mxc.c 就是 I.MX6ULL 的 GPIO 控制器驅動文件。gpio-mxc.c 所在的目錄為 drivers/gpio,打開這個目錄可以看到很多芯片的 gpio 驅動文件, “gpiolib”開始的文件是 gpio 驅動的核心文件,如圖 45.2.2.2 所示:

我們重點來看一下 gpio-mxc.c 這個文件,在 gpio-mxc.c 文件中有如下所示內容:

可以看出 GPIO 驅動也是個平臺設備驅動,因此當設備樹中的設備節點與驅動的 of_device_id 匹配以后 probe 函數就會執行,在這里就是 mxc_gpio_probe 函數,這個函數就是I.MX6ULL 的 GPIO 驅動入口函數。我們簡單來分析一下 mxc_gpio_probe 這個函數,函數內容如下:

第 405 行,設備樹節點指針。

第 406 行,定義一個結構體指針 port,結構體類型為 mxc_gpio_port。gpio-mxc.c 的重點工

作就是維護 mxc_gpio_port,mxc_gpio_port 就是對 I.MX6ULL GPIO 的抽象。mxc_gpio_port 結構體定義如下:

mxc_gpio_port 的 bgc 成員變量很重要,因為稍后的重點就是初始化 bgc。

繼續回到 mxc_gpio_probe 函數函數,第 411 行調用 mxc_gpio_get_hw 函數獲取 gpio 的硬

件相關數據,其實就是 gpio 的寄存器組,函數 mxc_gpio_get_hw 里面有如下代碼:

注意第 385 行,mxc_gpio_hwdata 是個全局變量,如果硬件類型是 IMX35_GPIO 的話設置

mxc_gpio_hwdat 為 imx35_gpio_hwdata。對于 I.MX6ULL 而言,硬件類型就是IMX35_GPIO,imx35_gpio_hwdata 是個結構體變量,描述了 GPIO 寄存器組,內容如下:

大家將 imx35_gpio_hwdata 中的各個成員變量和圖 45.2.2.1 中的 GPIO 寄存器表對比就會發現,imx35_gpio_hwdata 結構體就是 GPIO 寄存器組結構。這樣我們后面就可以通過

mxc_gpio_hwdata 這個全局變量來訪問 GPIO 的相應寄存器了。

繼續回到示例代碼 45.2.2.5 的 mxc_gpio_probe 函數中,第 417 行,調用函數 platform_get_resource 獲取設備樹中內存資源信息,也就是 reg 屬性值。前面說了 reg 屬性指定了 GPIO1 控制器的寄存器基地址為 0X0209C000,在配合前面已經得到的mxc_gpio_hwdata,這樣 Linux 內核就可以訪問 gpio1 的所有寄存器了。

第 418 行,調用 devm_ioremap_resource 函數進行內存映射,得到 0x0209C000 在 Linux 內核中的虛擬地址。

第 422、423 行,通過 platform_get_irq 函數獲取中斷號,第 422 行獲取高 16 位 GPIO 的中

斷號,第 423 行獲取底 16 位 GPIO 中斷號。

第 428、429 行,操作 GPIO1 的 IMR 和 ISR 這兩個寄存器,關閉 GPIO1 所有 IO 中斷,并

且清除狀態寄存器。

第 438~448 行,設置對應 GPIO 的中斷服務函數,不管是高 16 位還是低 16 位,中斷服務

函數都是 mx3_gpio_irq_handler。

第 450~453 行,bgpio_init 函數第一個參數為 bgc,是 bgpio_chip 結構體指針。bgpio_chip

結構體有個 gc 成員變量,gc 是個 gpio_chip 結構體類型的變量。gpio_chip 結構體是抽象出來的GPIO 控制器,gpio_chip 結構體如下所示(有縮減):

可以看出,gpio_chip 大量的成員都是函數,這些函數就是 GPIO 操作函數。bgpio_init 函數

主 要 任 務 就 是 初 始 化 bgc->gc 。 bgpio_init 里 面 有 三 個 setup 函 數 : bgpio_setup_io 、bgpio_setup_accessors 和 bgpio_setup_direction。這三個函數就是初始化 bgc->gc 中的各種有關GPIO 的操作,比如輸出,輸入等等。第 451~453 行的GPIO_PSR、GPIO_DR 和 GPIO_GDIR 都是 I.MX6ULL 的 GPIO 寄存器。這些寄存器地址會賦值給 bgc 參數的 reg_dat、reg_set、reg_clr和 reg_dir 這些成員變量。至此,bgc 既有了對 GPIO 的操作函數,又有了 I.MX6ULL 有關 GPIO的寄存器,那么只要得到 bgc 就可以對 I.MX6ULL 的 GPIO 進行操作。繼續回到mxc_gpio_probe 函數,第461行調用函數gpiochip_add向Linux內核注gpio_chip,也就是 port->bgc.gc。注冊完成以后我們就可以在驅動中使用 gpiolib 提供的各個 API 函數。

gpio 子系統 API 函數

對于驅動開發人員,設置好設備樹以后就可以使用 gpio 子系統提供的 API 函數來操作指定

的 GPIO,gpio 子系統向驅動開發人員屏蔽了具體的讀寫寄存器過程。這就是驅動分層與分離的好處,大家各司其職,做好自己的本職工作即可。gpio 子系統提供的常用的 API 函數有下面幾個:

1gpio_request 函數

gpio_request 函數用于申請一個 GPIO 管腳,在使用一個 GPIO 之前一定要使用gpio_request

進行申請,函數原型如下:

int gpio_request(unsigned gpio, const char *label)

函數參數和返回值含義如下:

gpio:要申請的 gpio 標號,使用 of_get_named_gpio 函數從設備樹獲取指定 GPIO 屬性信

息,此函數會返回這個 GPIO 的標號。

label:給 gpio 設置個名字。

返回值:0,申請成功;其他值,申請失敗。

2gpio_free 函數

如果不使用某個 GPIO 了,那么就可以調用 gpio_free 函數進行釋放。函數原型如下:

void gpio_free(unsigned gpio)

函數參數和返回值含義如下:

gpio:要釋放的 gpio 標號。

返回值:無。

3gpio_direction_input 函數

此函數用于設置某個 GPIO 為輸入,函數原型如下所示:

int gpio_direction_input(unsigned gpio)

函數參數和返回值含義如下:

gpio:要設置為輸入的 GPIO 標號。

返回值:0,設置成功;負值,設置失敗。

4gpio_direction_output 函數

此函數用于設置某個 GPIO 為輸出,并且設置默認輸出值,函數原型如下:

int gpio_direction_output(unsigned gpio, int value)

函數參數和返回值含義如下:

gpio:要設置為輸出的 GPIO 標號。

valueGPIO 默認輸出值。

返回值:0,設置成功;負值,設置失敗。

5gpio_get_value 函數

此函數用于獲取某個 GPIO 的值(0 或 1),此函數是個宏,定義所示:

#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

函數參數和返回值含義如下:

gpio:要獲取的 GPIO 標號。

返回值:非負值,得到的 GPIO 值;負值,獲取失敗。

6gpio_set_value 函數

此函數用于設置某個 GPIO 的值,此函數是個宏,定義如下

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)

函數參數和返回值含義如下:

gpio:要設置的 GPIO 標號。

value要設置的值。

返回值:

關于 gpio 子系統常用的 API 函數就講這些,這些是我們用的最多的。

設備樹中添加 gpio 節點模板?

繼續完成 45.1.3 中的 test 設備,在 45.1.3 中我們已經講解了如何創建 test 設備的 pinctrl 節

點。本節我們來學習一下如何創建 test 設備的 GPIO 節點。

1、創建 test 設備節點

在根節點“/”下創建 test 設備子節點,如下所示:

注意,這里是設備,一會兒設備要指定使用哪個引腳,這個引腳需要先在pinctrl子系統里設置好。

2、添加 pinctrl 信息

在 45.1.3 中我們創建了 pinctrl_test 節點,此節點描述了 test 設備所使用的 GPIO1_IO00 這

個 PIN 的信息,我們要將這節點添加到 test 設備節點中,如下所示:

第 2 行,添加 pinctrl-names 屬性,此屬性描述 pinctrl 名字為“default”。

第 3 行,添加 pinctrl-0 節點,此節點引用 45.1.3 中創建的 pinctrl_test 節點,表示 tset 設備

的所使用的 PIN 信息保存在 pinctrl_test 節點中,所以,最好還是寫上,不知道上面為啥要注釋掉。

3、添加 GPIO 屬性信息

我們最后需要在 test 節點中添加 GPIO 屬性信息,表明 test 所使用的 GPIO 是哪個引腳,添

加完成以后如下所示:

第 4 行,test 設備所使用的 gpio。

關于 pinctrl 子系統和 gpio 子系統就講解到這里,接下來就使用 pinctrl 和 gpio 子系統來驅

動 I.MX6ULL-ALPHA 開發板上的 LED 燈。

gpio 相關的 OF 函數

注意,以下這些of函數并不是gpio子系統提供的函數,而是設備樹機制提供的獲取設備樹中gpio信息相關的接口。

在示例代碼 45.2.4.3 中,我們定義了一個名為“gpio”的屬性,gpio 屬性描述了 test 這個設

備所使用的 GPIO。在驅動程序中需要讀取 gpio 屬性內容,Linux 內核提供了幾個與 GPIO 有關的 OF 函數,常用的幾個 OF 函數如下所示:

1of_gpio_named_count 函數

of_gpio_named_count 函數用于獲取設備樹某個屬性里面定義了幾個 GPIO 信息,要注意的

是空的 GPIO 信息也會被統計到,比如:

上述代碼的“gpios”節點一共定義了 4 個 GPIO,但是有 2 個是空的,沒有實際的含義。

通過 of_gpio_named_count 函數統計出來的 GPIO 數量就是 4 個,此函數原型如下:

int of_gpio_named_count(struct device_node *np, const char *propname)

函數參數和返回值含義如下:

np:設備節點。

propname:要統計的 GPIO 屬性。

返回值:正值,統計到的 GPIO 數量;負值,失敗。

2of_gpio_count 函數

和 of_gpio_named_count 函數一樣,但是不同的地方在于,此函數統計的是“gpios”這個屬

性的 GPIO 數量,而 of_gpio_named_count 函數可以統計任意屬性的 GPIO 信息,函數原型如下所示:

int of_gpio_count(struct device_node *np)

函數參數和返回值含義如下:

np:設備節點。

返回值:正值,統計到的 GPIO 數量;負值,失敗。

3of_get_named_gpio 函數

此函數獲取 GPIO 編號,因為 Linux 內核中關于 GPIO 的 API 函數都要使用 GPIO 編號,

此函數會將設備樹中類似<&gpio5 7 GPIO_ACTIVE_LOW>的屬性信息轉換為對應的 GPIO 編

號,此函數在驅動中使用很頻繁!函數原型如下:

int of_get_named_gpio(struct device_node *np, const char *propname, int index)

函數參數和返回值含義如下:

np:設備節點。

propname:包含要獲取 GPIO 信息的屬性名。

indexGPIO 索引,因為一個屬性里面可能包含多個 GPIO,此參數指定要獲取哪個 GPIO

的編號,如果只有一個 GPIO 信息的話此參數為 0。

返回值:正值,獲取到的 GPIO 編號;負值,失敗。

實驗程序編寫

本實驗對應的例程路徑為:開發板光盤-> 2、Linux 驅動例程-> 5_gpioled。

本章實驗我們繼續研究 LED 燈,在第四十四章實驗中我們通過設備樹向 dtsled.c 文件傳遞

相應的寄存器物理地址,然后在驅動文件中配置寄存器。本章實驗我們使用 pinctrl 和 gpio 子系統來完成 LED 燈驅動。

修改設備樹文件?

1、添加 pinctrl 節點

I.MX6U-ALPHA 開發板上的 LED 燈使用了 GPIO1_IO03 這個 PIN,打開 imx6ull-alientek

emmc.dts,在 iomuxc 節點的 imx6ul-evk 子節點下創建一個名為“pinctrl_led”的子節點,節點

內容如下所示:

第 3 行,將 GPIO1_IO03 這個 PIN 復用為 GPIO1_IO03,電氣屬性值為 0X10B0。

2、添加 LED 設備節點

在根節點“/”下創建 LED 燈節點,節點名為“gpioled”,節點內容如下:

第 6 行,pinctrl-0 屬性設置 LED 燈所使用的 PIN 對應的 pinctrl 節點。

第 7 行,led-gpio 屬性指定了 LED 燈所使用的 GPIO,在這里就是 GPIO1 的 IO03,低電平

有效。稍后編寫驅動程序的時候會獲取 led-gpio 屬性的內容來得到 GPIO 編號,因為 gpio 子系統的 API 操作函數需要 GPIO 編號。

使用gpio子系統的前提是pin已經(通過pinctrl子系統或者其他原始方式)被復用為了gpio功能,然后,因為gpio也是被某個具體外設所使用,所以通常還需要在具體外設的節點中關聯上對應的gpio。

關聯分為兩個部分:

  • 通過pinctrl-names和pinctrl-x的形式來指定使用的是哪個pinctrl信息;
  • 關聯上具體的gpio控制器;

3、檢查 PIN 是否被其他外設使用

這一點非常重要!!!

很多初次接觸設備樹的驅動開發人員很容易因為這個小問題栽了大跟頭!因為我們所使用的設備樹基本都是在半導體廠商提供的設備樹文件基礎上修改而來的,而半導體廠商提供的設備樹是根據自己官方開發板編寫的,很多 PIN 的配置和我們所使用的開發板不一樣。比如 A 這個引腳在官方開發板接的是 I2C 的 SDA,而我們所使用的硬件可能將 A 這個引腳接到了其他的外設,比如 LED 燈上,接不同的外設,A 這個引腳的配置就不同。一個引腳一次只能實現一個功能,如果 A 引腳在設備樹中配置為了 I2C 的 SDA 信號,那么 A 引腳就不能再配置為 GPIO,否則的話驅動程序在申請 GPIO 的時候就會失敗。檢查 PIN 有沒有被其他外設使用包括兩個方面:

①、檢查 pinctrl 設置。

②、如果這個 PIN 配置為 GPIO 的話,檢查這個 GPIO 有沒有被別的外設使用。

在本章實驗中 LED 燈使用的 PIN 為 GPIO1_IO03,因此先檢查 GPIO_IO03 這個 PIN 有沒

有被其他的 pinctrl 節點使用,在 imx6ull-alientek-emmc.dts 中找到如下內容:

pinctrl_tsc 節點是 TSC(電阻觸摸屏接口)的 pinctrl 節點,從第 484 行可以看出,默認情況下

GPIO1_IO03 作為了 TSC 外設的 PIN。所以我們需要將第 484 行屏蔽掉!和 C 語言一樣,在要屏蔽的內容前后加上“/*”和“*/”符號即可。其實在 I.MX6U-ALPHA 開發板上并沒有用到 TSC接口,所以第 482~485 行的內容可以全部屏蔽掉。

因為本章實驗我們將 GPIO1_IO03 這個 PIN 配置為了 GPIO,所以還需要查找一下有沒有

其他的外設使用了 GPIO1_IO03,在 imx6ull-alientek-emmc.dts 中搜索“gpio1 3”,找到如下內容:

tsc 是 TSC 的外設節點,從 726 行可以看出,tsc 外設也使用了 GPIO1_IO03,同樣我們需

要將這一行屏蔽掉。然后在繼續搜索“gpio1 3”,看看除了本章的 LED 燈以外還有沒有其他的

地方也使用了 GPIO1_IO03,找到一個屏蔽一個。

設備樹編寫完成以后使用“make dtbs”命令重新編譯設備樹,然后使用新編譯出來的imx6ull-alientek-emmc.dtb 文件啟動 Linux 系統。啟動成功以后進入“/proc/device-tree”目錄中查看“gpioled”節點是否存在,如果存在的話就說明設備樹基本修改成功(具體還要驅動驗證),結果如圖 45.4.1.1 所示:

linux中gpio子系統的使用流程

在Linux中,GPIO子系統的使用流程主要涉及到設備樹配置、驅動程序編寫與注冊、以及用戶空間的應用開發。以下是詳細的步驟:

設備樹配置

添加pinctrl節點:在設備樹文件(通常是.dts.dtsi文件)中,添加pinctrl節點來描述GPIO控制器的引腳復用和配置信息。這些節點定義了哪些引腳可以用作GPIO,以及它們的默認功能和電氣屬性。

添加設備節點:除了pinctrl節點外,還需要在設備樹中添加具體的設備節點,這些節點描述了使用GPIO的設備(如LED、按鈕等)的相關信息,包括它們連接到哪個GPIO引腳、是否使用中斷等。

編寫驅動程序

初始化GPIO子系統:在驅動程序的初始化函數中,首先需要調用相關函數來初始化GPIO子系統,確保系統能夠識別和管理GPIO設備。

獲取GPIO資源:使用Linux內核提供的API函數(如gpio_requestgpio_free等)來請求和釋放GPIO資源。這些函數允許驅動程序指定要操作的GPIO引腳,并設置其初始狀態(如輸入/輸出方向、初始電平等)。

實現設備操作函數:根據設備的具體需求,實現相應的設備操作函數。例如,對于LED設備,可能需要實現打開和關閉LED的函數;對于按鈕設備,則可能需要實現讀取按鈕狀態的函數。

注冊設備驅動:將編寫好的驅動程序注冊到Linux內核中,使系統能夠識別并加載該驅動。這通常涉及到填充特定的數據結構(如platform_driver),并調用platform_driver_register等函數進行注冊。

用戶空間的應用開發

訪問GPIO設備:在用戶空間應用程序中,可以通過讀取和寫入/sys/class/gpio目錄下的文件來控制GPIO設備。例如,向/sys/class/gpio/gpio<number>/value文件寫入“1”或“0”可以分別打開和關閉連接到該GPIO引腳的LED燈。

處理中斷事件:如果設備使用了中斷功能(如按鈕按下時觸發中斷),則需要在用戶空間應用程序中處理這些中斷事件。這通常涉及到安裝中斷處理程序,并在中斷發生時執行相應的操作。

總的來說,通過遵循以上步驟,可以在Linux系統中有效地使用GPIO子系統來控制硬件設備。這不僅簡化了硬件控制的復雜性,還提高了系統的可擴展性和可維護性。

LED 燈驅動程序編寫?

設備樹準備好以后就可以編寫驅動程序了,本章實驗在第四十四章實驗驅動文件 dtsled.c 的

基礎上修改而來。新建名為“5_gpioled”文件夾,然后在 5_gpioled 文件夾里面創建 vscode 工

程,工作區命名為“gpioled”。工程創建好以后新建 gpioled.c 文件,在 gpioled.c 里面輸入如下內容:

第 41 行,在設備結構體 gpioled_dev 中加入 led_gpio 這個成員變量,此成員變量保存 LED

等所使用的 GPIO 編號。

第 55 行,將設備結構體變量 gpioled 設置為 filp 的私有數據 private_data。

第 85 行,通過讀取 filp 的 private_data 成員變量來得到設備結構體變量,也就是 gpioled。

這種將設備結構體設置為 filp 私有數據的方法在 Linux 內核驅動里面非常常見。

第 96、97 行,直接調用 gpio_set_value 函數來向 GPIO 寫入數據,實現開/關 LED 的效果。不需要我們直接操作相應的寄存器。

第 133 行,獲取節點“/gpioled”。

第 142 行,通過函數 of_get_named_gpio 函數獲取 LED 所使用的 LED 編號。相當于將

gpioled 節點中的“led-gpio”屬性值轉換為對應的 LED 編號。

第 150 行,調用函數 gpio_direction_output 設置 GPIO1_IO03 這個 GPIO 為輸出,并且默認高電平,這樣默認就會關閉 LED 燈。

可以看出 gpioled.c 文件中的內容和第四十四章的 dtsled.c 差不多,只是取消掉了配置寄存

器的過程,改為使用 Linux 內核提供的 API 函數。在 GPIO 操作上更加的規范化,符合Linux

代碼框架,而且也簡化了 GPIO 驅動開發的難度,以后我們所有例程用到 GPIO 的地方都采用此方法。

接下來的測試內容和之前一樣,就不贅述了。?

貌似并沒有使用gpio_request呀?

gpio_request函數在以下幾種情況下使用:

  • 驅動初始化階段

    • 硬件資源預留:當驅動程序加載并開始初始化時,需要確定要控制的GPIO引腳。此時,調用gpio_request函數向內核請求特定編號的GPIO引腳的使用權,以便后續對其進行操作和控制。這確保了該引腳在整個驅動程序運行期間被正確分配和使用,避免了與其他設備或驅動程序產生沖突。

    • 設置引腳功能:通過gpio_request函數請求GPIO引腳后,可以進一步設置引腳的功能,如將引腳配置為輸入模式用于讀取外部信號,或者配置為輸出模式用于控制外部設備等。

  • 設備操作之前

    • 確保獨占訪問權:在對GPIO引腳進行讀寫操作之前,必須先成功調用gpio_request函數獲取該引腳的控制權。這樣可以保證當前進程或驅動程序對引腳的獨占訪問,防止其他進程或設備同時訪問該引腳導致數據沖突或錯誤。

    • 避免沖突:如果多個設備或驅動程序試圖同時訪問同一個GPIO引腳,可能會導致系統不穩定或出現錯誤的結果。gpio_request函數的使用可以避免這種沖突,確保每個設備或驅動程序都能正確地獲取到自己所需的GPIO引腳資源。

  • 實現特定功能時

    • 根據需求動態申請:在一些復雜的系統中,設備的運行狀態可能會根據不同的條件發生變化,需要動態地控制不同的GPIO引腳。例如,當某個事件發生時,驅動程序可能需要臨時申請一個特定的GPIO引腳來實現相應的功能,如點亮一個指示燈、發送一個信號等。在這種情況下,gpio_request函數可以在需要的時候被調用來請求所需的GPIO引腳。

    • 與其他硬件協同工作:當設備需要與其他外部硬件設備進行通信或協同工作時,可能需要通過GPIO引腳來傳輸信號或數據。在建立這種連接之前,需要先使用gpio_request函數請求相應的GPIO引腳,以便與外部設備進行正確的交互。

綜上所述,gpio_request函數在Linux內核中扮演著至關重要的角色,它確保了對GPIO引腳的安全、有效管理,是實現硬件設備控制與交互不可或缺的一環。

暫時搞不太明白,后面再補充吧。

我看這幾個gpio的操作函數的參數都是gpio編號,可以通過of_get_named_gpio 獲得,也不需要gpio_request的什么返回值,所以,gpio_request有啥意義?

使用了of_get_named_gpio 函數還需要使用gpio_request函數嗎?

在使用 of_get_named_gpio 函數獲取到 GPIO 編號后,是否需要使用 gpio_request 函數取決于具體的應用場景和需求。以下是對此問題的詳細分析:

  • 需要使用 gpio_request 的情況

    • 獨占訪問需求:如果需要在驅動程序中獨占地使用某個 GPIO 引腳,確保其他進程或設備無法同時訪問該引腳,那么就需要使用 gpio_request 函數來請求該 GPIO 引腳的控制權。這樣可以保證對該引腳的操作是安全且唯一的,避免與其他使用者產生沖突。

    • 配置引腳方向等操作:若要對獲取到的 GPIO 引腳進行進一步的配置,如設置引腳方向為輸入或輸出、初始化引腳狀態等,通常需要先通過 gpio_request 函數成功請求該引腳后,才能使用相關的配置函數(如 gpio_direction_inputgpio_direction_output 等)對其進行操作。

    • 規范的設備驅動開發:在遵循 Linux 內核的設備驅動開發規范和最佳實踐時,即使在某些簡單場景下可能看似可以直接使用獲取到的 GPIO 編號進行操作,但為了保證代碼的可維護性、可擴展性和兼容性,也應該先使用 gpio_request 函數來正確地請求和管理 GPIO 資源。

  • 可能不需要使用 gpio_request 的情況

    • 只讀取信息:如果僅僅是從設備樹中獲取 GPIO 引腳的編號或其他相關信息,而不打算對該引腳進行實際的控制或配置操作,那么可以不使用 gpio_request 函數。例如,在一些只涉及硬件信息查詢的場景中,只需要知道某個 GPIO 引腳的存在和其編號即可,無需進一步的交互。

    • 特殊的硬件抽象層或中間層處理:在某些特定的硬件抽象層(HAL)或中間層實現中,可能已經對 GPIO 資源的管理和訪問進行了統一的處理和封裝,使得在上層應用或驅動程序中無需直接調用 gpio_request 函數來請求 GPIO 引腳。這種情況下,開發者只需按照既定的接口和規范來使用 GPIO 資源即可,無需關心底層的請求細節。

綜上所述,一般情況下,為了確保對 GPIO 引腳的正確控制和避免潛在的資源沖突,建議在使用 of_get_named_gpio 函數獲取到 GPIO 編號后,根據具體的需求和使用場景來決定是否使用 gpio_request 函數來請求和管理該 GPIO 引腳。

總結

總體來說,使用還是比較簡單的。

  1. pinctrl就是在設備樹里配置pin信息;
  2. gpio子系統就是在設備樹gpio的節點屬性中引用pinctrl信息,并指明要使用的是哪個gpio;
  3. 之后在驅動里正常獲取設備節點,并從屬性里獲取對應的gpio的編號,這個編號應該是系統自動分配的;
  4. 最后,就可以通過編號用對應的函數來操作gpio了。

具體的初始化過程,廠家維護的linux版本里就已經實現了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921635.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921635.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921635.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VC++ CPU指令集檢測工具實現原理

&#x1f4c8; VC CPU指令集檢測工具實現原理 例圖&#xff1a;&#x1f9e0; 1. 核心原理&#xff1a;CPUID指令 // 使用CPUID指令獲取CPU信息 int cpuInfo[4] { -1 }; __cpuid(cpuInfo, 0); // 調用CPUID指令 int nIds cpuInfo[0]; // 獲取最大標準功能號CPUID指令工作流程…

大模型微調理論、實戰:LLaMA-Factory、Unsloth

概述 微調&#xff0c;Fine-Tuning&#xff0c;簡稱FT&#xff0c;可理解為對LLM的定制&#xff0c;目的是增強專業領域知識&#xff0c;并優化特定任務的性能。通過在特定數據集上微調一個預訓練模型&#xff0c;可實現&#xff1a; 更新知識&#xff1a;引入新的領域專屬信…

【LCA 樹上倍增】P9245 [藍橋杯 2023 省 B] 景區導游|普及+

本文涉及知識點 樹上倍增 P9245 [藍橋杯 2023 省 B] 景區導游 題目描述 某景區一共有 NNN 個景點&#xff0c;編號 111 到 NNN。景點之間共有 N?1N-1N?1 條雙向的擺渡車線路相連&#xff0c;形成一棵樹狀結構。在景點之間往返只能通過這些擺渡車進行&#xff0c;需要花費…

基于Python+Streamlit的旅游數據分析與預測系統:從數據可視化到機器學習預測的完整實現

&#x1f3de;? 基于PythonStreamlit的旅游數據分析與預測系統&#xff1a;從數據可視化到機器學習預測的完整實現 &#x1f4dd; 前言 在大數據時代&#xff0c;旅游行業的數據分析變得越來越重要。如何從海量的旅游數據中挖掘有價值的信息&#xff0c;并進行準確的銷量預測&…

飛算JavaAI全鏈路實戰:智能構建高可用電商系統核心架構

飛算JavaAI全鏈路實戰&#xff1a;智能構建高可用電商系統核心架構 前言&#xff1a;AI編程新時代的電商系統開發范式變革 在當今數字經濟時代&#xff0c;電商系統作為企業數字化轉型的核心載體&#xff0c;其復雜度和技術要求與日俱增。一個完整的電商系統不僅需要處理商品、…

論文精讀(五):面向鏈接預測的知識圖譜表示學習方法綜述

筆者鏈接&#xff1a;撲克中的黑桃A 專欄鏈接&#xff1a;論文精讀 本文關鍵詞&#xff1a;知識圖譜; 表示學習; 鏈接預測; 多元關系; 超關系 引 諸位技術同仁&#xff1a; 本系列將系統精讀的方式&#xff0c;深入剖析計算機科學頂級期刊/會議論文&#xff0c;聚焦前沿突破…

Roo Code之自定義指令(Custom Instructions),規則(Rules)

在Roo Code 中&#xff0c;Custom Instructions 可以通過Instructions 設定和Rules 規則文件實現。什么是Custom Instructions&#xff1f; 自定義指令(Custom Instructions)定義了超出Roo基本角色定義范圍的具體行為、偏好和約束。示例包括編碼風格、文檔標準、測試要求和工作…

9/8我是ai大師

一、變量定義部分&#xff08;理解程序的 "記憶"&#xff09;c運行/* USER CODE BEGIN PV */ static uint8_t last_button_state 1; // 初始為高電平&#xff08;未按下&#xff09; static uint8_t device_mode 0; // 設備模式&#xff1a;0LD1, 1LD3, 2蜂鳴器, 3…

前沿重器[74] | 淘寶RecGPT:大模型推薦框架,打破信息繭房

前沿重器欄目主要給大家分享各種大廠、頂會的論文和分享&#xff0c;從中抽取關鍵精華的部分和大家分享&#xff0c;和大家一起把握前沿技術。具體介紹&#xff1a;倉頡專項&#xff1a;飛機大炮我都會&#xff0c;利器心法我還有。&#xff08;算起來&#xff0c;專項啟動已經…

jenkins加docker 部署項目

jenkins加docker 部署springboot項目 1項目結構Dockerfile 內容 FROM openjdk:8-jdk-alpine ARG JAR_FILEtarget/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar","--server.port9090"]在A服務器上啟動jenkins …

提示詞工程(Prompt Engineering)的崛起——為什么“會寫Prompt”成了新技能?

&#x1f380;【開場 貓貓狐狐的對話】&#x1f43e;貓貓扒著屏幕&#xff1a;“喵&#xff1f;咱寫的這句 Prompt 怎么又跑偏啦&#xff1f;明明只是想讓它幫忙寫一段 Python 代碼&#xff0c;它偏要給咱寫論文摘要……” &#x1f98a;狐狐瞇著眼&#xff0c;聲音帶點冷意&a…

供應鏈管理系統入門知識:是什么,功能模塊,怎么定制開發?

如果你是剛接觸企業運營的新手&#xff0c;聽到 “供應鏈管理系統” 可能會覺得有點復雜。其實&#xff0c;它就像一個 “智能管家”&#xff0c;幫企業把從買材料到賣產品的一系列流程管得明明白白。今天就用大白話給你講清楚這個系統到底是什么&#xff0c;以及它能幫上什么忙…

kotlin - 平板分屏,左右拖動,2個Activity計算寬度,使用ActivityOptions、Rect(三)

kotlin - 平板分屏&#xff0c;左右拖動&#xff0c;2個Activity計算寬度&#xff0c;使用ActivityOptions、Rect使用平板&#xff0c;api33才支持&#xff0c;可以左右拖動&#xff0c;分屏第一個頁面 &#xff0c; 思考&#xff1a;分屏后&#xff0c;對整個app的影響&#x…

v0.29.3 敏感詞性能優化之繁簡體轉換 opencc4j 優化

敏感詞性能調優系列 v0.29.0 敏感詞性能優化提升 14 倍全過程 v0.29.1 敏感詞性能優化之內部類迭代器內部類 v0.29.2 敏感詞性能優化之基本類型拆箱、裝箱的進一步優化的嘗試 v0.29.3 敏感詞性能優化之繁簡體轉換 opencc4j 優化 背景 opencc4j opencc4j 中&#xff0c;因…

Spark SQL解析查詢parquet格式Hive表獲取分區字段和查詢條件

首先說一下&#xff0c;這里解決的問題應用場景&#xff1a; sparksql處理Hive表數據時&#xff0c;判斷加載的是否是分區表&#xff0c;以及分區表的字段有哪些&#xff1f;再進一步限制查詢分區表必須指定分區&#xff1f; 這里涉及到兩種情況&#xff1a;select SQL查詢和…

谷歌發布文本嵌入模型EmbeddingGemma(附部署方式)

EmbeddingGemma是谷歌于2025年9月開源的開放式文本嵌入模型&#xff0c;專為端側設備設計&#xff0c;具備以下核心優勢&#xff1a; 性能優勢 在MTEB基準測試中&#xff0c;EmbeddingGemma在500M以下參數規模的多語言文本嵌入模型中表現最佳&#xff0c;性能接近參數翻倍的頂…

CPU調度——調度的目標

2.2.2 調度的目標 當系統中“想運行”的實體多于 CPU 的數量時&#xff0c;調度就不可避免地要在“效率”與“公平”之間做取舍。直觀地說&#xff0c;一類目標希望把硬件壓榨到更高的利用率&#xff0c;讓單位時間內做更多的工作&#xff1b;另一類目標則關心個體體驗&#x…

C++ 8

封裝一個學生的類&#xff0c;定義一個學生這樣類的vector容器, 里面存放學生對象&#xff08;至少3個&#xff09;再把該容器中的對象&#xff0c;保存到文件中。再把這些學生從文件中讀取出來&#xff0c;放入另一個容器中并且遍歷輸出該容器里的學生。#include <iostream…

短視頻矩陣系統源碼開發搭建技術指南--支持OEM

短視頻矩陣系統架構設計短視頻矩陣系統通常采用分布式架構&#xff0c;包含內容管理、用戶管理、推薦算法、存儲分發等模塊。主流技術棧包括微服務框架&#xff08;Spring Cloud/Dubbo&#xff09;、消息隊列&#xff08;Kafka/RabbitMQ&#xff09;、數據庫&#xff08;MySQL/…

不連續頁分配器補充

vmalloc流程 1. 背景&#xff1a;vmalloc() 要解決的問題 kmalloc() 要求 虛擬地址連續&#xff0c;物理頁也連續。大塊內存分配可能失敗。vmalloc() 只保證 虛擬地址連續&#xff0c;物理內存可以由很多不連續的頁拼接。 實現的關鍵就是&#xff1a; 在 vmalloc 區域 找一塊空…