Linux 内核 GPIO 用户空间接口

news2024/11/26 11:59:38

文章目录

  • 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.coloradmin.cn/o/1897755.html

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

相关文章

防静电监控系统在电子制造业智能化转型中的应用价值

在电子制造业迅速向智能化转型的当下&#xff0c;防静电监控系统正发挥着日益重要的作用&#xff0c;其应用价值体现在多个关键方面。 一、ESD防静电监控系统简介 ESD防静电监控系统是对企业防静电设备&#xff08;机器、台垫、离子风机&#xff09;和人员进行实时监控、数据存…

c++之旅第十一弹——顺序表

大家好啊&#xff0c;这里是c之旅第十一弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一,数据结构…

Linux系统(CentOS)安装Mysql5.7.x

安装准备&#xff1a; Linux系统(CentOS)添加防火墙、iptables的安装和配置 请访问地址&#xff1a;https://blog.csdn.net/esqabc/article/details/140209894 1&#xff0c;下载mysql安装文件&#xff08;mysql-5.7.44为例&#xff09; 选择Linux通用版本64位&#xff08;L…

2024年保安员职业资格考试题库大数据揭秘,冲刺高分!

186.安全技术防范是一种由探测、&#xff08;&#xff09;、快速反应相结合的安全防范体系。 A.保安 B.出警 C.延迟 D.监控 答案&#xff1a;C 187.安全技术防范是以&#xff08;&#xff09;和预防犯罪为目的的一项社会公共安全业务。 A.预防灾害 B.预防损失 C.预防失…

昇思25天学习打卡营第5天 | 神经网络构建

1. 神经网络构建 神经网络模型是由神经网络层和Tensor操作构成的&#xff0c;mindspore.nn提供了常见神经网络层的实现&#xff0c;在MindSpore中&#xff0c;Cell类是构建所有网络的基类&#xff0c;也是网络的基本单元。一个神经网络模型表示为一个Cell&#xff0c;它由不同…

MobaXterm不显示隐藏文件

MobaXterm在左边显示隐藏文件&#xff0c;以.开头的文件&#xff0c;想让它不显示&#xff0c;点击红框按钮就可以了

计算机视觉——opencv快速入门(二) 图像的基本操作

前言 上一篇文章中我们介绍了如何配置opencv&#xff0c;而在这篇文章我们主要介绍的是如何使用opencv来是实现一些常见的图像操作。 图像的读取&#xff0c;显示与存储 读取图像文件 在opencv中我们利用imread函数来读取图像文件,函数语法如下&#xff1a; imagecv2.imre…

Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO

官网&#xff1a;https://streamlit.io/ github&#xff1a;https://github.com/streamlit/streamlit API 参考&#xff1a;https://docs.streamlit.io/library/api-reference 最全 Streamlit 教程&#xff1a;https://juejin.cn/column/7265946243196436520 Streamlit-中文文档…

如何在 Ubuntu上搭建 LAMP

远程登录 Ubuntu系统环境 ssh (User)(IP) # 比如&#xff1a;ssh lennlouis192.168.207.128 为安全起见&#xff0c;建议你使用 root 登录 VPS 后创建一个具有 sudo 权限的帐号。 安装和配置 Apache 2 Apache Http Server 是一个开源的&#xff0c;非常流行&#xff0c;使用…

直播预告 | VMware大规模迁移实战,HyperMotion助力业务高效迁移

2006年核高基专项启动&#xff0c;2022年国家79号文件要求2027年央国企100%完成信创改造……国家一系列信创改造政策的推动&#xff0c;让服务器虚拟化软件巨头VMware在中国的市场份额迅速缩水。 加之VMware永久授权的取消和部分软件组件销售策略的变更&#xff0c;导致VMware…

QoS-优先级映射

拓扑图 配置 先完成此配置复杂流分类-CSDN博客 配置qos map-table 接口开启信任DSCP qos map-table dscp-lpinput 32 output 5input 46 output 6 # interface GigabitEthernet0/0/0trust dscp override # AR1上10.1.1.1 ping 3.3.3.3&#xff0c;该流量标记为EF EF映射为p…

vs 远程链接ssh 开发 简单实验

1.概要 动态编译语言&#xff0c;跨平台必须做分别的编译&#xff0c;比如linux和windows。如何再windows环境下开发编译出linux平台的程序呢&#xff0c;vs支持远程链接编辑&#xff0c;就是再vs中写代码&#xff0c;但是编译确是链接远程的环境编译的。 2.环境准备 2.1 vs…

DataWhale-吃瓜教程学习笔记 (七)

学习视频**&#xff1a;第6章-支持向量机_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 第六章 支持向量机 - 算法原理 几何角度 对于线性可分数据集&#xff0c;找距离正负样本距离都最远的超平面&#xff0c;解是唯一的&#xff0c;泛化性能较好 - 超平面 - 几何间隔 例…

如何让自动化测试更加灵活简洁?

简化的架构对于自动化测试和主代码一样重要。冗余和不灵活性可能会导致一些问题&#xff1a;比如 UI 中的任何更改都需要更新多个文件&#xff0c;测试可能在功能上相互重复&#xff0c;并且支持新功能可能会变成一项耗时且有挑战性的工作来适应现有测试。 页面对象模式如何理…

前后端数据交互流程

一、前言 用户在浏览器访问一个网站时&#xff0c;会有前后端数据交互的过程&#xff0c;前后端数据交互也有几种的情况&#xff0c;一下就简单的来说明一下 二、原理 介绍前后端交互前先来了解一下浏览器的功能&#xff0c;浏览器通过渲染引擎和 JavaScript 引擎协同工作&am…

东北财税之星:董女士的家乡创业记

乐财业智慧财税赋能平台&#xff0c;是一个帮助财税机构专业提升、业务增长&#xff0c;让财税生意更好做的综合赋能平台。聚焦财税公司业绩增长&#xff0c;预计2027年帮助2000家财税合伙人利润增长300%&#xff0c;致力打造轻量化、批量化、智能化的”业财税“一体财税服务生…

【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树

目录 1 -> 底层结构 2 -> AVL树 2.1 -> AVL树的概念 2.2 -> AVL树节点的定义 2.3 -> AVL树的插入 2.4 -> AVL树的旋转 2.5 -> AVL树的验证 2.6 -> AVL树的性能 1 -> 底层结构 在上文中对对map/multimap/set/multiset进行了简单的介绍&…

从CPU的视角看C++的构造函数和this指针

从汇编角度&#xff0c;清晰的去看构造函数和this指针到底是个什么东西呢&#xff1f;也许可以解决你的一点小疑问 首先写一个很简单的代码demo&#xff1a; class A{ public:int a;A(){;}void seta(int _a){a_a;}A* getA(){return this;} };int fun1(int px){return px; }in…

烟台LP-SCADA系统如何实现实时监控和过程控制?

关键字:LP-SCADA系统, 传感器可视化, 设备可视化, 独立SPC系统, 智能仪表系统,SPC可视化,独立SPC系统 LP-SCADA&#xff08;监控控制与数据采集&#xff09;系统实现实时监控和过程控制的主要原理和组件如下&#xff1a; 数据采集&#xff1a;LP-SCADA系统通过部署在现场的传…

跨境人最怕的封店要怎么规避?

跨境人最怕的是什么&#xff1f;——封店 造成封店的原因很多&#xff0c;IP关联、无版权售卖、虚假发货等等&#xff0c;其中IP关联这个问题导致店铺被封在跨境商家中简直是屡见不鲜 IP关联&#xff0c;是指被海外平台检测到多家店铺开设在同一个站点上的情况。我们知道有些…