GPIO 子系统重要概念

GPIO 子系统重要概念

要操作 GPIO 引脚,先把所用到的引脚配置为 GPIO 功能,通过Pinctrl 子系统来实现。然后根据设置引脚方向(输入还是输出)、读取值 —— 获得电平状态,写入值—— 输出高低电平。

以前我们通过寄存器来操作 GPIO 引脚,即使LED 驱动程序,对于不同的板子它的代码也完全不同。

当BSP 工程师实现了GPIO 子系统后,我们就可以

  1. 在设备树里面指定GPIO 引脚。
  2. 在驱动代码中:使用GPIO 子系统的标准函数获得 GPIO 、设置GPIO 方向、读取/设置GPIO 的值。

这样的驱动代码,将与单板无关。

设备树中指定引脚

几乎在所有的ARM 芯片中,GPIO 都分为几组,每组若干个引脚,所以在使用GPIO 子系统之前,我们就要确定它是哪组的,组里的哪一个的。
在设备树中, GPIO 组就是一个 GPIO Controller ,这通常都由芯片厂家设置好,我们要做的是找到它的名字,比如gpio1,然后指定要用它里面的那个引脚,比如 <&gpio1 0>。

下图是一些芯片的 GPIO 控制器节点,他们一般都是厂家定义好,在xxx.dtsi文件中
在这里插入图片描述
我们暂时需要关心的属性有2个

gpio-controller;
#gpio-cells=<2>;
  • gpio-controller 表示这个节点是一个 GPIO Controller ,它下面有很多引脚。
  • #gpio-cells=<2> 表示这个控制器下每一个引脚要用2个32位的数字(cell)来描述。

为什么要用 2 个数?其实使用多个 cell 来描述一个引脚,这是 GPIO Controller 自己决定的。比如可以用其中一个 cell 来表示那是哪一个引脚,用另一个 cell 来表示它是高电平有效还是低电平有效,甚至还可以用更多的cell 来示其他特性。

普遍的用法是,用第1个 cell 来表示哪一个引脚,用第 2 个 cell 来表示有效电平:

GPIO_ACTIVE_HIGH : 高电平有效
GPIO_ACTIVE_LOW :   低电平有效

定义 GPIO Controller 是芯片厂家的事,我们怎么引用某个引脚呢?在自己的设备节点中使用属性" [-]gpios",示例如下
在这里插入图片描述
上图中,可以使用 gpios 属性,也可以使用 name-gpios 属性。

在驱动代码中调用GPIO 子系统

在设备树中指定了GPIO 引脚,在驱动代码中如何使用?
也就是GPIO 子系统的接口函数是什么?

GPIO 子系统有两套接口:基于描述符的(descriptor-based)老的(legacy);前者的函数都有前缀gpiod_,它使用gpio_desc结构体来表示一个引脚;后者的函数都有前缀gpio_,它使用一个整数来表示一个引脚。

要操作一个引脚,首先要get 引脚,然后设置方向,读取值,写入值。驱动程序中要包含头文件

#include <linux/gpio/consumer.h> // descriptor-based
#include <linux/gpio.h>          // legacy

下表列出常用的函数

descriptor-basedlegacy(老的)
获得 GPIO
gpiod_getgpio_request
gpiod_get_index
gpiod_get_arraygpio_request_array
devm_gpiod_get
devm_gpiod_get_index
devm_gpiod_get_array
设置方向
gpiod_direction_inputgpio_direction_input
gpiod_direction_outputgpio_direction_output
读值、写值
gpiod_get_valuegpio_get_value
gpiod_set_valuegpio_set_value
释放 GPIO
gpiod_freegpio_free
gpiod_putgpio_free_array
gpiod_put_array
devm_gpiod_put
devm_gpiod_put_array

有前缀 “devm_”的含义是“设备资源管理 ” (Managed Device Resource),这是一种自动释放资源的机制。它的思想是“资源是属于设备的,设备不存在时资源就可以自动释放”。

比如在 Linux 开发过程中,先申请了 GPIO,再申请内存;如果内存申请失败,那么在返回之前就需要先释放 GPIO 资源。如果使用 devm 的相关函数,在内存申请失败时可以直接返回:设备的销毁函数会自动地释放已经申请了的GPIO 资源。

建议使用“ devm_”版本的相关函数。

假设备在设备树中有如下节点:

foo_device {
    compatible = "acme,foo";
    ...
    led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
                <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
                <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
    power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
};

那么可以使用下面的函数获得引脚

struct gpio_desc *red, *green, *blue, *power;
 
// 取出名为led的第0个引脚,并设置为输出高电平
red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);        
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);
power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);

gpiod_set_value 设置的值是“逻辑值”,不一定等于物理值。什么意思?
在这里插入图片描述
旧的“ gpio_”函数没办法根据设备树信息获得引脚,它需要先知道引脚号。

引脚号怎么确定?

在 GPIO 子系统中,每注册一个 GPIO Controller 时会确定它的“ base number”,那么这个控制器里的第 n 号引脚的号码就是: base number + n。

但是如果硬件有变化、设备树有变化,这个 base number 并不能保证是固定的,应该查看 sysfs 来确定 base number。

sysfs 中的访问方法

sysfs中访问 GPIO ,实际上用的就是引脚号,老的方法。

先确定某个 GPIO Controller 的基准引脚号(base number),再计算出某个引脚的号码。

方法如下:

  1. 先在开发板的/sys/class/gpio 目录下,找到各个 gpiochipXXX 目录
  2. 然后进入某个 gpiochip 目录,查看文件 label 的内容
  3. 根据 label 的内容对比设备树

label 内容来自设备树,比如它的寄存器基地址。用来跟设备树(dtsi 文件)比较,就可以知道这对应哪一个 GPIO Controller。

通过对比设备树可知 gpiochip128对应 gpioH:
在这里插入图片描述
有多个gpiochipXXXX 文件,其中gpiochip128 的基地址是128,ngpio 是个数,GPIOH是第H 组GPIO ,有时候是一串别的字符串,根据字符在设备树中查找对应的GPIO 组。

所以 gpioH 这组引脚的基准引脚号就是 128,这也可以“ cat base”来再次确认。

基于 sysfs 操作引脚:

100ask_im6ull 为例,他有一个按键,原理图如下:
在这里插入图片描述
那么GPIO_14的引脚编号就是 96 + 14 = 110,可以如下操作读取按键的值

echo 110 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio98/direction
cat /sys/class/gpio/gpio98/value

不用的时候执行

echo 110 > /sys/class/gpio/unexport

对于输出引脚,假设引脚号为 N,可以用下面的方法设置它的值为 1:

echo N > /sys/class/gpio/export
echo out > /sys/class/gpio/gpioN/direction
echo 1 > /sys/class/gpio/gpioN/value
echo N > /sys/class/gpio/unexport

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值