嵌入式Linux中的 gpio、gpiod基本分析

news2024/12/24 13:07:38

GPIO 应该是每个嵌入式设备都避免不了的。最近在做项目的时候,也遇到这方面的问题,所以简单总结一下。

现在内核里面多了 gpiod 的来控制 gpio 口,相对于原来的形式,使用 gpiod 的好处是我们申请后不进行 free 也没有什么问题。但是你要是使用原来的方式后,一定要记得释放。不释放的话可能会有问题。

#旧的 GPIO 使用实例

DTS 文件

 

det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;

驱动文件调用

    gc5025->det_pin = of_get_named_gpio_flags(node, "det-gpios", 0, &det_flags);
    camera_det_irq = gpio_to_irq(gc5025->det_pin);
    gc5025->det_value = gpio_get_value(gc5025->det_pin);
    /*判断注册终端*/
    if(camera_det_irq){
        if (gpio_request(gc5025->det_pin, "camera-irq-gpio")) {
            printk("gpio %d request failed!\n", gc5025->det_pin);
            gpio_free(gc5025->det_pin);
            return IRQ_NONE;
        }
        ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH, "det-gpio", NULL);
        if (ret != 0) {
            free_irq(camera_det_irq, NULL);
            dev_err(dev, "Failed to request IRQ: %d\n", ret);
            return ret;
        }
    }

# 新的 GPIOD 文档

Linux 内核文档

https://www.kernel.org/doc/Documentation/gpio/consumer.txt

#头文件

我们需要包含头文件

#include <linux/gpio/consumer.h>

看头文件里面包含的函数列表

desc_to_gpio
devm_get_gpiod_from_chi
devm_gpiod_get
devm_gpiod_get_array
devm_gpiod_get_array_op
devm_gpiod_get_index
devm_gpiod_get_index_op
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_
gpiod_export
gpiod_export_link
gpiod_get
gpiod_get_array
gpiod_get_array_optiona
gpiod_get_direction
gpiod_get_index
gpiod_get_index_optiona
gpiod_get_optional
gpiod_get_raw_value
gpiod_get_raw_value_can
gpiod_get_value
gpiod_get_value_canslee
gpiod_is_active_low
gpiod_put
gpiod_put_array
gpiod_set_array_value
gpiod_set_array_value_c
gpiod_set_debounce
gpiod_set_raw_array_val
gpiod_set_raw_array_val
gpiod_set_raw_value
gpiod_set_raw_value_can
gpiod_set_value
gpiod_set_value_canslee
gpiod_to_irq
gpiod_unexport

#获取 gpio 描述符和释放

使用一下两个函数获取 GPIO 设备,多个设备时需要附带 index 参数。函数返回一个 GPIO 描述符,或一个错误编码,可以使用 IS_ERR() 进行检查:

struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
                    enum gpiod_flags flags)

struct gpio_desc *gpiod_get_index(struct device *dev,
                      const char *con_id, unsigned int idx,
                      enum gpiod_flags flags)

或者也可以使用如下两个函数获取可用设备:

struct gpio_desc *gpiod_get_optional(struct device *dev,
                         const char *con_id,
                         enum gpiod_flags flags)

struct gpio_desc *gpiod_get_index_optional(struct device *dev,
                           const char *con_id,
                           unsigned int index,
                           enum gpiod_flags flags)

使用如下函数同时获取多个设备:

struct gpio_descs *gpiod_get_array(struct device *dev,
                       const char *con_id,
                       enum gpiod_flags flags)

该函数返回一个GPIO描述结构体:

struct gpio_descs {
    unsigned int ndescs;
    struct gpio_desc *desc[];
}

一个GPIO描述符可以使用如下函数释放:

void gpiod_put(struct gpio_desc *desc)
void gpiod_put_array(struct gpio_descs *descs)

需要注意GPIO描述符被释放后不可再使用,而且不允许使用第一个函数来释放通过序列获取得到GPIO描述符。

#举个例子

#dts 文件

gc5025: gc5025@37 {
		status = "okay";
		compatible = "galaxycore,gc5025";
		reg = <0x37>;
		clock-frequency = <400000>;

		pinctrl-names = "default";
		pinctrl-0 = <&cif_clkout_m0>;

		clocks = <&cru SCLK_CIF_OUT>;
		clock-names = "xvclk";

		avdd-supply = <&vcc2v8_dvp>;
		dovdd-supply = <&vcc1v8_dvp>;
		dvdd-supply = <&vdd1v2_dvp>;

		reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
		pwdn-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
		det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;

		rockchip,camera-module-index = <0>;
		rockchip,camera-module-facing = "front";
		rockchip,camera-module-name = "CMK-CW4191-FG1";
		rockchip,camera-module-lens-name = "CK5502";

		port {
			ucam_out: endpoint {
				remote-endpoint = <&mipi_in_ucam>;
				data-lanes = <1 2>;
			};
		};
	};

驱动文件调用:

    gc5025->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
    if (IS_ERR(gc5025->reset_gpio))
        dev_warn(dev, "Failed to get reset-gpios\n");
    gc5025->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
    if (IS_ERR(gc5025->pwdn_gpio))
        dev_warn(dev, "Failed to get pwdn-gpios\n");
    /*新的GPIO子系统方式,这种方式不需要手动释放资源*/
    gc5025->det_gpio = devm_gpiod_get(dev, "det", GPIOD_OUT_LOW);
    if (IS_ERR(gc5025->det_gpio))

#GPIO 使用

#设置 GPIO 口方向

int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)

#检查 GPIO 口是方向

int gpiod_get_direction(const struct gpio_desc *desc)

函数返回 GPIOF_DIR_IN 或者 GPIOF_DIR_OUT

#读取 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)

#active-low和raw-value

active-low & raw value 有些设备采用低电平有效的方式输出逻辑信号。此时低电平输出 1,高电平输出 0。此时可以通过访问 raw_value 的方式来访问实际电路上的值,与逻辑处理无关:假设我们在 DTS 里面这样设置

reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;

然后我们这样调用

gpiod_set_value_cansleep(gc5025->reset_gpio, 1);

因为 DTS 里面的 active 状态是 GPIO_ACTIVE_LOW,所以这个代码输出的是 低电平

gpiod_set_value_cansleep(gc5025->reset_gpio, 0);

输出的是高电平

这几个函数如下:

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)

raw-value 的意思就是不在乎 DTS 里面的 ACTIVE,我 set 高电平,就是高电平。逻辑关系汇总如下:

Function (example) active-low 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

可以使用如下函数判断一个设备是否是低电平有效的设备。

int gpiod_is_active_low(const struct gpio_desc *desc)

#设置多个输出

这个没使用过 使用如下函数设置一组设备的输出值

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)

#兼容旧版本

旧的 GPIO 系统使用基于标号的结构而不是基于描述符。可以使用如下两个函数进行相互转换:

int desc_to_gpio(const struct gpio_desc *desc)
struct gpio_desc *gpio_to_desc(unsigned gpio)

注意不能使用一套 API 的方法释放另一套 API 获取的设备

#和中断IRQ相关

使用如下函数获取一个 GPIO 设备对应的 IRQ 中断号

int gpiod_to_irq(const struct gpio_desc *desc)

返回值时一个 IRQ number,或者一个负数的错误代码。得到的中断号可以传递给函数 request_irq(),free_irq().

#举例子

    /*新的GPIO子系统方式,这种方式不需要手动释放资源*/
	gc5025->det_gpio = devm_gpiod_get(dev, "det", GPIOD_OUT_LOW);
	if (IS_ERR(gc5025->det_gpio))
		dev_warn(dev, "Failed to get det-gpios\n");
	camera_det_irq = gpiod_to_irq(gc5025->det_gpio);
	/*新gpio子系统转成旧gpio子系统*/
	gc5025->det_pin = desc_to_gpio(gc5025->det_gpio);
	/*读取上电gpio电平*/
	gc5025->det_value = gpio_get_value(gc5025->det_pin);

	/*判断注册终端*/
	if(camera_det_irq){
        ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH, "det-gpio", NULL);
        if (ret != 0) {
			free_irq(camera_det_irq, NULL);
            dev_err(dev, "Failed to request IRQ: %d\n", ret);
			return ret;
		}
	}

#调试

移植驱动阶段或者调试阶段的工程中,难免想知道当前 gpio 的电平状态。当然很 easy。万用表戳上去不就行了。是啊!硬件工程师的思维。作为软件工程师自然是要软件的方法。下面介绍两个 api 接口。自己摸索使用吧。点到为止。

static inline int gpio_export(unsigned gpio, bool direction_may_change);
static inline int gpio_export_link(struct device *dev, const char *name, unsigned gpio);

在你的 driver 中调用以上 api 后,编译下载。去 /sys/class/gpio 目录看看有什么发现。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/584218.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Seata分布式事务AT、TCC、SAGA、XA模式

Seata是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式&#xff0c;为用户打造一站式的分布式解决方案。 AT模式 &#x1f36e;实现原理 阿里SEATA独有模式&#xff0c;通过生成反向SQ…

STM32读取24位模数转换(24bit ADC)芯片ADS1231数据

STM32读取24位模数转换&#xff08;24bit ADC&#xff09;芯片ADS1231数据 ADS1231是一款TI公司出品的24位ADC芯片&#xff0c;常用于与称重传感器配合实现体重计的应用。这里介绍STM32读取ADS1231的电路和代码实现。ADS1231的特点为通过硬件管脚可控制两种采样速率&#xff0…

日期与字符串相互转化(自定义日期工具类总结,Hutool工具类使用)

一、将日期格式化成字符串输出&#xff08;Date转String&#xff09; Java旧的时间API饱受诟病&#xff0c;Java8以后提供了新的时间API&#xff0c;在java.time包下。 //获取当前时间LocalDateTime dateLocalDateTime.now();//创建日期时间对象格式化器&#xff0c;日期格式类…

Hadoop学习---11、HA高可用

1、Hadoop HA高可用 1.1 HA概述 1、所谓HA&#xff08;High Availablity&#xff09;&#xff0c;即高可用&#xff08;7 * 24小时不中断服务&#xff09;。 2、实现高可用最关键的策略是消除单点故障。HA严格来说应该分成各个组件的HA机制&#xff1a;HDFS的HA和YARN的HA。 …

数字化转型,低代码开发真的靠谱?

关于数字化转型&#xff0c;有这么一种定义&#xff0c;即利用数字化技术和能力来驱动企业商业模式创新和商业生态系统重构的一种途径与方法。通俗点说&#xff0c;数字化的核心和目的就是通过数字化技术&#xff0c;实现业务的转型、创新和增长&#xff0c;提供新的营收点与价…

超详细,性能测试三大模式详解,资深测试老鸟一文道破...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在性能测试工作中…

IC芯片自动测试系统平台ATECLOUD:提高半导体制造效率与质量

近年来&#xff0c;随着半导体产业的快速发展&#xff0c;IC芯片在各种电子设备中扮演着越来越重要的角色。然而&#xff0c;为了满足不断增长的需求和保持竞争力&#xff0c;制造商需要不断提高生产效率并确保产品质量。ATECLOUD——一款基于云计算技术的IC芯片自动测试系统平…

租赁小程序|人车网租赁系统|电动车租赁系统包含哪些功能?

随着科技不断完善&#xff0c;租赁行业的发展也越来越好&#xff0c;电动车也开始进行线上推广租赁&#xff0c;不仅可以让人们更加方便的租用电动车&#xff0c;还让商家可以通过线上模式带动线下的经营&#xff0c;不同于传统的汽车租赁&#xff0c;用户只要打开手机就可以进…

MySQL-5.7.19版本安装详解

MySQL-5.7.19版本安装详解 1. 软件下载 Mysql5.7地址&#xff1a;https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.19-winx64.zip 2. 软件安装 2.1 第一步&#xff1a;解压 下载后会得到mysql-5.7.19-winx64的zip 安装文件&#xff0c;先将其解压解压的路径最好不…

微信小程序导入Vant Weapp ui组件库2023年最新版

微信小程序导入Vant Weapp ui组件库2023年最新版 写这篇文章的原因是Vant Weapp的官方文档快速上手是有一定的问题的&#xff0c;没错&#xff0c;我就是那个试错的人&#xff0c;弄了一下午才发现问题所在&#xff0c;所以写了一篇正确的导入教程。 第一步&#xff1a;在项目…

Python进阶 │反爬虫和怎样反反爬虫

爬虫、反爬虫和反反爬虫是网络爬虫工作过程中一直伴随的问题。 在现实生活中&#xff0c;网络爬虫的程序并不像之前介绍的爬取博客那么简单&#xff0c;运行效果不如意者十有八九。首先需要理解一下“反爬虫”这个概念&#xff0c;其实就是“反对爬虫”。根据网络上的定义&…

PowerShell:因为在此系统上禁止运行脚本,解决方法

运行powershell脚本遇见报错&#xff1a; 无法加载文件 C:\Users\DH\Desktop\cs\rename.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 查了查之后发现是在计算…

差动测试棒如何连接到示波器

差动测试棒提供一个安全的绝缘仪器给示波器使用&#xff0c;它可以转换由高输入的差动电压进入一个低电压并且显示波形在示波器上&#xff0c;使用频率高达100MHz&#xff0c;非常适合大电力测试、研发使用。 差动测试棒输出标是设计在操作示波器1MΩ的输入阻抗的相对衰减量&a…

人工智能学习07--pytorch20--目标检测:COCO数据集介绍+pycocotools简单使用

如&#xff1a;天空 coco包含pascal voc 的所有类别&#xff0c;并且对每个类别的标注目标个数也比pascal voc的多。 一般使用coco数据集预训练好的权重来迁移学习。 如果仅仅针对目标检测object80类而言&#xff0c;有些图片并没有标注信息&#xff0c;或者有错误标注信息。…

需求:js给html添加css

目前发送请求&#xff0c;请求回html&#xff1a;<div class"articleRecommendation_con"> <a href"https://weekly.caixin.com/2023-05-26/102059365.html"><img src"https://img.caixin.com/2023-05-27/168515251870385…

如何选择适合平台的直播美颜SDK:从技术和商业角度考虑

直播美颜SDK作为一种技术解决方案&#xff0c;可以帮助直播应用实现优秀的美颜效果&#xff0c;提高用户体验和粘性。然而&#xff0c;如何选择适合你应用的直播美颜SDK&#xff0c;需要从技术和商业角度综合考虑。本文将从这两个角度出发&#xff0c;为你介绍如何选择适合你应…

手把手带你读vue2文档(基础篇总结)

目录 声明式渲染 v-bind v-if条件与循环 name命名 学会log打印 一些js方法 双向绑定v-model和v-bind 那么请实现一个复选框吧 自定义标签模板 vue2实例 数据与方法 你提到vue有两种数据和方法&#xff0c;js是不是只有一种 vue2自带的实例和方法 vue2$会和jQuery冲…

四站精彩回顾 | Fortinet Accelerate 2023·中国区巡展火热进行中

Fortinet Accelerate 2023中国区巡展 上周&#xff0c;Fortinet Accelerate 2023中国区巡展分别走过青岛、南京、长沙、合肥四站&#xff0c;Fortinet携手太平洋电信、亚马逊云科技、中企通信等云、网、安合作伙伴&#xff0c;与各行业典型代表客户&#xff0c;就网安融合、网…

spring事务管理详解和实例(事务传播机制、事务隔离级别)

目录 1 理解spring事务 2 核心接口 2.1 事务管理器 2.1.1 JDBC事务 2.1.2 Hibernate事务 2.1.3 Java持久化API事务&#xff08;JPA&#xff09; 2.2 基本事务属性的定义 2.2.1 传播行为 2.2.2 隔离级别 2.2.3 只读 2.2.4 事务超时 2.2.5 回滚规则 2.3 事务状态 3…

盘点索引常见的11个知识点

今天来盘点一下关于MySQL索引常见的知识点 本来这篇文章我前两个星期就打算写了&#xff0c;提纲都列好了&#xff0c;但是后面我去追《漫长的季节》这部剧去了&#xff0c;这就花了一个周末的时间&#xff0c;再加上后面一些其它的事&#xff0c;导致没来得及写 不过不要紧&…