zoukankan      html  css  js  c++  java
  • GPIOLIB框架下的GPIO驱动

    一、GPIOLIB 简介

    GPIO(通用目的输入/输出端口)是一种灵活的软件控制的数字信号。大多数的嵌入式 处理器都引出一组或多组的 GPIO,并且部分普通管脚通过配置可以复用为 GPIO。

    利用可 编程逻辑器件,或总线(如 I2C、SPI)转 GPIO 芯片,也可以扩展系统的 GPIO。不管是何种 GPIO,GPIOLIB 为内核和用户层都提供了标准的操作方法。

    GPIOLIB 的接口十分简洁。在 GPIOLIB,所有的 GPIO 都是用整形的 GPIO 编号标识。 只要获得要操作 GPIO 的编号,就可以调用 GPIOLIB 提供的方法操作 GPIO。

    二、GPIOLIB 的内核接口

    GPIOLIB 的内核接口是指:若某些 GPIO 在 GPIOLIB 框架下被驱动后,GPIOLIB 为内 核的其它代码操作该 GPIO 而提供的标准接口。

    1 GPIO 的申请和释放

    GPIO 在使用前,必须先调用 gpio_request()函数申请 GPIO:

    int gpio_request(unsigned gpio, const char *label);

    该函数的 gpio 参数为 GPIO 编号;label 参数为 GPIO 的标识字符串,可以随意设定。 若该函数调用成功,将返回 0 值;否则返回非 0 值。gpio_request()函数调用失败的原因可能为 GPIO 的编号不存在,或在其它地方已经申请了该 GPIO 编号而还没有释放。

    当 GPIO 使用完成后,应当调用 gpio_free()函数释放 GPIO:

    void gpio_free(unsigned gpio);

    2 GPIO 的输出控制

    在操作 GPIO 输出信号前,需要调用 gpio_direction_output()函数把 GPIO 设置为输出方向:

    int gpio_direction_output(unsigned gpio, int value);

    把 GPIO 设置为输出方向后,参数 value 为默认的输出电平:1 为高电平;0 为低电平。

    GPIO 被设置为输出方向后,就可以调用 gpio_set_value()函数控制 GPIO 输出高电平或 低电平:

     void gpio_set_value(unsigned gpio, int value);

    该函数的 value 参数可取值为:1 为高电平;0 为低电平。

    3 GPIO 的输入控制

    当需要从 GPIO 读取输入电平状态前,需要调用 gpio_direction_input()函数设置 GPIO 为 输入方向:

     int gpio_direction_input(unsigned gpio);

    在 GPIO 被设置为输入方向后,就可以调用 gpio_get_value()函数读取 GPIO 的输入电平 状态:

     int gpio_get_value(unsigned gpio);

    该函数的返回值为 GPIO 的输入电平状态:1 为高电平;0 为低电平。

    4 GPIO 的中断映射

    大多数的嵌入式处理器的 GPIO 引脚在被设置为输入方向后,可以用于外部中断信号的 输入。这些中断号和 GPIO 编号通常有对应关系,因此 GPIOLIB 为这些 GPIO 提供了 gpio_to_irq()函数用于通过 GPIO 编号而获得该 GPIO 中断号:

     int gpio_to_irq(unsigned gpio);

    gpio_to_irq()函数调用完成后,返回 GPIO 中断号。

    由于并不是所有的 GPIO 都可以作为外部中断信号输入端口,所以 gpio_to_irq()函数不 是对所有的 GPIO 都强制实现的。

    三、GPIOLIB的实现方法

    大部分的嵌入式处理器的 GPIO 都是分组的。以 i.MX28 系列的处理器为例,所有 GPIO 被分为 5 组,每组 GPIO 数量从 20 到 32 不等。之所以把 GPIO 分组,是因为每组 GPIO 的 操作寄存器是相同或相近的。若 GPIO 是用可编程逻辑器件或总线(如 I2C、SPI 等)转 GPIO 扩展的,也需要按实现情况对其 GPIO 分组。GPIOLIB 对系统的所有 GPIO 统一编号,而每 组的 GPIO 编号都是连续的。

    GPIOLIB 对每组 GPIO 都用一个 gpio_chip 对象来实现其驱动,其定义如所示:

     1 struct gpio_chip {
     2     const char        *label;
     3     struct device     *dev;
     4     struct module     *owner;
     5     struct list_head  list;
     6 
     7     int            (*request)(struct gpio_chip *chip, unsigned offset);
     9     void           (*free)(struct gpio_chip *chip, unsigned offset);
    11     int            (*get_direction)(struct gpio_chip *chip, unsigned offset);
    13     int            (*direction_input)(struct gpio_chip *chip, unsigned offset);
    15     int            (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
    17     int            (*get)(struct gpio_chip *chip, unsigned offset);
    19     void           (*set)(struct gpio_chip *chip, unsigned offset, int value);
    21     int            (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce);
    25     int            (*to_irq)(struct gpio_chip *chip, unsigned offset);
    28     void           (*dbg_show)(struct seq_file *s, struct gpio_chip *chip);
    30     int            base;
    31     u16            ngpio;
    32     struct gpio_desc    *desc;
    33     const char          *const *names;
    34     bool            can_sleep;
    35     bool            exported;
    36 
    37 #if defined(CONFIG_OF_GPIO)
    38     /*
    39      * If CONFIG_OF is enabled, then all GPIO controllers described in the
    40      * device tree automatically may have an OF translation
    41      */
    42     struct device_node *of_node;
    43     int of_gpio_n_cells;
    44     int (*of_xlate)(struct gpio_chip *gc,
    45             const struct of_phandle_args *gpiospec, u32 *flags);
    46 #endif
    47 #ifdef CONFIG_PINCTRL
    48     /*
    49      * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
    50      * describe the actual pin range which they serve in an SoC. This
    51      * information would be used by pinctrl subsystem to configure
    52      * corresponding pins for gpio usage.
    53      */
    54     struct list_head pin_ranges;
    55 #endif
    56 };

    下面介绍 gpio_chip 的部分成员:

    base 该组 GPIO 的起始值。

    ngpio 该组 GPIO 的数量。

    owner  该成员表示所有者,一般设置为 THIS_MODULE。

    request 在对该组的 GPIO 调用 gpio_request()函数时,该成员指向的实现函数将被调用。 在该成员指向的实现函数中,通常需要执行指定 GPIO 的初始化操作。

    在实现函数中都是用索引值来区别组内的 GPIO。索引值是指组内的某一 GPIO 编号相 对于该组 GPIO 起始值(base)的偏移量,例如,组内第 1 个 GPIO 的索引值为 0、第 2 个 GPIO 的索引值为 1„„ 实现函数的 offset 参数为要操作 GPIO 的索引值(以下相同)。

    free 在对该组的 GPIO 调用 gpio_free()函数时,该成员指向的实现函数将被调用。在该 成员的实现函数中,通常需要执行指定 GPIO 硬件资源的释放操作。

    direction_input 在对该组的 GPIO 调用 gpio_direction_input()函数时,该成员指向的实 现函数将被调用。在该成员的实现函数中,需要把指定的 GPIO 设置为输入方向。

    get 在对该组的 GPIO 调用 gpio_get_value()函数时,该成员指向的实现函数将被调用。 在该成员的实现函数中,需要返回指定 GPIO 的电平输入状态。

    direction_output 在对该组的 GPIO 调用 gpio_direction_output()函数时,该成员指向的实 现函数将被调用。在该成员的实现函数中,需要把指定的 GPIO 设置为输出方向。

    set 在对该组的 GPIO 调用 gpio_set_value()函数时,该成员指向的实现函数将被调用。 在该成员的实现函数中,需要把指定的 GPIO 设置为指定的电平输出状态。

    当 GPIO 控制器初始化完成后,就可以调用 gpiochip_add()函数注册到内核:

     int gpiochip_add(struct gpio_chip *chip);

    该函数调用成功后,将返回 0 值;否则将返回非 0 值。

    在给一组 GPIO 安排编号时,注意不要和其它 GPIO 组的编号有重叠,否则会造成注册GPIO 控制器的出错。

    对于每种处理器平台,其最大 GPIO 编号值都由 ARCH_NR_GPIOS 宏设定的。在gpiolib.c中有引用

    static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

      ARCH_NR_GPIOS 宏的值会限制系统的 GPIO 数量。当需要为系统添加 GPIO 而 受到该值的制约时,解决办法时是把该值改成足够大,然后重新编译内核,并把新的内核固 件烧写到目标机。

    当需要把 GPIO 控制器从内核注销时,可以调用 gpiochip_remove()函数:

    int __must_check gpiochip_remove(struct gpio_chip *chip);

    该函数也需要检查返回值:0 值为注销成功;非 0 值为注销失败。注销失败的原因可能 是有 GPIO 正被使用。

    具体实现,以IMX283为例,其源码是driversgpiogpio-mxs.c文件。

     四、驱动测试

    给 gpio_chip 结构体的 base = 160; ngpio = 2;

    编译好驱动之后, 在/sys/class/gpio/目录可看到新添加的控制器

    进入 gpiochip160 目录,可以看到新添加的 GPIO 控制器的属性文件

     base 属性文件中可以看到该控制器的 GPIO 始起值;在 ngpio 属性文件中可以看到该 控制器的 GPIO 数量

     

    /sys/class/gpio/export 中,可以导出 160  161  GPIO

    gpio160 目录包含了 GPIO3_4 的控制属性文件

    这时 direction 属性文件的默认值为输入

    输入下面命令,在 direction 属性文件设置 GPIO 为输出工作状态

     # echo out >direction 

    这时在 value 属性文件分别设置 1  0 值,在 GPIO 分别输出高电平和低电平

    五、在 C 程序中操作 GPIO

     使用系统调用实现 GPIO 输入输出操作时,首先需要使用 export 属性文件导出 GPIO: 

     

     可以调用 write 函数向 direction 设备写入方向 in/out 字符串,将 GPIO 设置为输入(输 出):

     

     GPIO 设置为输入时使用 read 系统调用读取 value 属性文件,就可以读取 GPIO 电平值。 GPIO 设置为输出时,使用 write 系统调用向 value 属性文件写入 0  1 字符串,就可以设置 GPIO 电平值:

     

    注: Linux代码摘取的是3.14版本

  • 相关阅读:
    改 hadoop ssh 端口
    java.lang.OutOfMemoryError: Java heap space 解决方法
    LucidGaze for Solr 搜索监测工具
    hadoop 文件浏览器
    CF1427C Solution
    技术经理必备的六个好习惯
    今天申请博客
    同志们都走了!!
    好笑
    今天看了《浅谈多态》这篇文章
  • 原文地址:https://www.cnblogs.com/-tbd-/p/12918485.html
Copyright © 2011-2022 走看看