Linux 内核 GPIO 用户空间接口

文章目录

  • Linux 内核 GPIO 接口
    • 旧版本方式:sysfs 接口
    • 新版本方式:chardev 接口
  • gpiod 库及其命令行
    • gpiod 库的命令行
    • gpiod 库函数的应用

GPIO(General Purpose Input/Output,通用输入/输出接口),是微控制器或微处理器上的引脚,可以被编程为输入或输出,用于与外部设备进行通信。在 Linux 系统中,通过内核提供的用户空间接口,开发者能够轻松地读取、设置 GPIO 的状态,实现对外部设备的控制和监测。

本文将基于 Orangepi ZERO 2开发板,探讨 Linux 内核(kernel 4.8 版本起)基于字符设备的新接口,用于访问和管理用户空间中的 GPIO 线路。
在这里插入图片描述

Linux 内核 GPIO 接口

在 Linux 系统内部,Linux 内核通过生产者/消费者模型实现对 GPIO 的访问。 有生产 GPIO 线路的驱动程序(GPIO控制器驱动程序)和消耗 GPIO 线路的驱动程序(键盘、触摸屏、传感器等)。

[!IMPORTANT]

生产 GPIO 线路的驱动程序相关链接:GPIO Driver Interface — The Linux Kernel documentation

消耗 GPIO 线路的驱动程序相关链接:GPIO Descriptor Consumer Interface — The Linux Kernel documentation

为了管理 GPIO 注册和分配,Linux 内核中有一个名为 gpiolib 的框架。 该框架为在内核空间和用户空间应用程序中运行的设备驱动程序提供了一个 API。

在这里插入图片描述

旧版本方式:sysfs 接口

在 Linux kernel 4.7 版本之前,管理用户空间中 GPIO 线路的接口一直通过在 /sys/class/gpio 导出的文件在 sysfs 中。 因此,如果想要将某个 GPIO 设置成输出,且让这个 GPIO 输入高电平,整体步骤如下:

  1. 确定GPIO线路的编号;
  2. 将 GPIO 编号写入到 /sys/class/gpio/export
  3. 将 GPIO 线路配置为输出,对应设置的文件为 /sys/class/gpio/gpioX/direction
  4. 将 1 写入到 /sys/class/gpio/gpioX/value,使 GPIO 输出高电平。

以 Orangepi ZERO 2 为例子,要从用户空间设置 GPIO 69 输出高电平,要执行以下命令(root 用户下执行):

echo 69 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio69/direction
echo 1 > /sys/class/gpio/gpio69/value

执行前后效果如下图所示(gpio readall 命令用于查看物理引脚的状态):

在这里插入图片描述

从命令的运行界面来看,效果很好,执行也简单,但还是有一些缺陷:

  1. GPIO 的分配不与任何进程绑定,如果使用 GPIO 的进程结束执行或崩溃,GPIO 可能会继续保持当前状态;
  2. 如果多个进程访问同一 GPIO 线路,并发可能是一个问题;
  3. 写入多个引脚需要对大量文件(export / direction / value等)进行 openreadwriteclose 等操作;
  4. 捕获事件(GPIO 线路的中断)的轮询过程不可靠;
  5. 没有接口来配置 GPIO 线路(开源、开漏等);
  6. 分配给 GPIO 线路的编号不确定。

新版本方式:chardev 接口

从 Linux kernel 4.8 版本开始,GPIO sysfs 接口被弃用,现在有一个基于字符设备的新 API,可以从用户空间访问 GPIO 线路。

每个 GPIO 控制器(gpiochip)在 /dev 中都会有一个字符设备,可以使用文件操作(open、read、write、ioctl、poll、close)来管理 GPIO 行并与之交互,输入 ls /dev/gpiochip*,可以看到 GPIO 的所有字符设备:

在这里插入图片描述

尽管这个新的字符设备接口可以防止使用 echocat 等标准命令行操作 GPIO,但与 sysfs 接口相比,它有一些优点:

  1. GPIO 的分配与正在使用它的进程相关联,从而改进了对用户空间进程使用哪些 GPIO 线路的控制;
  2. 以一次读取或写入多条 GPIO 线路;
  3. 可以按名称找到 GPIO 控制器和 GPIO 线路;
  4. 可以配置引脚的状态(开源、开漏等);
  5. 捕获事件(来自 GPIO 线路的中断)的轮询过程是可靠的。

gpiod 库及其命令行

使用 chardev 接口,可以安装 libgpiod 项目来使用,这是个与 Linux GPIO 字符设备交互的 C 库和工具。

还是以 Orangepi ZERO 2 开发板为例,该板运行的操作系统为 Ubuntu22.04,kernel 版本为 5.16。安装 libgpiod 库和工具的命令如下(如不是在 root 用户下操作,需要命令前加上 sudo 扩充命令的权限):

apt update
apt install libgpiod-dev
apt install gpiod

gpiod 库的命令行

安装好库和工具后,可以使用一些命令来确认是否安装成功。例如,检查可用的 GPIO 芯片:

gpiodetect

Orangepi ZERO 2 开发板的 GPIO 芯片如下图所示,有两个 gpiochip,GPIO 线路分别是 288 和 32。

在这里插入图片描述

[300b000.pinctrl][7022000.pinctrl] 是GPIO芯片的名称或标签,通常与芯片的硬件地址或设备树节点名称相关联。这些名称由内核设备驱动程序指定,用于标识和区分不同的GPIO芯片。

查看 GPIO 芯片的信息命令(以 gpiochip0 为例):

gpioinfo /dev/gpiochip0

这个命令显示的信息很多,line 是GPIO 线的编号(从 0 到 287),后面是 GPIO 线的名称,未命名的线显示为 unnamedunused表示该 GPIO 线目前未被使用,input 表示该 GPIO 线配置为输入模式,output 表示该 GPIO 线配置为输出模式,active-high 表示该 GPIO 线的活动电平为高电平。

在这里插入图片描述

在上图可以看出,有几个 GPIO 引脚被使用,被使用的 GPIO 会被命名为其他的名字,后面还会出 [used] 的字样。

用此前 wiringPi 库的命令 gpio readall 查看当前物理引脚的状态。其中物理引脚 12 号对应的 GPIO 线路号为 75,当前值为 0。

在这里插入图片描述

[!NOTE]

wiringPi 库是开发 Orangepi ZERO 2 开发板应用层程序的中间件,安装教程在我之前的博客《OrangePi ZERO 2 外设应用程序开发之接口与 wiringOP 库》提及。

gpioset 命令可以设置 GPIO 线路,假设现在要改变 75 号 GPIO 的输出状态(输出为 1),具体命令如下:

gpioset 0 75=1

命令中的 0 表示 gpiochip075 为 GPIO 线路号,1 为写入值。

执行后,GPIO 75 的输出值立刻变为 1,相较于 sysfs 接口的调用,方便了非常多。

在这里插入图片描述

gpioget 命令将读取 GPIO 线路的值。例如,读取 GPIO 65 的值,命令如下:

gpioget 0 65

执行结果如下图,表示 GPIO 65 号引脚当前值为 0。

在这里插入图片描述

所有这些命令的源代码都可以在 libgpiod 仓库中找到。

gpiod 库函数的应用

gpiod 库提供了很多 API,可以直接对 GPIO 线路进行操作:

struct gpiod_chip *gpiod_chip_open(const char *path);
struct gpiod_line *gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset);
int gpiod_line_request_input(struct gpiod_line *line, const char *consumer);
int gpiod_line_get_value(struct gpiod_line *line);

下面是 libgpiod 提供的几个函数的解释:

  1. gpiod_chip_open

    • 功能: 打开一个 GPIO 芯片。
    • 参数:
      • const char *path - 设备文件的路径,例如 /dev/gpiochip0
    • 返回值:
      • 成功时返回一个指向 gpiod_chip 结构体的指针。
      • 失败时返回 NULL
    • 用途: 这是使用 GPIO 芯片的第一步,通过指定的路径打开一个 GPIO 芯片。
    struct gpiod_chip *chip;
    chip = gpiod_chip_open("/dev/gpiochip0");
    if (!chip) {
        perror("gpiod_chip_open");
        exit(1);
    }
    
  2. gpiod_chip_get_line

    • 功能: 获取 GPIO 芯片上的一条 GPIO 线。
    • 参数:
      • struct gpiod_chip *chip - 一个指向打开的 GPIO 芯片的指针。
      • unsigned int offset - 要获取的 GPIO 线的编号(从 0 开始)。
    • 返回值:
      • 成功时返回一个指向 gpiod_line 结构体的指针。
      • 失败时返回 NULL
    • 用途: 获取特定编号的 GPIO 线,以便对其进行操作。
    struct gpiod_line *line;
    line = gpiod_chip_get_line(chip, 4); // 获取第 4 条 GPIO 线
    if (!line) {
        perror("gpiod_chip_get_line");
        gpiod_chip_close(chip);
        exit(1);
    }
    
  3. gpiod_line_request_input

    • 功能: 请求将 GPIO 线配置为输入模式。
    • 参数:
      • struct gpiod_line *line - 一个指向要配置的 GPIO 线的指针。
      • const char *consumer - 请求者的名称(通常是应用程序的名称,用于调试和日志记录)。
    • 返回值:
      • 成功时返回 0。
      • 失败时返回 -1。
    • 用途: 将指定的 GPIO 线配置为输入模式,以便读取其电平值。
    int ret;
    ret = gpiod_line_request_input(line, "my_consumer");
    if (ret) {
        perror("gpiod_line_request_input");
        gpiod_line_release(line);
        gpiod_chip_close(chip);
        exit(1);
    }
    
  4. gpiod_line_get_value

    • 功能: 获取 GPIO 线的电平值。
    • 参数:
      • struct gpiod_line *line - 一个指向配置为输入模式的 GPIO 线的指针。
    • 返回值:
      • 成功时返回 0 或 1,分别表示低电平和高电平。
      • 失败时返回 -1。
    • 用途: 读取 GPIO 线的当前电平值。
    int value;
    value = gpiod_line_get_value(line);
    if (value < 0) {
        perror("gpiod_line_get_value");
    } else {
        printf("GPIO line value: %d\n", value);
    }
    

总结来说,这几个函数的主要作用是打开 GPIO 芯片、获取 GPIO 线路、将 GPIO 线路配置为输入模式并读取其电平值。通过这些步骤,可以实现对 GPIO 线状态的监测和响应。

以下 C 程序使用 libgpiod 读取 GPIO 70 的例子:

// gpio_line.c

#include <stdio.h>
#include <string.h>
#include <gpiod.h>
#include <errno.h>

int main()
{
    struct gpiod_chip *chip;
    struct gpiod_line *line;
    int req, value;

    chip = gpiod_chip_open("/dev/gpiochip0");
    if (!chip) {
        perror("Failed to open GPIO chip");
        return -1;
    }

    line = gpiod_chip_get_line(chip, 70);
    if (!line) {
        gpiod_chip_close(chip);
        perror("Failed to get line");
        return -1;
    }

    req = gpiod_line_request_input(line, "gpio_state");
    if (req) {
        gpiod_chip_close(chip);
        fprintf(stderr, "Failed to request line as input: %s\n", strerror(errno));
        return -1;
    }

    value = gpiod_line_get_value(line);
    if (value < 0) {
        gpiod_chip_close(chip);
        fprintf(stderr, "Failed to get line value: %s\n", strerror(errno));
        return -1;
    }

    printf("GPIO value is: %d\n", value);

    gpiod_chip_close(chip);
    return 0;
}

编译和运行命令如下:

gcc -o gpio_line gpio_line.c -lgpiod
./gpio_line

运行结果如下:

在这里插入图片描述

新的 Linux 内核 GPIO 用户空间接口是一个非常简单、优雅和健壮的 API,从现在开始应该用在嵌入式Linux开发上吧!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 端口

什么是虚拟端口 计算机程序之间的通讯&#xff0c;通过IP只能锁定计算机&#xff0c;但是无法锁定具体的程序。通过端口可以锁定计算机上具体的程序&#xff0c;确保程序之间进行沟通。 IP地址相当于小区地址&#xff0c;在小区内可以有许多用户&#xff08;程序&#xff09;&…

AI绘画Stable Diffusion 双重曝光-神秘意境和难以言喻的视觉体验,SD提示词轻松搞定

大家好&#xff0c;我是画画的小强 今天给大家介绍AIGC绘图提示语使用常见摄影手法&#xff1a;双重曝光。双重曝光摄影效果是一种摄影爱好所热衷的常见摄影手法之一。通过双重曝光摄影手法&#xff0c;能够为图同摄影图像引入神秘的意境感和一种难以言喻的视觉体验&#xff0…

android应用的持续构建CI(二)-- jenkins集成

一、背景 接着上一篇文章&#xff0c;本文我们将使用jenkins把所有的流程串起来。 略去了对android应用的加固流程&#xff0c;重点是jenkins的job该如何配置。 二、配置jenkins job 0、新建job 选择一个自由风格的软件项目 1、参数赋值 你可以增加许多参数&#xff0c;这…

Windows环境使用SpringBoot整合Minio平替OSS

目录 配置Minio环境 一、下载minio.exe mc.exe 二、设置用户名和密码 用管理员模式打开cmd 三、启动Minio服务器 四、访问WebUI给的地址 SpringBoot整合Minio 一、配置依赖&#xff0c;application.yml 二、代码部分 FileVO MinioConfig MinioUploadService MinioController 三…

c++习题05-斐波那契数列

目录 一&#xff0c;问题 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;问题 二&#xff0c;思路 根据题目&#xff0c;可以自己列出斐波那契数列&#xff08;前四个&#xff09;如下&#xff1a; 通过列出来的值&#xff0c;可以发现&#xff0c;前两个都是1&…

如何优化圆柱晶振32.768KHz的外壳接地?

圆柱晶振32.768KHz在电子设备中扮演着重要的角色&#xff0c;其精确的时钟信号对于许多应用至关重要。为了确保晶振的稳定性和准确性&#xff0c;外壳接地是一个关键步骤。 一、外壳接地的目的 外壳接地的主要目的是为了防止信号干扰。当晶振的外壳接地后&#xff0c;它相当于…

联合概率密度函数

目录 1. 什么是概率密度由联合概率密度求概率参考链接 1. 什么是概率密度 概率密度到底在表达什么&#xff1f; 外卖在20-40分钟内送达的概率 随机变量落在[20,40]之间的概率。下图中&#xff0c;对总面积做规范化处理&#xff0c;令总面积1&#xff0c; f ( x ) f(x) f(x)则成…

创建本地仓库

一、新建挂载目录 二、将挂载本地镜像挂载到目录 三、配置yum仓库 一、新建挂载目录 mkdir /BenDiCangKu 二、将挂载本地镜像挂载到目录 1、先连接本地光盘 2、挂载光盘 mount /dev/sr0 /BenDiCangKu 3、查看挂载 由此可见挂载成功 三、配置yum仓库 1、新建yum仓库文件…

Zabbix6.4安装教程

目录 前言 主要功能 常见应用场景 一. 环境准备 二. 部署安装 三. Web页面安装 前言 Zabbix是一个开源的企业级监控解决方案&#xff0c;用于监控各种网络设备、服务器、应用程序和云资源。它能够提供全面的监控、告警和数据分析功能&#xff0c;帮助管理员及时发现和解决…

测试几个 ocr 对日语的识别情况

测试几个 ocr 对日语的识别情况 1. EasyOCR2. PaddleOCR3. Deepdoc&#xff08;识别pdf中图片&#xff09;4. Deepdoc&#xff08;识别pdf中文字&#xff09;5. Nvidia neva-22b6. Claude 3.5 sonnet 识别图片中的文字7. Claude 3.5 sonnet 识别 pdf 中表格8. OpenAI gpt-4o 识…

【面向就业的Linux基础】从入门到熟练,探索Linux的秘密(十)-git(2)

下面是一些git的常用命令和基本操作&#xff0c;可以当做平常的笔记查询&#xff0c;用于学习&#xff01;&#xff01;&#xff01; 文章目录 前言 一、git 二、git常用命令 总结 前言 下面是一些git的常用命令和基本操作&#xff0c;可以当做平常的笔记查询&#xff0c;用于…

UE4_材质基础_切线空间与法线贴图

学习笔记&#xff0c;不喜勿喷&#xff0c;侵权立删&#xff0c;祝愿大家生活越来越好&#xff01; 一、切线空间 在《OpenGL基础11&#xff1a;空间》中提到了观察空间、裁剪空间、世界空间等。切线空间和它们一样&#xff0c;都属于坐标空间 上面就是一个…

基于jeecgboot-vue3的Flowable流程-集成仿钉钉流程(二)增加基本的发起人审批与多用户多实例

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、AssigneeNode 增加approvalText public abstract class AssigneeNode extends Node {// 审批对象private AssigneeTypeEnum assigneeType;// 表单内人员private String formUser;// 表…

【智能制造-10】样条曲线、贝塞尔曲线、B样条

什么是样条&#xff1f; 样条是通过一组指定点集而生成平滑曲线的柔性带。 什么是B样条&#xff1f; B样条就相当于一个函数&#xff0c;这个函数在系数不同时就可以变化成各种曲线的形状。 B样条的优势&#xff1f; 保留了Bezier曲线的优势可局部修改&#xff0c;调整某一…

专题三:Spring源码中新建module

前面我们构建好了Spring源码&#xff0c;接下来肯定迫不及待来调试啦&#xff0c;来一起看看大名鼎鼎ApplicationContext 新建模块 1、基础步骤 1.1 自定义模块名称如&#xff1a;spring-self 1.2 选择构建工具因为spring使用的是gradle&#xff0c;所以这边需要我们切换默认…

21.【C语言】顺序结构和选择结构之if

顺序结构 从上至下&#xff0c;按顺序执行代码 选择结构 1.if语句 01零分支 if (条件)表达式; 02双分支 详见第10篇 if (条件) { ... } else { ... }03多分支 使用if嵌套 if (条件1)表达式1 else if (条件2)表达式2 else if (条件3)表达式3 else if (条件4)表达式4 .…

Patch SCN使用说明---惜分飞

软件说明 该软件是惜分飞&#xff08;https://www.xifenfei.com&#xff09;开发&#xff0c;仅用来查看和修改Oracle数据库SCN(System Change Number),主要使用在数据库因为某种原因导致无法正常启动的情况下使用该工具进行解决.特别是Oracle新版本中使用隐含参数,event,orad…

jmeter-beanshell学习2-beanshell断言

继续写&#xff0c;之前写了获取变量&#xff0c;设置变量&#xff0c;今天先写个简单点的断言。 一般情况用响应断言&#xff0c;就挺好使&#xff0c;但是自动化还要生成报告&#xff0c;如果断言失败了&#xff0c;要保存结果&#xff0c;只能用beanshell处理&#xff0c;顺…

mysql在windows下的安装

一&#xff0c;软件安装 只修改开头的系统盘 二&#xff0c;环境变量配置 找到MySQL安装目录对应的bin目录复制路径粘贴过来 三&#xff0c;cmd

通过端口和进程pid查找启动文件/脚本

今天审计一个程序又让GPT给我上了一课&#xff0c;记一下笔记&#xff1a; 1、首先该程序开启了8080端口&#xff0c;使用如下命令得到pid为1817 netstat -tunlp|grep 80802、使用pid得到父进程 pstree -ps 1817输出结果如下&#xff1a; 3、看出程序是由systemd启动的&…