Linux驅動編程 - gpio、gpiod函數

?????

目錄

簡介:

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


(1)基于描述符(descriptor-based): // 官方推薦
  • 前綴為?gpiod_
  • 使用?gpio_desc?幾個題來表示一個引腳。
  • 參考文檔:Documentation/gpio/consumer.txt
(2)老接口( legacy integer-based):
  • 前綴為?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

(1)int desc_to_gpio(const struct gpio_desc *desc)

功能:通過引腳 gpio_desc 結構體指針 獲取引腳 GPIO 編號;

(2)struct gpio_desc *gpio_to_desc(unsigned 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
源碼路徑: drivers\gpio\gpiolib.c

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,高電平為邏輯?0gpiod_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_"為前綴)


參考文檔: Documentation/gpio/gpio-legacy.txt
使用流程:
  • 申請、釋放: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)
gpio_request_one 請求 GPIO 并同時設置初始方向/值,flags 配置 GPIO 的初始狀態
標志
含義
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 方向


源碼路徑: include\linux\gpio.h

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 值


源碼路徑: include\asm-generic\gpio.h

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


源碼路徑: drivers\gpio\gpiolib.c

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博客

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

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

相關文章

Tensorflow推理時遇見PTX錯誤,安裝CUDA及CuDNN, 解決問題!

問題原因&#xff1a; 使用TensorFlow一個小模型是進行推理的時候&#xff0c;報了PTX錯誤&#xff1a; Traceback (most recent call last): 20273 2025-06-18 10:20:38.345 INFO 1 --- [checkTask-1] c.l.a.d.a.util.AnalyzeCommonHelper : File "/home/python/commo…

C# 網絡編程-關于HTTP/HTTPS的基礎(一)

一、HTTP基礎概念 1. 請求-響應模型 HTTP是基于客戶端-服務器的無狀態協議&#xff0c;流程如下&#xff1a; 客戶端&#xff08;如瀏覽器&#xff09;發起請求。服務器接收請求并處理。服務器返回響應&#xff0c;包含狀態碼、Header和響應體。連接關閉&#xff0c;后續請求…

小程序右上角○關閉事件

小程序用戶真實離開事件追蹤&#xff1a;一場與技術細節的博弈 在數據分析的場景下&#xff0c;精準捕捉用戶行為至關重要。我們遇到了這樣一個需求&#xff1a;在小程序的埋點方案中&#xff0c;只記錄用戶真正意義上的離開&#xff0c;即通過點擊小程序右上角關閉按鈕觸發的…

數據庫高性能應用分析報告

數據庫高性能應用分析報告 引言摘要 在數字經濟加速發展的今天&#xff0c;數據庫性能已成為企業核心競爭力的關鍵要素。根據Gartner 2024年最新研究&#xff0c;全球企業因數據庫性能問題導致的直接經濟損失高達每年420億美元&#xff0c;同時性能優化帶來的業務提升可達到2…

Java使用itext pdf生成PDF文檔

Java使用itext pdf生成PDF文檔 Java使用itextpdf生成PDF文檔 在日常開發中&#xff0c;我們經常需要生成各種類型的文檔&#xff0c;其中PDF是最常用的一種格式。本文將介紹如何使用Java和iText庫生成包含中文內容的PDF文檔&#xff0c;并通過一個具體的示例來展示整個過程。…

利用VBA將Word文檔修改為符合EPUB3標準規范的HTML文件

Word本身具有將docx文件轉換為HTML文件的功能&#xff0c;但是轉換出來的HTML文檔源代碼令人不忍卒讀&#xff0c;占用空間大&#xff0c;可維護性極差&#xff0c;如果想給HTML文檔加上點自定義交互行為&#xff0c;也不是一般的麻煩。如果文檔中包含注釋&#xff0c;對于Word…

開發語言本身只是提供了一種解決問題的工具

前言 你是否曾經注意到&#xff0c;在中國的軟件工程師日常工作中&#xff0c;他們使用的工具界面大多為英文&#xff1f;從代碼編輯器到開發框架文檔&#xff0c;再到錯誤信息提示框&#xff0c;英語似乎已經成為了計算機領域事實上的標準語言。那么為什么在全球化日益加深的…

2024計算機二級Python真題精講|第一套(易錯點分析)

一、選擇題 1.計算機完成一條指令所花費的時間稱為一個( )。 A.執行時序 B.執行速度 C.執行速度 D.指令周期 答案 D 一般把計算機完成一條指令所花費發時間稱為一個指令周期。指令周期越短&#xff0c;指令執行就越快。 2.順序程序不具有&#xff08; &#xf…

BGP路由反射器(RR)實驗詳解,結尾有詳細腳本

目錄 路由反射器基礎概念 實驗拓撲與設計 實驗配置步驟 配置驗證與排錯 實驗總結 完整配置命令集 路由反射器基礎概念 在傳統的IBGP網絡中&#xff0c;為了防止路由環路&#xff0c;BGP規定通過IBGP學到的路由不能再傳遞給其他IBGP對等體&#xff0c;這導致所有IBGP路由…

(aaai2025) SparseViT: 用于圖像篡改檢測的Spare-Coding Transformer

論文&#xff1a;(aaai2025) SparseViT: Nonsemantics-Centered, Parameter-Efficient Image Manipulation Localization through Spare-Coding Transformer 代碼&#xff1a;https://github.com/scu-zjz/SparseViT 這個論文研究的是圖像篡改檢測&#xff08;Image Manipulatio…

C#測試調用Markdig解析Markdown的基本用法

Markdig是.NET平臺的高性能開源Markdown處理器&#xff0c;嚴格遵循 CommonMark 標準&#xff0c;確保解析一致性&#xff0c;其核心優勢在于擴展性強&#xff1a;通過模塊化管道模型&#xff0c;可輕松添加自定義語法或修改現有邏輯。Markdig內置支持表格、任務列表、數學公式…

MySQL 主從同步完整配置示例

以下是 MySQL 主從同步完整配置示例&#xff08;基于 Linux 系統&#xff09;&#xff0c;包含主庫和從庫的配置步驟&#xff1a; 一、主庫&#xff08;Master&#xff09;配置 1. 安裝 MySQL&#xff08;以 CentOS 為例&#xff09; yum install -y mysql-server systemctl …

可信啟動與fTPM的交互驗證(概念驗證)

安全之安全(security)博客目錄導讀 目錄 一、組件構成 二、Arm FVP平臺PoC構建 三、在Armv8-A Foundation FVP上運行PoC 四、微調fTPM TA 可信啟動&#xff08;Measured Boot&#xff09;是通過密碼學方式度量啟動階段代碼及關鍵數據&#xff08;例如使用TPM芯片&#xff…

SQL Server基礎語句4:數據定義

文章目錄 一、數據庫與架構1.1 創建與刪除數據庫1.1.1 使用CREATE DATABASE語句創建數據庫1.1.2 使用DROP DATABASE語句刪除數據庫1.1.3 使用SSMS創建數據庫1.1.4 使用SSMS刪除數據庫 1.2 CREATE SCHEMA&#xff1a;創建新架構1.2.1 Schema簡介1.2.2 使用CREATE SCHEMA語句創建…

上門按摩app會員系統框架搭建

一、邏輯分析 用戶注冊與登錄&#xff1a; 新用戶需要提供基本信息&#xff0c;如姓名、手機號、郵箱等進行注冊。手機號用于接收驗證碼進行身份驗證&#xff0c;郵箱可用于密碼找回等功能。注冊成功后&#xff0c;用戶可以使用手機號 / 郵箱和密碼進行登錄。登錄時需要驗證用戶…

java項目打包成jar包,并給jmeter使用

1.新建項目 編寫代碼&#xff0c;導入必要的jar包&#xff0c; 右鍵點擊項目&#xff0c;然后export&#xff0c;選擇main函數&#xff0c; package utils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Random; …

【Qt開發】Qt音頻

Qt中&#xff0c;視頻播放的功能主要是通過 QMediaPlayer類和 QVideoWidget類來實現。在使用這兩個類時&#xff0c;需要在 .pro項目配置文件中添加對應的模塊——multimedia 和 multimediawidgets。 核心的API如下&#xff1a; 下面來模擬實現音頻文件的播放。 Widget::Widget…

橢球面上梯形面積的通用公式和圖幅公式

import numpy as np def ellipsoidal_trapezoid_area(a, b, phi1_deg, phi2_deg, delta_L_deg, is_map_sheetFalse): """ 計算橢球面上梯形面積的通用公式和圖幅公式 參數: a: 橢球長半軸&#xff08;米&#xff09; b: 橢球…

Pytest 入門:測試函數與測試類詳解

概述 在編寫自動化測試時,了解如何組織和管理測試用例是至關重要的。Pytest 提供了靈活的方式來定義測試函數和測試類,并支持多層次的設置(setup)和清理(teardown)機制,幫助你更高效地進行代碼驗證。本文將詳細介紹 測試函數 和 測試類 的概念、定義、注意點以及實際操…

大模型的部署簡單搭建

大模型的部署搭建 真的好久沒有進行博客的寫作了,原因,最近的自己生活有點小波動, 最近想想還是有空寫一篇文章,這篇文章的描述是學習的當下熱門AI 本期,介紹的也是AI相關的LLM(大語言模型) 這個好多人應該都是模糊的,包括我一開始沒有學習,就感覺牛逼的不行,今天我們親自部署實…