zoukankan      html  css  js  c++  java
  • 【linux】驱动-11-gpio子系统


    前言

    参考文档:

    建议:复制以下链接,到原文观看,原文排版清晰,便于学习。

    11. gpio子系统

    引脚配置为 GPIO 模式后,便可使用 GPIO子系统 来控制引脚。
    可以通过 pinctrl子系统 配置,也可以自己编程配置。

    参考文档:Documentation/devicetree/bindings/gpio/ 下对应芯片厂商的文件。

    11.1 操作步骤

    1. 在设备树对应节点中指定引脚。(哪一组、组里哪一个引脚)
    2. 在驱动程序中通过 GPIO 子系统提供的 API 控制引脚。

    11.1.1 新版 API 操作流程

    个人喜欢用代码格式表述:

    /** @file driver.c
     * @brief 驱动教程文件
     * @details 
     * @author lzm
     * @date 2021-04-09 20:22:22
     * @version v1.0
     * @copyright Copyright By lizhuming, All Rights Reserved
     * @cnblogs   https://www.cnblogs.com/lizhuming/
     **********************************************************
     * @LOG 修改日志:
     **********************************************************
     */
    
    /* gpio 子系统开发步骤 */
    
    /* [gpio][1] 请求引脚 */
    /* 使用 gpiod_get() 或 devm_gpiod_get() 或 gpiod_get_index() 或 devm_gpiod_get_index() 等等函数 */
    
    /* [gpio][2] 设置方向 */
    /* 使用 gpiod_direction_input() 或 gpiod_direction_output() 等等函数 */
    
    /* [gpio][3] 导出到应用层 */
    /* 使用 gpiod_export() 函数 */
    
    /* [gpio][4] 设置/获取 值 */
    /* 使用 gpiod_set_value() 或 gpiod_get_value() 等等函数 */
    
    /* [gpio][5] 转中断,注册中断 */
    /* 使用 gpiod_set_value() 和 request_irq() 函数 */
    
    /* [gpio][6] 释放引脚 */
    /* 使用 gpiod_put() 或 gpiod_put() 函数 */
    
    

    11.1.2 旧版 API 操作流程

    流程和新版 API 操作流程差不多。找到对应的函数即可。

    11.2 设备树中使用gpio子系统

    在设备树中,GPIO组 就是一个 GPIO Controller

    GPIO组 的节点内容是由芯片厂商设置好的,一般在芯片厂商提供的设备树头文件 xxx.dtsi 中。如 IMX6UL 的就在 imx6ull.dtsi 文件中定义。
    用户只需要做的是根据芯片厂商文档格式要求,在相应设备树节点中填写引脚信息。
    如 IMX6ULL:fsl-imx-gpio.txt

    一般,我们参考 GPIO组 节点里面的两个属性即可:

    • gpio-controller
      • 如果 GPIO组 节点内含有该属性,则表示该节点为 GPIO 控制器节点。
    • gpio-cells
      • 表示这个控制器下的每一个引脚需要用多少个 32 位数来描述。

    如 IMX6ULL:

    • gpio-controller: Marks the device node as a gpio controller.
    • #gpio-cells: Should be two.
      • The first cell is the pin number.
      • The second cell is used to specify the gpio polarity:
        • 0 = active high.
        • 1 = active low.

    例子:

    /*添加rgb_led节点*/
    rgb_led{
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "fire,rgb-led"; // 节点兼容性
        /* pinctrl 子系统 */
        pinctrl-names = "default"; // 引脚状态名称表
        pinctrl-0 = <&pinctrl_rgb_led>; // 第 0 个状态使用的引脚
        /* GPIO子系统 */
        rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>; // 使用的引脚 。旧版
        rgb_led_green-gpios = <&gpio4 20 GPIO_ACTIVE_LOW>; // 新版
        rgb_led_blue-gpios = <&gpio4 19 GPIO_ACTIVE_LOW>; // 新版
        status = "okay";
    };
    

    分析 rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;

    • rgb_led_red:自定义的引脚名字。(旧版
      • 新版必须使用 gpios 或 后缀为 -gpios 的属性名。这样才能使用新的 GPIO 子系统 API。
      • 如:rgb_led_red 改为 rgb_led_red-gpios
    • &gpio1:GPIO组1。
    • 4:第 4 号引脚。
    • GPIO_ACTIVE_LOW:低电平有效。

    11.3 GPIO 子系统 API 说明

    GPIO 子系统有两套API

    • 基于描述符(descriptor-based)
      • 前缀为 gpiod_
      • 使用 gpio_desc 几个题来表示一个引脚。
      • 参考文档:Documentation/gpio/consumer.txt
    • 老接口(legacy)
      • 前缀为 gpio_
      • 使用一个整数来表示一个引脚。
      • 参考文档:Documentation/gpio/gpio-legacy.txt

    11.3.1 驱动操作一个引脚的步骤

    1. get 引脚。
    2. 设置引脚方向。
    3. 读、写引脚。

    11.3.2 API 所需头文件

    #include <linux/gpio/consumer.h>   // 基于描述符
    
    #include <linux/gpio.h>            // 老接口 
    

    11.3.3 主要结构体

    gpio_desc:

    struct gpio_desc 
    {
        struct gpio_chip  *chip;  /* 这个 gpio pin 所在的 chip */
        unsigned long  flags;    /*  设置 is_out flag */
        const char  *label;        /* label 就是名字 */
    };
    
    • 源码路径:内核源码driversgpiogpiolib.h
    struct gpio_descs 
    {
        unsigned int ndescs; // gpio_desc 个数
        struct gpio_desc *desc[]; // gpio_desc
    }
    
    • 源码路径:内核源码driversgpiogpiolib.h

    11.4 新旧版互相兼容转换 API

    旧版API是使用整数标记引脚的;
    新版API是使用字符标记引脚的。
    但是引脚都是唯一的,所以两者可以相互转化。

    转化函数:

    • desc_to_gpio()
    • gpio_to_desc()

    desc_to_gpio

    • 函数原型:int desc_to_gpio(const struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib.c
      • 通过引脚 gpio_desc 结构体指针 获取引脚 GPIO 号。
      • gpio:GPIO number。

    gpio_to_desc

    • 函数原型:struct gpio_desc *gpio_to_desc(unsigned gpio)
      • 源码路径:driversgpiogpiolib.c
      • 通过引脚 GPIO 号获取引脚 gpio_desc 结构体指针。
      • gpio:GPIO number。

    11.5 descriptor-based 版常用 API

    11.5.1 获取 GPIO

    gpiod_get

    • 函数原型:struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
      • 源码路径:driversgpiogpiolib.c
      • 获取 dev 设备,con_id 的第 0 个引脚信息,并做 flags 初始化。
      • dev:设备指针。从该设备获取引脚信息。
      • con_id:引脚组名称(不包含前缀)。
        • 如引脚组名为 rgb_led_green-gpios。则 con_id = "rgb_led_green"
      • flags:初始化标志。
        • GPIOD_ASIS or 0 to not initialize the GPIO at all. The direction must be set later with one of the dedicated functions.
        • GPIOD_IN to initialize the GPIO as input.
        • GPIOD_OUT_LOW to initialize the GPIO as output with a value of 0.
        • GPIOD_OUT_HIGH to initialize the GPIO as output with a value of 1.
        • GPIOD_OUT_LOW_OPEN_DRAIN same as GPIOD_OUT_LOW but also enforce the line to be electrically used with open drain.
        • GPIOD_OUT_HIGH_OPEN_DRAIN same as GPIOD_OUT_HIGH but also enforce the line to be electrically used with open drain.
      • 返回:
        • 成功:gpio_desc 结构体指针。
        • 失败:-ENOENT。具体可通过 IS_ERR() 获取返回码。

    gpiod_get_index

    • 函数原型:struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int idx, enum gpiod_flags flags)
      • 源码路径:driversgpiogpiolib.c
      • 获取 dev 设备,con_id 的第 idx 个引脚信息,并做 flags 初始化。

    gpiod_get_array

    • 函数原型:struct gpio_descs *__must_check gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
      • 源码路径:driversgpiogpiolib.c
      • 获取 dev 设备 con_id 的所有引脚信息,并做 flags 初始化。

    其它获取GPIO的函数

    • gpiod_get_optional:和 gpiod_get 差不多。不同的是该函数返回 gpio_desc 结构体指针NULL
    • gpiod_get_index_optional
    • gpiod_get_index_optional
    • devm_xxx:以上函数均可添加 devm_ 前缀。比以上函数多了绑定设备,设备被删除时,自动释放引脚。

    11.5.2 释放 GPIO

    gpiod_put

    • 函数原型:void gpiod_put(struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib.c
      • 释放 desc 引脚。
      • desc:gpio_desc 结构体指针。

    其它释放GPIO的函数

    • gpiod_put_array
    • devm_gpiod_put
    • devm_gpiod_put_array

    11.5.3 设置/获取 GPIO 方向

    gpiod_direction_input

    • 函数原型:int gpiod_direction_input(struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib.c
      • 设置该引脚为输入方向。

    gpiod_direction_output

    • 函数原型:int gpiod_direction_output(struct gpio_desc *desc, int value)
      • 源码路径:driversgpiogpiolib.c
      • 设置该引脚为输入方向。
      • value:设置方向后的初始值。

    gpiod_get_direction

    • 函数原型:int gpiod_get_direction(struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib.c
      • 获取引脚方向。
      • 返回:
        • 0:输出。
        • 1:输入。

    11.5.4 导出 GPIO 到 sysfs

    gpio 通过 sys 文件系统导出,应用层可以通过文件操作gpio。如查看状态、设置状态等等。

    主要用于调试

    导出后访问路径:/sys/class/gpio 下。

    gpiod_export

    • 函数原型:int gpiod_direction_input(struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib-sysfs.c
      • 把该 gpio 导出到 sys。

    gpiod_unexport

    • 函数原型:void gpiod_unexport(struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib-sysfs.c
      • 取消导出。

    11.5.5 设置/获取 GPIO 值

    有两种访问方式:

    1. 原子方式。
    2. 队列方式。

    gpiod_get_value

    • 函数原型:int gpiod_get_value(const struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib.c
      • 获取引脚值。
      • 返回:
        • 成功:非负数:zero for low, nonzero for high。
        • 失败:负数。

    gpiod_set_value

    • 函数原型:int gpiod_set_value(struct gpio_desc *desc, int value)
      • 源码路径:driversgpiogpiolib.c

    以上两种函数均属原子操作,能作用于中断程序
    以下两种函数,在队列中等待访问引脚,可能会进入睡眠,不能作用于中断

    访问必须通过消息总线比如I2C或者SPI,这些需要在队列中访问。

    gpiod_get_value_cansleep

    • 函数原型:int gpiod_get_value_cansleep(const struct gpio_desc *desc)

    gpiod_set_value_cansleep

    • 函数原型:void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)

    可以使用 gpiod_cansleep() 函数分辨该引脚是否需要通过消息总线访问

    gpiod_cansleep

    • 函数原型:int gpiod_cansleep(const struct gpio_desc *desc)

    11.5.6 GPIO IRQ

    gpiod_to_irq

    • 函数原型:int gpiod_to_irq(const struct gpio_desc *desc)
      • 源码路径:driversgpiogpiolib.c
      • 获取该引脚对应的 IRQ number
      • 返回:
        • 成功:中断号。
        • 失败:负数。

    request_irq

    • 函数原型:static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
      • 源码路径:driversgpiogpiolib.c
      • 请求中断。
      • irq:中断号。
      • handler:中断回调函数。
      • flags:中断类型。
      • name:请求中断的设备名称。
      • dev:可取任意值。
        • 但必须唯一能够代表发出中断请求的设备。
        • 通常取描述该设备的结构体,或NULL。
        • 用于共享中断时。(若中断被共享,则不能为 NULL

    11.5.7 GPIO 逻辑电平与物理电平

    当设备采用低电平有效时,即是低电平为逻辑 1,高电平为逻辑 0

    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);
    

    逻辑电平相关函数 与 物理电平相关函数对比

    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

    11.6 legacy 版常用 API

    尽管建议升级使用新版API,但是现在很多子系统依然使用旧版API。
    所以,本笔记也记录旧版API。

    11.6.1 判断 GPIO number

    gpio_is_valid

    • 函数原型:static inline bool gpio_is_valid(int number)
      • 源码路径:includeasm-genericgpio.h
      • 判断 GPIO number 是否有效。
      • 返回:
        • 有效:1。
        • 无效:0。

    11.6.2 申请 GPIO

    gpio_request

    • 函数原型:int gpio_request(unsigned gpio, const char *label)

      • 源码路径:driversgpiogpiolib-legacy.c
      • 申请 GPIO。
      • gpio:需要申请的 GPIO 编号。
      • label:引脚名字,相当于为申请到的引脚取个别名。
      • 返回:
        • 成功:0。
        • 失败:负数。

      其它请求函数

      • gpio_request_one()
      • gpio_request_array()

    11.6.3 释放 GPIO

    gpio_free

    • 函数原型:static inline void gpio_free(unsigned gpio);

      • 源码路径:driversgpiogpiolib-legacy.c
      • 申请 GPIO。
      • gpio:需要释放的 GPIO 编号。

      其它释放函数

      • gpio_free_array()

    11.6.3 设置 GPIO 方向

    gpio_direction_input

    • 函数原型:static inline int gpio_direction_input(unsigned gpio)
      • 源码路径:includelinuxgpio.h
      • 把 gpio 引脚设置为为输入方向。
      • gpio:GPIO 编号。
      • 返回:
        • 成功:0。
        • 失败:负数。

    gpio_direction_output

    • 函数原型:static inline int gpio_direction_output(unsigned gpio, int value)
      • 源码路径:includelinuxgpio.h
      • 把 gpio 引脚设置为为输出方向。
      • gpio:GPIO 编号。
      • value:初始值。
      • 返回:
        • 成功:0。
        • 失败:负数。

    11.6.4 导出 GPIO 到 sysfs

    gpio 通过 sys 文件系统导出,应用层可以通过文件操作gpio。如查看状态、设置状态等等。
    主要用于调试

    导出后访问路径:/sys/class/gpio 下。

    gpio_export

    • 函数原型:static inline int gpio_export(unsigned gpio, bool direction_may_change)
      • 源码路径:includelinuxgpio.h
      • 把该 gpio 导出到 sys。
      • gpio:GPIO number。
      • direction_may_change:表示用户是否可以改变方向。

    gpio_unexport

    • 函数原型:static inline void gpio_unexport(unsigned gpio)
      • 源码路径:includelinuxgpio.h
      • 取消导出。
      • gpio:GPIO number。

    11.6.5 设置/获取 GPIO 值

    有两种访问方式:

    1. 原子方式。
    2. 队列方式。

    gpio_get_value

    • 函数原型:static inline int gpio_get_value(unsigned gpio)
      • 源码路径:includeasm-genericgpio.h
      • 获取引脚值。
      • 返回:
        • 成功:非负数:zero for low, nonzero for high。
        • 失败:负数。

    gpio_set_value

    • 函数原型:static inline void gpio_set_value(unsigned int gpio, int value)

      • 源码路径:includeasm-genericgpio.h

      其它设置函数

      • gpio_set_debounce():支持消抖。

    以上两种函数均属原子操作,能作用于中断程序
    以下两种函数,在队列中等待访问引脚,可能会进入睡眠,不能作用于中断

    访问必须通过消息总线比如I2C或者SPI,这些需要在队列中访问。

    gpio_get_value_cansleep

    • 函数原型:int gpio_get_value_cansleep(unsigned gpio)

    gpio_set_value_cansleep

    • 函数原型:void gpio_set_value_cansleep(unsigned gpio, int value)

    可以使用 gpiod_cansleep() 函数分辨该引脚是否需要通过消息总线访问

    gpio_cansleep

    • 函数原型:int gpiod_cansleep(unsigned gpio)

    11.6.6 GPIO IRQ

    gpio_to_irq

    • 函数原型:int gpio_to_irq(unsigned gpio)
      • 源码路径:driversgpiogpiolib.c
      • 获取该引脚对应的 IRQ number
      • 返回:
        • 成功:中断号。
        • 失败:负数。

    irq_to_gpio:(尽量避免使用

    • 函数原型:int irq_to_gpio(unsigned irq)
      • 源码路径:driversgpiogpiolib.c
      • 获取该引脚对应的 GPIO number
      • 返回:
        • 成功:gpio号。
        • 失败:负数。

    request_irq

    • 函数原型:static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
      • 源码路径:driversgpiogpiolib.c
      • 请求中断。
      • irq:中断号。
      • handler:中断回调函数。
      • flags:中断类型。
      • name:请求中断的设备名称。
      • dev:可取任意值。
        • 但必须唯一能够代表发出中断请求的设备。
        • 通常取描述该设备的结构体,或NULL。
        • 用于共享中断时。(若中断被共享,则不能为 NULL
  • 相关阅读:
    相对路径与绝对路径问题
    javaee自定义servlet的步骤
    Struts1.X与Spring集成——另外一种方案
    菜鸟也能学cocos2dx3.0 浅析刀塔传奇(下)
    JAVA之了解类载入器Classloader
    IOS 编程中引用第三方的方类库的方法及常见问题
    通过eclipse的egit插件提交提示Auth fail
    定时器0的方式1 定时器1的方式1 数码管和led
    MongoDB入门学习(四):MongoDB的索引
    J2EE--JDBC
  • 原文地址:https://www.cnblogs.com/lizhuming/p/14652611.html
Copyright © 2011-2022 走看看