?????
目錄
簡介:
1、GPIO 子系統有兩套API:
一、GPIO新、舊版互相兼容轉換 API
1、轉化函數
二、基于描述符接口(descriptor-based) (以"gpiod_"為前綴)
1、獲取 GPIO
2.1 struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
2.2 struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
2.3 struct gpio_descs *__must_check gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
2、釋放 GPIO
2.1 void gpiod_put(struct gpio_desc *desc)
3、設置/獲取 GPIO 方向
3.1 int gpiod_direction_input(struct gpio_desc *desc)
3.2 int gpiod_direction_output(struct gpio_desc *desc, int value)
3.3 int gpiod_get_direction(struct gpio_desc *desc)
4、設置/獲取 GPIO 值
4.1 原子方式
4.1.1 int gpiod_get_value(const struct gpio_desc *desc)
4.1.2 void gpiod_set_value(struct gpio_desc *desc, int value)
4.2 隊列方式
4.2.1 int gpiod_get_value_cansleep(const struct gpio_desc *desc)
4.2.2 void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
4.2.3 int gpiod_cansleep(const struct gpio_desc *desc)
5、GPIO IRQ 設為中斷
5.1 int gpiod_to_irq(const struct gpio_desc *desc)
5.2 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
三、legency接口(以"gpio_"為前綴)
1、申請 / 釋放 GPIO
1.1 int gpio_request(unsigned gpio, const char *label)
1.2 void gpio_free(unsigned gpio)
2、設置 GPIO 方向
2.1 int gpio_direction_input(unsigned gpio)
2.2 int gpio_direction_output(unsigned gpio, int value)
3、設置 / 獲取 GPIO 值
3.1 gpio_get_value
3.2 gpio_set_value
4、判斷 GPIO 是否有效
4.1 static inline bool gpio_is_valid(int number)
5、導出 GPIO 到 sysfs
5.1 static inline int gpio_export(unsigned gpio, bool direction_may_change)
5.2 static inline void gpio_unexport(unsigned gpio)
6、GPIO IRQ
6.1 int gpio_to_irq(unsigned gpio)
6.2 int irq_to_gpio(unsigned irq)(盡量避免使用)
6.3 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
7、示例
7.1 輸出
7.2 輸入
7.3 中斷
四、gpio_set_value獲取物理電平,gpiod_set_value據 active-low 設置邏輯電平
簡介:
????????在 Linux 內核中,GPIO(General Purpose Input/Output,通用輸入/輸出)的管理方式經歷了從?舊版?gpio_*
?接口?到?新版?gpiod_*
?接口?的演變
1、GPIO 子系統有兩套API:
- 前綴為?gpiod_。
- 使用?gpio_desc?幾個題來表示一個引腳。
- 參考文檔:Documentation/gpio/consumer.txt
- 前綴為?gpio_。
- 使用一個整數來表示一個引腳。
- 參考文檔:Documentation/gpio/gpio-legacy.txt
特性 | gpio_* ?舊接口 | gpiod_* ?新接口 |
---|---|---|
標識方式 | 全局 GPIO 編號(如?120 ) | 描述符(struct gpio_desc ) |
設備樹支持 | 不友好 | 原生支持 |
資源管理 | 手動?gpio_request/free | 自動釋放(devm_* ?可選) |
休眠安全 | 無 | 提供?_cansleep ?版本 |
適用場景 | 舊內核或簡單硬件 | 現代驅動開發(推薦) |
一、GPIO新、舊版互相兼容轉換 API
-
舊版API是使用整數標記引腳的;
-
新版API是使用字符標記引腳的。
但是引腳都是唯一的,所以兩者可以相互轉化。
1、轉化函數
源碼路徑:drivers\gpio\gpiolib.c
功能:通過引腳 gpio_desc 結構體指針 獲取引腳 GPIO 編號;
功能:通過引腳 GPIO 編號獲取引腳 gpio_desc 結構體指針;
二、基于描述符接口(descriptor-based) (以"gpiod_"為前綴)
參考文檔:Documentation/gpio/consumer.txt
desc_to_gpio
devm_get_gpiod_from_child
devm_fwnode_get_gpiod_from_child
devm_gpiod_get
devm_gpiod_get_array
devm_gpiod_get_array_optional
devm_gpiod_get_index
devm_gpiod_get_index_optional
devm_gpiod_get_optional
devm_gpiod_put
devm_gpiod_put_array
fwnode_get_named_gpiod
gpio_to_desc
gpiod_cansleep
gpiod_count
gpiod_direction_input
gpiod_direction_output
gpiod_direction_output_raw
gpiod_export
gpiod_export_link
gpiod_get
gpiod_get_array
gpiod_get_array_optional
gpiod_get_direction
gpiod_get_index
gpiod_get_index_optional
gpiod_get_optional
gpiod_get_raw_value
gpiod_get_raw_value_cansleep
gpiod_get_value
gpiod_get_value_cansleep
gpiod_is_active_low
gpiod_put
gpiod_put_array
gpiod_set_array_value
gpiod_set_array_value_cansleep
gpiod_set_debounce
gpiod_set_raw_array_val
gpiod_set_raw_array_val
gpiod_set_raw_value
gpiod_set_raw_value_cansleep
gpiod_set_value
gpiod_set_value_cansleep
gpiod_to_irq
gpiod_unexport
1、獲取 GPIO
- gpiod_get / devm_gpiod_get
- gpiod_get_index / devm_gpiod_get_index
- gpiod_get_array / devm_gpiod_get_array
前綴為"devm_"表示自動釋放資源機制。資源是屬于設備的,設備不存在時資源就可以自動釋放
背景:在Linux驅動開發過程中,先申請了GPIO,再申請內存,如果內存申請失敗,那么在返回之前就需要先釋放GPIO資源。如果使用的是devm相關函數,在內存申請失敗時可以直接返回,設備的銷毀函數會自動地釋放已經申請了的GPIO資源。
因此,建議使用devm相關函數操作GPIO。
2.1 struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
- 功能:獲取 dev 設備,con_id 的第 0 個引腳信息,并做 flags 初始化。 等價于 gpiod_get_index 的idx 為0。gpiod_get_index 函數調用了gpiod_request
- 參數:
- dev:設備指針。從該設備獲取引腳信息;
- con_id:引腳組名稱(不包含前綴 "-gpios")。如引腳組名為?reset-gpios = <&gpio1 3 GPIO_ACTIVE_HIGH>。則?con_id = "reset";
- flags:初始化標志
GPIOD_ASIS或0根本不初始化GPIO。稍后必須使用專用功能之一設置方 GPIOD_IN將GPIO初始化為輸入。 GPIOD_OUT_LOW將GPIO初始化為值為0的輸出。 GPIOD_OUT_HIGH將GPIO初始化為值為1的輸出。 GPIOD_OUT_LOW_OPEN_DRAIN與GPIOD_OUT_LOW相同,但也強制該線路與開漏極電連接。 GPIOD_OUT_HIGH_OPEN_DRAIN與GPIOD_OUT_HIGH相同,但也強制線路與漏極開路電連接。
- 返回:
- 成功:gpio_desc 結構體指針。
- 失敗:-ENOENT。具體可通過?IS_ERR()?獲取返回碼。
例一:GPIO輸出
// 設備樹示例:
// mygpio {
// compatible = "my-gpio-driver";
// led-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>, // GPIO1
// button-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>; // GPIO2
// };
在設備樹中,"xxx-gpios"中的 "xxx" 代表定義的GPIO名(label)。
static int gpio_probe(struct platform_device *pdev) {... ...static struct gpio_desc *mygpio_led = NULL;int ret = 0;/* GPIO輸出,"led"是對應設備樹文件中name-gpios中的name */mygpio_led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW); //將GPIO初始化為輸出值為0的if (IS_ERR(mygpio_led)) { //判斷指針是否錯誤ret = PTR_ERR(mygpio_led); //返回錯誤碼goto error_gpio;}gpiod_set_value(mygpio_led, 1); // 設置 GPIO 為高電平error_gpio:if (mygpio_led)gpiod_put(mygpio_led); // 釋放 GPIOreturn ret;
}
例二:GPIO輸入
static int gpio_probe(struct platform_device *pdev) {... ...static struct gpio_desc *mygpio_button = NULL;int ret = 0;/* GPIO輸入 */mygpio_button = gpiod_get(&pdev->dev, "button", GPIOD_IN); //將GPIO初始化為輸入if (IS_ERR(mygpio_button)) { //判斷指針是否錯誤ret = PTR_ERR(mygpio_button); //返回錯誤碼goto error_gpio;}// 讀取 GPIO 值int value = gpiod_get_value(mygpio_button);
error_gpio:gpiod_put(mygpio_button); // 釋放 GPIOreturn ret;
}
2.2 struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
- 功能:獲取 dev 設備,con_id 的第?idx?個引腳信息,并做 flags 初始化。gpiod_get_index 函數調用了gpiod_request
- 舉例:
// 設備樹示例:
// leds {
// compatible = "my-led-driver";
// led-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>, // LED1
// <&gpio2 3 GPIO_ACTIVE_HIGH>; // LED2
// };
static int led_probe(struct platform_device *pdev) {... ...int num_leds = gpiod_count(&pdev->dev, "led"); //獲取 led-gpios 中 GPIO 數量for (int i = 0; i < num_leds; i++) {struct gpio_desc *led = gpiod_get_index(&pdev->dev, "led", i, GPIOD_OUT_LOW);// 控制每個 LED...}... ...
}
2.3 struct gpio_descs *__must_check gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
- 功能:獲取 dev 設備 con_id 的所有引腳信息,并做 flags 初始化
例一: 獲取 GPIO 數組,并控制輸出電平
//leds {
// compatible = "gpio-leds";
// led-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>, // LED0
// <&gpio 11 GPIO_ACTIVE_HIGH>, // LED1
// <&gpio 12 GPIO_ACTIVE_HIGH>; // LED2
//};
static int led_probe(struct platform_device *pdev)
{... ...struct gpio_descs *leds = NULL;//獲取 GPIO 數組("led"是對應設備樹文件中name-gpios中的name)leds = gpiod_get_array(&pdev->dev, "led", GPIOD_OUT_LOW); if (IS_ERR(leds)) { //判斷指針是否錯誤ret = PTR_ERR(leds); //返回錯誤碼goto error_gpios;}// 設置所有 GPIO 為高電平(點亮 LED)for (int i = 0; i < leds->ndescs; i++) {gpiod_set_value(leds->desc[i], 1);}error_gpios:if (leds) gpiod_put_array(leds); // 使用完后釋放return ret;
}
例二:獲取 GPIO 數組,讀取buttons輸入狀態
//buttons {
// buttons-gpios = <&gpio 5 GPIO_ACTIVE_LOW>, // BTN0
// <&gpio 6 GPIO_ACTIVE_LOW>; // BTN1
//};
static int led_probe(struct platform_device *pdev)
{... ...struct gpio_descs *buttons;buttons = gpiod_get_array(&pdev->dev, "buttons", GPIOD_IN);if (IS_ERR(buttons)) { //判斷指針是否錯誤ret = PTR_ERR(buttons); //返回錯誤碼goto error_gpios;}// 讀取所有按鈕狀態for (int i = 0; i < buttons->ndescs; i++) {int val = gpiod_get_value(buttons->desc[i]);printk("Button %d: %d\n", i, val);}error_gpios:if (buttons)gpiod_put_array(buttons); // 釋放return ret;
}
其它獲取GPIO的函數:
-
gpiod_get_optional:和?gpiod_get?差不多。不同的是該函數返回?gpio_desc 結構體指針?或?NULL。如果 GPIO 不存在或無法獲取,函數返回?NULL(而不是錯誤),驅動可以安全地忽略該 GPIO。例如:
struct gpio_desc *desc;desc = gpiod_get_optional(&pdev->dev, "debug-enable", GPIOD_OUT_HIGH);
if (!desc) {dev_info(&pdev->dev, "Debug GPIO not available, skipping\n");// 可以繼續執行,不報錯
} else {// 如果 GPIO 存在,則使用gpiod_set_value(desc, 1);
}
-
devm_xxx:以上函數均可添加?devm_?前綴。比以上函數多了綁定設備dev,設備被刪除時,自動釋放引腳。
2、釋放 GPIO
-
gpiod_put / devm_gpiod_put
-
gpiod_put_array / devm_gpiod_put_array
2.1 void gpiod_put(struct gpio_desc *desc)
- 功能:釋放 desc 引腳
3、設置/獲取 GPIO 方向
- gpiod_direction_input
- gpiod_direction_output
- gpiod_get_direction
3.1 int gpiod_direction_input(struct gpio_desc *desc)
- 功能:設置該引腳為輸入方向
- 返回值:成功返回值為零,否則返回值為負的錯誤碼
3.2 int gpiod_direction_output(struct gpio_desc *desc, int value)
- 功能:設置該引腳為輸入方向。
- 參數:
- value:設置方向后的初始值。
- 返回值:成功返回值為零,否則返回值為負的錯誤碼
3.3 int gpiod_get_direction(struct gpio_desc *desc)
- 功能:獲取引腳方向。
- 返回值:
- 0(GPIOF_DIR_OUT):輸出
- 1(GPIOF_DIR_IN):輸入
4、設置/獲取 GPIO 值
注意:"gpiod_"方式設置 GPIO 值需考慮 DTS 中 active-low狀態。當 GPIO 設置為 GPIO_ACTIVE_LOW 低電平有效時,即是低電平為邏輯?1,高電平為邏輯?0。gpiod_set_value 設置 1,就是將gpio拉低(物理電平)。這里一定要區分邏輯電平和物理電平的差別。
led-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>, //低有效,低電平為邏輯1
使用方法
gpiod_set_value_cansleep(led_gpio, 1); //輸出邏輯有效,設備樹active 有效狀態是 GPIO_ACTIVE_LOW,因此輸出低電平
因為DTS里面的active 狀態是 GPIO_ACTIVE_LOW,所以這個代碼輸出的是低電平。
當然,也有直接控制物理電平的方式,內核 Documentation/driver-api/gpio/consumer.rst 文件有相關介紹
Function (example) | line property | physical line |
gpiod_set_raw_value(desc, 0); | don't care | low |
gpiod_set_raw_value(desc, 1); | don't care | high |
gpiod_set_value(desc, 0); | default (active high) | low |
gpiod_set_value(desc, 1); | default (active high) | high |
gpiod_set_value(desc, 0); | active low | high |
gpiod_set_value(desc, 1); | active low | low |
gpiod_set_value(desc, 0); | default (active high) | low |
gpiod_set_value(desc, 1); | default (active high) | high |
gpiod_set_value(desc, 0); | open drain | low |
gpiod_set_value(desc, 1); | open drain | high impedance |
gpiod_set_value(desc, 0); | open source | high impedance |
gpiod_set_value(desc, 1); | open source | high |
gpiod_set_raw-value:忽略 DTS 中的 ACTIVE,直接輸出物理電平
int gpiod_get_raw_value(const struct gpio_desc *desc);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
設置 / 獲取GPIO 有兩種訪問方式:
-
原子方式:一種是通過儲存器讀寫實現的,這種操作屬于原子操作,不需要等待,所以可以在中斷處理程序中使用
int gpiod_get_value(const struct gpio_ _desc *desc);
void gpiod_set_value(struct gpio_ desc *desc, int value);
-
隊列方式:訪問必須通過消息總線比如I2C或者SPI,這種訪問需要在總線訪問隊列中等待,所以可能進入睡眠,此類訪問不能出現在IRQ handler
int gpiod_cansleep(const struct gpio_desc *desc) //分辨該引腳是否需要通過消息總線訪問
int gpiod_get_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
4.1 原子方式
4.1.1 int gpiod_get_value(const struct gpio_desc *desc)
- 功能:獲取引腳邏輯值
- 返回值:
- 成功:非負數:0-邏輯無效, 非0-邏輯有效
- 失敗:負數
4.1.2 void gpiod_set_value(struct gpio_desc *desc, int value)
- 功能:設置引腳邏輯值,1-有效,0-無效,例如設備樹 active 為 GPIO_ACTIVE_LOW 時,1有效表示低電平
4.2 隊列方式
隊列方式:在隊列中等待訪問引腳,可能會進入睡眠,不能作用于中斷。訪問必須通過消息總線比如I2C或者SPI,這些需要在隊列中訪問。
4.2.1 int gpiod_get_value_cansleep(const struct gpio_desc *desc)
- 功能:獲取gpio邏輯值,支持休眠
4.2.2 void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
- 功能:設置gpio邏輯值,支持休眠
4.2.3 int gpiod_cansleep(const struct gpio_desc *desc)
- 功能:可以使用 gpiod_cansleep() 函數分辨該引腳是否需要通過消息總線訪問
5、GPIO IRQ 設為中斷
request_irq(gpiod_to_irq(gpio_desc)...); //將gpio轉為對應的irq,然后注冊該irq的中斷handlerint gpio_probe(struct platform_device *pdev)
{... ...gpio_desc = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN); //配置成輸入if (gpio_desc) {ret = request_irq(gpiod_to_irq(gpio_desc), gpio_test_irq, IRQF_TRIGGER_FALLING, "gpio Detect", dev);}... ...
}//中斷服務函數
static irqreturn_t gpio_test_irq(int irq, void *devid)
{schedule_work(&bat_work);return IRQ_HANDLED;
}
5.1 int gpiod_to_irq(const struct gpio_desc *desc)
- 功能:獲取該引腳對應的?IRQ number。
- 返回:
- 成功:中斷號。
- 失敗:負數。
5.2 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
- 功能:請求中斷,需先配置為輸入GPIO
- 參數:
- irq:中斷號。
- handler:中斷回調函數。
- flags:中斷類型。
- IRQF_TRIGGER_RISING 上升沿觸發 按鍵、信號檢測
- IRQF_TRIGGER_FALLING 下降沿觸發 按鍵釋放檢測
- IRQF_TRIGGER_HIGH 高電平觸發 電平敏感設備
- IRQF_TRIGGER_LOW 低電平觸發 低電平有效設備
- IRQF_SHARED 共享中斷 多個設備共享同一條中斷線
- IRQF_ONESHOT 單次觸發 線程化中斷需要保持禁用直到線程完成
- IRQF_NO_THREAD 禁止線程化 要求快速處理的中斷
- IRQF_NOBALANCING 禁止CPU負載均衡 需要固定CPU處理的中斷
- name:請求中斷的設備名稱。
- dev:可取任意值。
- 但必須唯一能夠代表發出中斷請求的設備。
- 通常取描述該設備的結構體,或NULL。
- 用于共享中斷時。(若中斷被共享,則不能為?NULL)
三、legency接口(以"gpio_"為前綴)
- 申請、釋放:gpio_request、gpio_free
- 設置GPIO方向:gpio_direction_input、gpio_direction_output
- 獲取設置GPIO值:gpio_get_value、gpio_set_value
- 設置為中斷:gpio_to_irq
- 導出到sys文件系統:gpio_export
1、申請 / 釋放 GPIO
1.1 int gpio_request(unsigned gpio, const char *label)
- 功能:申請 GPIO
- 參數:
- gpio:要申請的 gpio 標號,使用 of_get_named_gpio 函數從設備樹獲取指定 GPIO 屬性信息,此函數會返回這個 GPIO 的標號。
- label:給 gpio 設置個名字。
- 返回值:
- 0,申請成功;其他值,申請失敗
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
標志 | 含義 |
GPIOF_IN | 設置為輸入方向 |
GPIOF_OUT_INIT_LOW | 設置為輸出,初始低電平 |
GPIOF_OUT_INIT_HIGH | 設置為輸出,初始高電平 |
GPIOF_OPEN_DRAIN | 開漏輸出 |
GPIOF_OPEN_SOURCE | 開源輸出 |
GPIOF_EXPORT | 同時導出到 sysfs |
1.2 void gpio_free(unsigned gpio)
- 功能:釋放 GPIO
- 參數:
- gpio:要釋放的 gpio 標號。
2、設置 GPIO 方向
2.1 int gpio_direction_input(unsigned gpio)
- 功能:把 gpio 引腳設置為為輸入方向
- 參數:
- gpio:要設置為輸入的 GPIO 標號。
- 返回值:
- 0,設置成功;負值,設置失敗
2.2 int gpio_direction_output(unsigned gpio, int value)
- 功能:把 gpio 引腳設置為為輸出方向
- 參數:
- gpio:要設置為輸出的 GPIO 標號。
- value: GPIO 默認輸出值。
- 返回值:
- 0,設置成功;負值,設置失敗
3、設置 / 獲取 GPIO 值
3.1 gpio_get_value
#define gpio_get_value __gpio_get_valuestatic inline int __gpio_get_value(unsigned gpio)
{return gpiod_get_raw_value(gpio_to_desc(gpio)); //這里也是用的 "gpiod_" 的 gpiod_get_raw_value 獲取物理電平
}
- 功能:調用 gpiod_get_raw_value 獲取GPIO物理電平
- 參數
- gpio:要獲取的 gpio編號
- 返回值:
- 非負值,得到的 GPIO 值;負值,獲取失敗。
3.2 gpio_set_value
#define gpio_set_value __gpio_set_valuestatic inline void __gpio_set_value(unsigned gpio, int value)
{return gpiod_set_raw_value(gpio_to_desc(gpio), value); //這里用 "gpiod_" 的 gpiod_get_raw_value 設置物理電平
}
- 功能:設置GPIO物理電平
- 參數:
- gpio:要設置的 GPIO 標號
- value: 要設置的值,1-高電平,0-低電平
4、判斷 GPIO 是否有效
4.1 static inline bool gpio_is_valid(int number)
- 功能:判斷 GPIO number 是否有效。
- 返回值:
- 有效:1
- 無效:0
5、導出 GPIO 到 sysfs
將?gpio?通過?sys?文件系統導出,應用層可以通過文件操作gpio。如查看狀態、設置狀態等等。
主要用于調試,導出后訪問路徑:/sys/class/gpio?下
源碼路徑:include\linux\gpio.h
5.1 static inline int gpio_export(unsigned gpio, bool direction_may_change)
- 功能:把該 gpio 導出到 sys。
- 參數:
- gpio:GPIO number。
- direction_may_change:表示用戶是否可以改變方向。
5.2 static inline void gpio_unexport(unsigned gpio)
- 功能:取消導出
- 參數:
- gpio:GPIO number
6、GPIO IRQ
6.1 int gpio_to_irq(unsigned gpio)
- 功能:獲取該引腳對應的?IRQ number。
- 返回:
- 成功:中斷號
- 失敗:負數
6.2 int irq_to_gpio(unsigned irq)(盡量避免使用)
- 功能:獲取該引腳對應的?GPIO number。
- 返回:
- 成功:gpio號
- 失敗:負數
6.3 static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
- 功能:請求中斷
- 參數:
- irq:中斷號。
- handler:中斷回調函數。
- flags:中斷類型。
- name:請求中斷的設備名稱。
- dev:可取任意值。
- 但必須唯一能夠代表發出中斷請求的設備。
- 通常取描述該設備的結構體,或NULL。
- 用于共享中斷時。(若中斷被共享,則不能為?NULL)
7、示例
7.1 輸出
static int gpio_out_probe(struct platform_device *pdev)
{int ret = 0;int gpio;/* 從設備樹中獲取GPIO */gpio = of_get_named_gpio(pdev->dev.of_node, "reset-gpios", 0);if(!gpio_is_valid(gpio)) {dev_err(dev, "%s: of_get_named_gpio (%d)\n", __func__, ret);return -EINVAL;}/* 申請使用GPIO */ret = gpio_request(gpio, "reset");if(ret < 0) {dev_err(dev, "%s: unable to request reset_gpio (%d)\n", __func__, ret);return ret;}/* 將GPIO設置為輸出模式并設置GPIO初始化物理電平狀態為低電平 */gpio_direction_output(gpio, 0);#if 1if (gpio) {if (gpiod_cansleep(gpio_to_desc(gpio)))gpio_set_value_cansleep(gpio, 0);elsegpio_set_value(gpio, 0); //輸出低電平}
#elsegpio_set_value(gpio, 1); //輸出高電平
#endifgpio_free(gpio); //釋放gpioreturn 0;
}
7.2 輸入
static int gpio_in_probe(struct platform_device *pdev)
{int ret = 0;int gpio;int value;/* 從設備樹中獲取GPIO */gpio = of_get_named_gpio(pdev->dev.of_node, "key-gpios", 0);if(!gpio_is_valid(gpio)) {dev_err(dev, "%s: of_get_named_gpio (%d)\n", __func__, ret);return -EINVAL;}/* 申請使用GPIO */ret = gpio_request(gpio, "key");if(ret < 0) {dev_err(dev, "%s: unable to request reset_gpio (%d)\n", __func__, ret);return ret;}/* 將GPIO設置為輸入模式 */ret = gpio_direction_input(gpio);if(ret < 0) {printk("can't set gpio!\r\n");return ret;}/* 獲取gpio電平狀態 */value = gpio_get_value(gpio);gpio_free(gpio); //釋放gpioreturn 0;
}
7.3 中斷
static int gpio_in_probe(struct platform_device *pdev)
{priv->ser_errb = of_get_named_gpio(ser, "ser-errb", 0);ret = devm_gpio_request_one(&client->dev, priv->ser_errb, GPIOF_DIR_IN, "GPIO_MAXIM_SER");if (ret < 0) {dev_err(dev, "%s: GPIO request failed\n ret: %d", __func__, ret);return ret;}if (gpio_is_valid(priv->ser_errb)) {priv->ser_irq = gpio_to_irq(priv->ser_errb);ret = request_threaded_irq(priv->ser_irq, NULL,gpio_irq_handler,IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "SER", priv);if (ret < 0) {dev_err(dev, "%s: Unable to register IRQ handler ret: %d\n", __func__, ret);return ret;}}return ret;
}static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{return IRQ_HANDLED;
}
四、gpio_set_value設置物理電平,gpiod_set_value根據 active-low 設置邏輯電平
gpioled {compatible = "led-gpio";led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; //gpio1組第3號引腳flag為低電平有效default-state = "on";status = "okay";
};
gpio_set_value、gpio_get_value 設置引腳物理電平(與active-low無關)。gpiod_set_value、gpiod_get_value 根據 active-low 設置邏輯電平
分析內核源碼可以看到gpio_set_value、gpio_get_value最終分別調用?gpiod_set_raw_value 和?gpiod_get_raw_value
//arch/arm/include/asm/gpio.h
#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value//include/asm-generic/gpio.h
static inline void __gpio_set_value(unsigned gpio, int value)
{return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}//drivers/gpio/gpiolib.c
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{VALIDATE_DESC_VOID(desc);/* Should be using gpiod_set_raw_value_cansleep() */WARN_ON(desc->gdev->chip->can_sleep);gpiod_set_raw_value_commit(desc, value);
}//include/asm-generic/gpio.h
static inline int __gpio_get_value(unsigned gpio)
{return gpiod_get_raw_value(gpio_to_desc(gpio));
}//drivers/gpio/gpiolib.c
int gpiod_get_raw_value(const struct gpio_desc *desc)
{VALIDATE_DESC(desc);/* Should be using gpiod_get_raw_value_cansleep() */WARN_ON(desc->gdev->chip->can_sleep);return gpiod_get_raw_value_commit(desc);
}
gpiod_get_value 調用 gpiod_set_value_nocheck 函數中會判斷 flags 為 FLAG_ACTIVE_LOW 就翻轉 value。gpiod_set_value函數同理。
//get同理
int gpiod_get_value(const struct gpio_desc *desc)
{int value;VALIDATE_DESC(desc);/* Should be using gpiod_get_value_cansleep() */WARN_ON(desc->gdev->chip->can_sleep);value = gpiod_get_raw_value_commit(desc);if (value < 0)return value;if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))value = !value;return value;
}
參考鏈接:
在Linux驅動中使用gpio子系統 - schips - 博客園
嵌入式Linux中的 gpio、gpiod基本分析_51CTO博客_linux gpio操作
詳解GPIO子系統_gpio子系統 設備樹講解-CSDN博客