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 输入高电平,整体步骤如下:
- 确定GPIO线路的编号;
- 将 GPIO 编号写入到
/sys/class/gpio/export
; - 将 GPIO 线路配置为输出,对应设置的文件为
/sys/class/gpio/gpioX/direction
; - 将 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
命令用于查看物理引脚的状态):
从命令的运行界面来看,效果很好,执行也简单,但还是有一些缺陷:
- GPIO 的分配不与任何进程绑定,如果使用 GPIO 的进程结束执行或崩溃,GPIO 可能会继续保持当前状态;
- 如果多个进程访问同一 GPIO 线路,并发可能是一个问题;
- 写入多个引脚需要对大量文件(export / direction / value等)进行
open
、read
、write
和close
等操作; - 捕获事件(GPIO 线路的中断)的轮询过程不可靠;
- 没有接口来配置 GPIO 线路(开源、开漏等);
- 分配给 GPIO 线路的编号不确定。
新版本方式:chardev 接口
从 Linux kernel 4.8 版本开始,GPIO sysfs 接口被弃用,现在有一个基于字符设备的新 API,可以从用户空间访问 GPIO 线路。
每个 GPIO 控制器(gpiochip)在 /dev
中都会有一个字符设备,可以使用文件操作(open、read、write、ioctl、poll、close)来管理 GPIO 行并与之交互,输入 ls /dev/gpiochip*
,可以看到 GPIO 的所有字符设备:
尽管这个新的字符设备接口可以防止使用 echo
和 cat
等标准命令行操作 GPIO,但与 sysfs 接口相比,它有一些优点:
- GPIO 的分配与正在使用它的进程相关联,从而改进了对用户空间进程使用哪些 GPIO 线路的控制;
- 以一次读取或写入多条 GPIO 线路;
- 可以按名称找到 GPIO 控制器和 GPIO 线路;
- 可以配置引脚的状态(开源、开漏等);
- 捕获事件(来自 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 线的名称,未命名的线显示为 unnamed
,unused
表示该 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
表示 gpiochip0
,75
为 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
提供的几个函数的解释:
-
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); }
-
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); }
-
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); }
-
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开发上吧!
相关文章:

Linux 内核 GPIO 用户空间接口
文章目录 Linux 内核 GPIO 接口旧版本方式:sysfs 接口新版本方式:chardev 接口 gpiod 库及其命令行gpiod 库的命令行gpiod 库函数的应用 GPIO(General Purpose Input/Output,通用输入/输出接口),是微控制器…...

Hive数据倾斜--处理方法
1. 什么是数据倾斜? 在分布式计算场景下,大量的数据集中在某一个节点而导致一个任务的执行时间变长。而大量的节点只处理了小部分的数据,大数据组件处理海量数据的特点就是不患多,而患不均。 2. 怎么发现任务出现了数据倾斜现象 …...

k8s流控平台apiserver详解
一、简单理解认识apiserver 1.主要功能 认证 鉴权 准入 mutating validating admission 限流 2.概念 apiserver保护etcd,缓存机制,有缓存直接返回,没缓存再去查看etcd,apiserver是担任和其他平台同信并认证 3.访问控制概览…...

unity对于文件夹的操作
1、获取目标文件夹内所有文件夹 string[] directories Directory.GetDirectories(Path);for (int i 0; i < directories.Length; i){print(directories[i]);}2、获取目标文件夹内指定文件 public List<string> GetAllTxt(string path){//只获取文件名string[] files…...

[Redis]哨兵机制
哨兵机制概念 在传统主从复制机制中,会存在一些问题: 1. 主节点发生故障时,进行主备切换的过程是复杂的,需要人工参与,导致故障恢复时间无法保障。 2. 主节点可以将读压力分散出去,但写压力/存储压力是无法…...

Vue3--Watch、Watcheffect、Computed的使用和区别
Vue3–Watch、Watcheffect、Computed的使用和区别 一、watch 1.功能 watch 用于监听响应式数据的变化,并在数据变化时执行特定的回调函数。适合在响应式数据变化时执行异步操作或复杂逻辑。 2.主要特点 指定数据监听:可以精确地监听一个或多个响应式…...

hive调优原理详解:案例解析参数配置(第17天)
系列文章目录 一、Hive常问面试函数(掌握) 二、Hive调优如何配置(重点) 文章目录 系列文章目录前言一、Hive函数(掌握)11、JSON数据处理12、炸裂函数13、高频面试题13.1 行转列13.2 列转行 14、开窗函数&a…...

华为机试HJ15求int型正整数在内存中存储时1的个数
华为机试HJ15求int型正整数在内存中存储时1的个数 题目: 输入一个 int 型的正整数,计算出该 int 型数据在内存中存储时 1 的个数。 数据范围:保证在 32 位整型数字范围内 想法: 将输入的十进制数转为二进制,遍历记…...

NLP - Softmax与层次Softmax对比
Softmax Softmax是神经网络中常用的一种激活函数,用于多分类任务。Softmax函数将未归一化的logits转换为概率分布。公式如下: P ( y i ) e z i ∑ j 1 N e z j P(y_i) \frac{e^{z_i}}{\sum_{j1}^{N} e^{z_j}} P(yi)∑j1Nezjezi 其中&#…...

HttpServer内存马
HttpServer内存马 基础知识 一些基础的方法和类 HttpServer:HttpServer主要是通过带参的create方法来创建,第一个参数InetSocketAddress表示绑定的ip地址和端口号。第二个参数为int类型,表示允许排队的最大TCP连接数,如果该值小…...

51单片机-让一个LED灯闪烁、流水灯(涉及:自定义单片机的延迟时间)
目录 设置单片机的延迟(睡眠)函数查看单片机的时钟频率设置系统频率、定时长度、指令集 完整代码生成HEX文件下载HEX文件到单片机流水灯代码 (自定义延迟时间) 设置单片机的延迟(睡眠)函数 查看单片机的时钟频率 检测前单片机必…...

MYSQL原理、设计与应用
概述 数据库(Database,DB)是按照数据结构来组织、存储和管理数据的仓库,其本身可被看作电子化的文件柜,用户可以对文件中的数据进行增删改查等操作。 数据库系统是指在计算机系统中引入数据库后的系统,除了数据库,还…...

flask项目部署总结
这个部署的时候要用虚拟环境,cd进项目文件夹 python3 -m venv myenv source myenv/bin/activate激活 之后就安装一些库包之类的,(flask,requests,bs4,等等) 最重要的是要写.flaskenv文件并且pip install 一个能运行…...

【总线】AXI4第八课时:介绍AXI的 “原子访问“ :独占访问(Exclusive Access)和锁定访问(Locked Access)
大家好,欢迎来到今天的总线学习时间!如果你对电子设计、特别是FPGA和SoC设计感兴趣,那你绝对不能错过我们今天的主角——AXI4总线。作为ARM公司AMBA总线家族中的佼佼者,AXI4以其高性能和高度可扩展性,成为了现代电子系统中不可或缺的通信桥梁…...

Java面试八股之MYISAM和INNODB有哪些不同
MYISAM和INNODB有哪些不同 MyISAM和InnoDB是MySQL数据库中两种不同的存储引擎,它们在设计哲学、功能特性和性能表现上存在显著差异。以下是一些关键的不同点: 事务支持: MyISAM 不支持事务,没有回滚或崩溃恢复的能力。 InnoDB…...

大数据面试题之数据库(2)
数据库中存储引擎MvlSAM与InnoDB的区别 Mylsam适用于什么场景? InnoDB和Mvlsam针对读写场景? MySQL Innodb实现了哪个隔离级别? InnoDB数据引擎的特点 InnoDB用什么索引 Hash索引缺点 数据库索引的类型,各有什么优缺点? MySQL的索引有哪些?索引…...

1421-04SF 同轴连接器
型号简介 1421-04SF是Southwest Microwave的2.4 mm 同轴连接器。这款连接器外壳和耦合螺母: 不锈钢 CRES 合金 UNS-S30300, 按照 ASTM A582 标准制造,并按照 ASTM A967-99 标准进行钝化处理。金镀层可以提供更低的接触电阻和更好的耐腐蚀性。 型号特点 50 欧姆密封…...

第一节-k8s架构图
一个Deployment,可以由多个不同Node下的Pod组成,每个Pod又由多个Container组成。 区分Deployment是用Labels(key:value),区分Pod是用PodName,区分Container是用ContainerName。 一个Node可以包含多个不同Deployment中的pod&…...

【Proteus】按键的实现『⒉种』
🚩 WRITE IN FRONT 🚩 🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四" 🔎🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…...

Windows 11 安装 Python 3.11 完整教程
Windows 11 安装 Python 3.11 完整教程 一、安装包安装 1. 下载 Python 3.11 安装包 打开浏览器,访问 Python 官方下载页面。点击“Download Python 3.11”,下载适用于 Windows 的安装包(Windows installer)。 2. 安装 Python 3.11 运行下载的安装包 python-3.11.x-amd6…...

外呼系统的功能有哪些
1. 自动拨号 - 系统能够自动拨打电话,避免了手动拨号的繁琐过程。 - 可以根据设定的电话号码列表自动拨号,提高电话接触率和工作效率。 2. 呼叫分配 - 根据事先设定的规则和策略,将呼叫分配给不同的坐席或代表。 - 确保呼叫平均分配和资源优…...

【C语言】C语言 4 个编译过程详解
C语言的编译过程涉及几个关键步骤、概念和细节,每个步骤都有助于将人类可读的源代码转换为可执行的机器码。以下是详细的解释和示例: 一、什么是编译? 编译是将源代码转换为目标代码的过程。它是在编译器的帮助下完成的。编译器检查源代码是…...

Linux 常见的几种编辑器的操作步骤
在大多数命令行文本编辑器中,保存并关闭文件的操作方式基本相似。以下是常见的几种编辑器的操作步骤: 使用 vi 编辑器保存并关闭文件 编辑文件: sudo vi /path/to/file 编辑内容: 按 i 进入插入模式,编辑文件内容。 …...

LabVIEW汽车转向器测试系统
绍了一种基于LabVIEW的汽车转向器测试系统。该系统集成了数据采集、控制和分析功能,能够对转向器进行高效、准确的测试。通过LabVIEW平台,实现了对转向器性能参数的实时监测和分析,提升了测试效率和数据精度,为汽车转向器的研发和…...

image媒体组件属性配合swiper轮播
图片组件(image) 先插入个图片试试,插入图片用src属性,这是图片: 代码如下: <template><view><swiper indicator-dots indicator-color "#126bae" indicator-active-color &…...

nginx的匹配及重定向
一、nginx的匹配: nginx中location的优先级和匹配方式: 1.精确匹配:location / 对字符串进行完全匹配,必须完全符合 2.正则匹配:location ^~ ^~ 前缀匹配,以什么为开头 ~区分大小写的匹配 ~* 不区分…...

云计算【第一阶段(23)】Linux系统安全及应用
一、账号安全控制 1.1、账号安全基本措施 1.1.1、系统账号清理 将非登录用户的shell设为/sbin/nologin锁定长期不使用的账号删除无用的账号 1.1.1.1、实验1 用于匹配以/sbin/nologin结尾的字符串,$ 表示行的末尾。 (一般是程序用户改为nologin&…...

YUM——简介、安装(Ubuntu22.04)
1、简介 YUM(Yellowdog Updater, Modified)是一个开源的命令行软件包管理工具,主要用于基于 RPM 包管理系统的 Linux 发行版,如 CentOS、Red Hat Enterprise Linux (RHEL) 和 Fedora。YUM 使用户能够轻松地安装、更新、删除和管理…...

Java面向对象练习(4.文字格斗游戏)(2024.7.4)
角色类 package FightGame20240704; import java.util.Random; public class GameRole {Random r new Random();private String name;private int blood;private char gender;private String face;public GameRole(){}public GameRole(String name, int blood, char gender){…...

Python获取QQ音乐歌单歌曲
准备工作 歌单分享的url地址 比如: https://i.y.qq.com/n2/m/share/details/taoge.html?hosteuinoKvzoK4l7evk7n**&id9102222552&appversion130605&ADTAGwxfshare&appshareiphone_wx 代码实现 def mu(share_url):share_url share_url.split(id…...