​​​​​

目录

简介:

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(大语言模型) 这个好多人应该都是模糊的,包括我一开始没有学习,就感觉牛逼的不行,今天我们亲自部署实…