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);                // 释放 GPIO
    
    return 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); // 释放 GPIO
    return 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的中断handler

int 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_value

static 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_value

static 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 1
    if (gpio) {
        if (gpiod_cansleep(gpio_to_desc(gpio)))
            gpio_set_value_cansleep(gpio, 0);
        else
            gpio_set_value(gpio, 0);        //输出低电平
    }
#else
    gpio_set_value(gpio, 1);	    //输出高电平
#endif
    
    gpio_free(gpio);        //释放gpio
    
    return 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);        //释放gpio
    
    return 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博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值