【linux】驱动-11-gpio子系统
前言
参考文档:
- 内核文档链接:https://www.kernel.org/doc/Documentation/
- 内核源码doc:Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
- 如在开发过程中遇到问题,建议可以查找内核源码&内核文档。
建议:复制以下链接,到原文观看,原文排版清晰,便于学习。
11. gpio子系统
引脚配置为 GPIO 模式后,便可使用 GPIO子系统 来控制引脚。
可以通过 pinctrl子系统 配置,也可以自己编程配置。
参考文档:Documentation/devicetree/bindings/gpio/ 下对应芯片厂商的文件。
11.1 操作步骤
- 在设备树对应节点中指定引脚。(哪一组、组里哪一个引脚)
- 在驱动程序中通过 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 驱动操作一个引脚的步骤
- get 引脚。
- 设置引脚方向。
- 读、写引脚。
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 就是名字 */
};
- 源码路径:内核源码\drivers\gpio\gpiolib.h
struct gpio_descs
{
unsigned int ndescs; // gpio_desc 个数
struct gpio_desc *desc[]; // gpio_desc
}
- 源码路径:内核源码\drivers\gpio\gpiolib.h
11.4 新旧版互相兼容转换 API
旧版API是使用整数标记引脚的;
新版API是使用字符标记引脚的。
但是引脚都是唯一的,所以两者可以相互转化。
转化函数:
- desc_to_gpio()
- gpio_to_desc()
desc_to_gpio:
- 函数原型:
int desc_to_gpio(const struct gpio_desc *desc)
- 源码路径:drivers\gpio\gpiolib.c
- 通过引脚 gpio_desc 结构体指针 获取引脚 GPIO 号。
- gpio:GPIO number。
gpio_to_desc:
- 函数原型:
struct gpio_desc *gpio_to_desc(unsigned gpio)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.c
- 设置该引脚为输入方向。
gpiod_direction_output:
- 函数原型:
int gpiod_direction_output(struct gpio_desc *desc, int value)
- 源码路径:drivers\gpio\gpiolib.c
- 设置该引脚为输入方向。
- value:设置方向后的初始值。
gpiod_get_direction:
- 函数原型:
int gpiod_get_direction(struct gpio_desc *desc)
- 源码路径:drivers\gpio\gpiolib.c
- 获取引脚方向。
- 返回:
- 0:输出。
- 1:输入。
11.5.4 导出 GPIO 到 sysfs
将 gpio 通过 sys 文件系统导出,应用层可以通过文件操作gpio。如查看状态、设置状态等等。
主要用于调试。
导出后访问路径:/sys/class/gpio 下。
gpiod_export:
- 函数原型:
int gpiod_direction_input(struct gpio_desc *desc)
- 源码路径:drivers\gpio\gpiolib-sysfs.c
- 把该 gpio 导出到 sys。
gpiod_unexport:
- 函数原型:
void gpiod_unexport(struct gpio_desc *desc)
- 源码路径:drivers\gpio\gpiolib-sysfs.c
- 取消导出。
11.5.5 设置/获取 GPIO 值
有两种访问方式:
- 原子方式。
- 队列方式。
gpiod_get_value:
- 函数原型:
int gpiod_get_value(const struct gpio_desc *desc)
- 源码路径:drivers\gpio\gpiolib.c
- 获取引脚值。
- 返回:
- 成功:非负数:zero for low, nonzero for high。
- 失败:负数。
gpiod_set_value:
- 函数原型:
int gpiod_set_value(struct gpio_desc *desc, int value)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:include\asm-generic\gpio.h
- 判断 GPIO number 是否有效。
- 返回:
- 有效:1。
- 无效:0。
11.6.2 申请 GPIO
gpio_request:
函数原型:
int gpio_request(unsigned gpio, const char *label)
- 源码路径:drivers\gpio\gpiolib-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);
- 源码路径:drivers\gpio\gpiolib-legacy.c
- 申请 GPIO。
- gpio:需要释放的 GPIO 编号。
其它释放函数:
- gpio_free_array()
11.6.3 设置 GPIO 方向
gpio_direction_input:
- 函数原型:
static inline int gpio_direction_input(unsigned gpio)
- 源码路径:include\linux\gpio.h
- 把 gpio 引脚设置为为输入方向。
- gpio:GPIO 编号。
- 返回:
- 成功:0。
- 失败:负数。
gpio_direction_output:
- 函数原型:
static inline int gpio_direction_output(unsigned gpio, int value)
- 源码路径:include\linux\gpio.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)
- 源码路径:include\linux\gpio.h
- 把该 gpio 导出到 sys。
- gpio:GPIO number。
- direction_may_change:表示用户是否可以改变方向。
gpio_unexport:
- 函数原型:
static inline void gpio_unexport(unsigned gpio)
- 源码路径:include\linux\gpio.h
- 取消导出。
- gpio:GPIO number。
11.6.5 设置/获取 GPIO 值
有两种访问方式:
- 原子方式。
- 队列方式。
gpio_get_value:
- 函数原型:
static inline int gpio_get_value(unsigned gpio)
- 源码路径:include\asm-generic\gpio.h
- 获取引脚值。
- 返回:
- 成功:非负数:zero for low, nonzero for high。
- 失败:负数。
gpio_set_value:
函数原型:
static inline void gpio_set_value(unsigned int gpio, int value)
- 源码路径:include\asm-generic\gpio.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)
- 源码路径:drivers\gpio\gpiolib.c
- 获取该引脚对应的 IRQ number。
- 返回:
- 成功:中断号。
- 失败:负数。
irq_to_gpio:(尽量避免使用)
- 函数原型:
int irq_to_gpio(unsigned irq)
- 源码路径:drivers\gpio\gpiolib.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)
- 源码路径:drivers\gpio\gpiolib.c
- 请求中断。
- irq:中断号。
- handler:中断回调函数。
- flags:中断类型。
- name:请求中断的设备名称。
- dev:可取任意值。
- 但必须唯一能够代表发出中断请求的设备。
- 通常取描述该设备的结构体,或NULL。
- 用于共享中断时。(若中断被共享,则不能为 NULL)
【linux】驱动-11-gpio子系统的更多相关文章
- Linux内核驱动之GPIO子系统API接口概述
1.前言 在嵌入式Linux开发中,对嵌入式SoC中的GPIO进行控制非常重要,Linux内核中提供了GPIO子系统,驱动开发者在驱动代码中使用GPIO子系统提供的API函数,便可以达到对GPIO控制 ...
- Linux 驱动框架---input子系统
input 子系统也是作为内核的一个字符设备模块存在的,所以他也是字符设备自然也会有字符设备的文件接口.input子系统的注册过程主要分为两步,先注册了一个input class然后再注册一个字符设备 ...
- Linux 驱动框架---input子系统框架
前面从具体(Linux 驱动框架---input子系统)的工作过程学习了Linux的input子系统相关的架构知识,但是前面的学习比较实际缺少总结,所以今天就来总结一下输入子系统的架构分层,站到远处来 ...
- Linux驱动之GPIO子系统和pinctrl子系统
前期知识 1.如何编写一个简单的Linux驱动(一)--驱动的基本框架 2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations 3.如何编写一个简单的Lin ...
- Linux内核驱动之GPIO子系统(一)GPIO的使用
转自:http://blog.csdn.net/mirkerson/article/details/8464290 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt , ...
- Linux内核驱动之GPIO子系统(一)GPIO的使用【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/9427047 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt ...
- linux驱动模型<输入子系统>
在linux中提供一种输入子系统的驱动模型,其主要是实现在input.c中. 在输入子系统这套模型中,他把驱动分层分类.首先分为上下两层,上层为input.c .下层为驱动的实现,下层分为两部分,一部 ...
- Linux驱动之输入子系统简析
输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...
- Linux设备驱动之IIO子系统——IIO框架及IIO数据结构
由于需要对ADC进行驱动设计,因此学习了一下Linux驱动的IIO子系统.本文翻译自<Linux Device Drivers Development >--John Madieu,本人水 ...
- Linux驱动之一个简单的输入子系统程序编写
的在Linux驱动之输入子系统简析已经分析过了输入子系统的构成,它是由设备层.核心层.事件层共同组成的.其中核心层提供一些设备层与事件层公用的函数,比如说注册函数.反注册函数.事件到来的处理函数等等: ...
随机推荐
- ts 函数重载
class User { constructor(public readonly name: string, public readonly value: Function) {} } class D ...
- MongoDB的下载、安装与部署
1.什么是MongoDB? 它是介于关系型数据库和非关系型数据库之间的一种NoSQL数据库,用C++编写,是一款集敏捷性.可伸缩性.扩展性于一身的高性能的面向文档的通用数据库. 2.为什么要用Mong ...
- 「NGK每日快讯」2021.1.25日NGK公链第83期官方快讯!
- 「NGK每日快讯」12.3日NGK公链第30期官方快讯!
- vue版本一直是2.9.6版本,卸载也卸载不掉,更新也更新不了
原文链接:https://blog.csdn.net/zlzbt/article/details/110136755 主要是找到本地文件 E:\StudyFile\VueStudy λ where v ...
- DQL:data query language用来查询数据库表中的数据
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 如果没有查询条件,则每次查询所有的行.实际应用中,一般要指定查询的条件.对记录进行过滤. 查询 ...
- DRF 视图家族及路由层补充
目录 视图家族 一.views视图类 1.APIView类 2.GenericAPIView类(generics中) 二.mixins类:视图辅助工具 1.RetrieveModelMixin 2.L ...
- Latency 和 Delay 区别
时延:Latency 指的是一个报文进入一台设备以致这台设备所经历的时间.实际上考验的是报文在这台设备上消耗的时间.时间越短,这台设备的性能越高. 延时:Delay 是指一个操作和另个一个操作之间 ...
- vue封装一个弹框组件
这是一个提示框和对话框,例: 这是一个组件 eject.vue <template> <div class='kz-cont' v-show='showstate'> &l ...
- 用java输出"Hello,World!"
Hello,World! 一般步骤 随便新建一个文件夹,存放代码 新建一个java文件 新建.txt文档 文件后缀名改为.java Hello.java [注意]系统可能没有显示后缀名,我们需要手动打 ...