尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Ubuntu实机GPIO控制全攻略:从libgpiod到寄存器操作

Ubuntu实机GPIO控制全攻略:从libgpiod到寄存器操作 开个篇先聊点实际的。之前有个朋友在GitHub上给我丢了一段代码说他在Ubuntu实机下控制GPIO读回来的电平怎么都不对。我远程看了半个多小时最后发现问题根本不在代码而是他压根没搞清楚自己操作的GPIO挂在哪个控制器下——板载SoC引脚、I2C扩展芯片、USB转GPIO模块这三者的访问方式完全不一样。Linux下玩GPIO现象千奇百怪根因其实就那么几个。这篇我就把在Ubuntu实机上调GPIO的完整路径捋一遍从硬件路线分类、内核接口选型到libgpiod命令行实操、C/Python编程、I2C扩展芯片再到/dev/mem寄存器直操作最后附一份高频报错排查表。内容偏“抄作业”风格命令和代码都直接可用适合刚接触Linux GPIO的嵌入式工程师、学生以及那些在树莓派、RK、全志等板子上折腾Ubuntu的老哥。1. 先搞清楚Ubuntu实机上的GPIO到底长什么样1.1 板载SoC引脚、扩展芯片、USB模块先分清你的硬件路线很多人一上来就问“Ubuntu怎么控制GPIO”但这个问题的答案取决于你的硬件平台。我习惯把常见情况分成三类。第一类是板载SoC GPIO也就是CPU/SoC直接引出来的引脚树莓派、香橙派、各种RK/全志核心板都属于这类。这类GPIO由SoC内部的GPIO控制器管理Linux内核会把它注册成一个或多个gpiochip用户空间通过/dev/gpiochipN访问。这是最常见的场景也是后面几章重点讲的。第二类是外部扩展芯片。有些工控主板、单板机的原生GPIO不够用于是通过I2C、SPI甚至UART串口外挂扩展芯片比如PCF8574、MCP23017或者一些“1路UART串口转16路GPIO扩展芯片”。这类芯片在Linux下会被识别为额外的gpiochip用法和板载GPIO高度相似但硬件接线和地址配置有自己的一套讲究。第三类是USB转GPIO模块比如常见的USB GPIO适配器。这类设备一般走USB串口或者HID协议很多时候厂商会提供自己的驱动和库不一定会注册成标准gpiochip。如果你拿到的是这种模块建议先看厂商SDK别想着用gpiod一套通吃。先想清楚自己属于哪一类再往下走能省很多无用功。1.2 用户空间访问GPIO的两代接口sysfs 与 chardevLinux用户空间操作GPIO历史上主要有两代接口。早期是sysfs接口路径在/sys/class/gpio下面。要操作某个引脚先往export文件里写入引脚编号然后系统会生成gpioN目录里面有一堆文件通过向direction和value写值来控制方向和高低电平。这个方式很直观很多老教程都在讲。但sysfs接口有几个硬伤多个进程同时操作时容易出现竞态电平变化事件只能用poll监听丢失事件是常事而且它对现代GPIO控制器的一些特性支持很差比如没法配置上拉/下拉、驱动强度、active-low这些属性。所以内核从4.8版本开始引入了新的字符设备接口也就是/dev/gpiochipN配合libgpiod库来使用。新接口通过ioctl完成所有操作支持设置消费标签支持事件去抖也支持更丰富的配置项。在新版Ubuntu里你会发现/sys/class/gpio可能压根不存在因为发行版已经默认关掉了CONFIG_GPIO_SYSFS。所以除非你在维护老项目否则我建议直接学libgpiod。1.3 为什么强调“实机”而不是虚拟机标题里特意写了“实机”因为这里有个经典误区。很多人习惯在VMware/VirtualBox里装Ubuntu练手但虚拟机里的Ubuntu默认是看不到宿主物理GPIO的。虚拟机里的/dev/gpiochip0如果存在那也是虚拟化软件模拟出来的设备比如某些场景下VMware会提供虚拟串口但不会把宿主的GPIO控制器暴露进去。你如果确实要在虚拟机里玩GPIO常见做法是USB转GPIO模块然后把USB设备透传给虚拟机而且还要祈祷厂商驱动在虚拟机里正常工作。这个链路会引入大量调试变量不如直接在实机上跑哪怕是一块几十块的树莓派Zero也行。WSL更不用说了它连完整内核都没有GPIO这类需要物理硬件配合的功能基本不用指望。2. 动手前的环境体检内核、设备节点与权限2.1 先在命令行确认内核与设备节点拿到一台Ubuntu实机第一件事不是写代码而是确认系统到底认不认你的GPIO硬件。打开终端依次执行uname -a ls /dev/gpiochip*如果你看到/dev/gpiochip0、/dev/gpiochip1之类的节点说明内核已经注册了GPIO控制器后面可以直接用。如果什么都看不到先别慌可能是下面几种情况。普通x86台式机/笔记本上跑Ubuntu很可能就是没有可被用户空间直接访问的GPIO控制器这是正常的需要用USB转GPIO或扩展芯片来解决。如果是开发板检查一下内核有没有把对应GPIO控制器驱动编译进内核。比如树莓派要确保配置里有pinctrl相关驱动可以用dmesg查一下dmesg | grep -i gpio另外也要注意新版内核里sysfs接口可能被裁剪所以别拿“/sys/class/gpio不存在”来判断硬件有问题要看/dev/gpiochip节点。2.2 用 gpiodetect/gpioinfo 摸清GPIO家底设备节点存在只是第一步接下来要弄清楚每个gpiochip对应什么硬件、有多少引脚、引脚当前是什么状态。这一步用libgpiod提供的工具最方便先安装sudo apt update sudo apt install gpiod然后执行gpiodetect gpioinfogpiodetect会列出系统里所有的gpiochip包括名字、标签和引脚数。gpioinfo会把每个chip里所有引脚的当前方向、active状态、使用的consumer等信息列出来。这个信息非常重要因为gpiochip内部的line编号不一定是板子丝印上的编号。比如有些板子丝印写的是“GPIO4_17”但它在gpiochip里的offset可能是97之类拿丝印编号直接操作小白很容易翻车。实际以gpioinfo输出的序号为准。2.3 非root用户操作GPIO的正确姿势不想每次操作都加sudo就得花两分钟把权限配好。我习惯的做法是创建或者复用系统里的gpio用户组sudo groupadd gpio sudo usermod -aG gpio $USER然后写一个udev规则文件比如/etc/udev/rules.d/99-gpio.rules内容如下KERNELgpiochip*, GROUPgpio, MODE0660 SUBSYSTEMgpio, GROUPgpio, MODE0660保存后重载规则sudo udevadm control --reload-rules sudo udevadm trigger重新登录一次之后当前用户就能直接访问/dev/gpiochip节点了。这个配置是新手最容易忽略的很多人一运行gpioset就报Operation not permitted其实就是权限没配。2.4 引脚被复用或占用时怎么定位板载GPIO经常不是所有引脚都能随便用不少引脚默认被复用成了UART、I2C、PWM等功能。如果你对某个引脚操作后毫无反应先查一下它是不是被其他驱动占用了。一个快速排查方法是看gpioinfo输出的consumer字段。如果一个引脚的consumer是“uart0”或者“pinctrl”说明它已经被别的驱动拿走了你用libgpiod去请求时会报错或者请求成功但电平不受你控制。更彻底的办法是看引脚复用的配置。有些板子需要在设备树或板级配置里改pinmux比如树莓派的config.txt里可以配置引脚功能。这块不同板卡差异很大但思路是统一的先确认引脚当前功能再决定要不要改设备树。3. 最顺手的控制方式libgpiod工具集实战3.1 安装和验证libgpiod环境体检通过之后就可以进入实操环节。前面已经装了gpiod这个包它提供gpiodetect、gpioinfo、gpioset、gpioget、gpiomon、gpiofind这六个命令行工具。开发库的安装是另一个包sudo apt install libgpiod-dev装完可以用如下命令验证工具版本gpioset --version这里提醒一下Ubuntu 22.04上默认是libgpiod 1.x系列Ubuntu 24.04上已经切到了2.x系列。命令行工具基本兼容但C/Python的开发接口差异很大后面写代码时我会专门说明版本差异。3.2 常用工具命令对照先让每个引脚动起来先用一组简单命令感受一下# 查看所有chip信息 gpiodetect # 查看某个chip的详细信息 gpioinfo gpiochip0 # 设置gpiochip0的第17号引脚输出高电平并保持 gpioset -m wait gpiochip0 171 # 读取gpiochip0第27号引脚当前电平 gpioget gpiochip0 27 # 监听第27号引脚的上升沿和下降沿带100ms去抖 gpiomon -b 100 -r -f gpiochip0 27这里最关键的是gpioset要加-m wait参数。默认情况下gpioset设置完电平后进程就退出了引脚电平会立即释放除非你用了-t mode参数指定toggle时间或者用-m wait/--modewait让进程保持在前台。我遇到过很多次“命令执行成功但LED闪一下就灭了”的情况就是忘了加这个参数。gpioinfo可以详细查看引脚状态这个命令也是排查问题的好帮手。比如某个引脚方向不对、被谁占用了一查便知。3.3 点亮LED并读取按键一次完整流程下面用一个最小实例把整个流程串起来。假设板子上有一个LED和一个按键硬件连接如下LED阳极接3.3V阴极经220Ω限流电阻接到GPIO17。为什么是220Ω简单算一下红色LED正向压降大约2.0V工作电流取10mA电源3.3V限流电阻就是(3.3-2.0)/0.01130Ω取常见的220Ω完全够用电流会略小一点但寿命更好。如果你是5V的IO电平按(5-2)/0.01300Ω选330Ω也行。这个计算习惯要刻在脑子里很多人烧坏引脚就是因为直接用导线把LED怼在GPIO上电流完全不受控制。按键一端接GND另一端接GPIO27。芯片内部如果支持上拉就配置内部上拉如果不支持就在按键和3.3V之间外接一个10kΩ上拉电阻这样按键按下时读到的电平是0松开是1。启动一个终端执行# 保持GPIO17输出低电平点亮LED因为LED阳极接了电源 gpioset -m wait gpiochip0 170这时候LED应该常亮。再开一个终端读取按键状态gpioget gpiochip0 27按下按键时读到0松开时读到1说明输入链路没问题。最后监听按键边沿gpiomon -b 100 -r -f gpiochip0 27按下和松开时终端会分别输出上升沿/下降沿事件。这个流程跑通说明你的GPIO环境基本OK后面无论是写脚本还是写C程序都有底了。3.4 三个容易踩的参数active-low、bias、edge实操中很多“奇怪现象”其实都出在参数上。第一个是active-low。有些板子上的LED是“低电平点亮”的有些继电器模块也是低电平触发如果你拿着“输出1就是高电平”的思维去操作LED当然不亮。libgpiod支持把某条线标记为active-low这样你在逻辑上写1实际物理电平是低。命令行里没有直接参数但gpioinfo里可以看到这条线是否被标记为active-low。要不要用这个功能看个人习惯我一般不用而是在应用层做电平逻辑转换少一层抽象反而更好排查。第二个是bias也就是上拉/下拉。输入引脚悬空时电平会飘读取结果不稳定。gpioinfo能显示每条线当前的上拉/下拉配置但命令行工具默认不做配置修改需要你在程序里用库接口配置。在写业务代码之前一定要确认这个参数否则按键输入会时好时坏。第三个是edge也就是边沿事件。gpiomon的-r监听上升沿、-f监听下降沿、不加参数监听双边沿。实际场景里机械按键按下去的瞬间会有电平抖动会产生好几个边沿事件所以务必加-b参数设置去抖时间100ms比较常用。如果是霍尔传感器这类快信号去抖时间要调小甚至关掉。4. 把GPIO写进业务代码C/Python与开机自启4.1 C语言最小实例基于libgpiod 1.x命令行工具能做的只是验证硬件真正做产品还得写代码。先给一个C语言最小例子基于libgpiod 1.x的API这也是网上最常用的版本。#include gpiod.h #include stdio.h #include unistd.h int main(void) { struct gpiod_chip *chip; struct gpiod_line *line; chip gpiod_chip_open_by_name(gpiochip0); if (!chip) { perror(open gpiochip0); return 1; } line gpiod_chip_get_line(chip, 17); if (!line) { perror(get line 17); gpiod_chip_close(chip); return 1; } if (gpiod_line_request_output(line, demo, 0) 0) { perror(request output); gpiod_chip_close(chip); return 1; } gpiod_line_set_value(line, 1); sleep(2); gpiod_line_set_value(line, 0); gpiod_line_release(line); gpiod_chip_close(chip); return 0; }编译命令gcc -o gpio_demo gpio_demo.c -lgpiod这个程序的逻辑很简单打开gpiochip0请求第17号引脚为输出先拉高2秒再拉低然后释放资源。需要注意的是如果你系统里装的是libgpiod 2.x上面的API已经变了编译会报一些头文件找不到或者函数未定义的错误。遇到这种情况先执行dpkg -l | grep libgpiod看看自己装的是哪个版本再按对应版本的手册调整。4.2 Python绑定与边沿事件监听Python做原型验证比C快得多。libgpiod 1.x的Python绑定大致长这样import gpiod import time chip gpiod.Chip(gpiochip0) # 第17号引脚输出高电平 line_out chip.get_line(17) line_out.request(consumerdemo_out, typegpiod.LINE_REQ_DIR_OUT) line_out.set_value(1) time.sleep(2) line_out.set_value(0) line_out.release() # 第27号引脚监听下降沿按键按下 line_in chip.get_line(27) line_in.request(consumerdemo_in, typegpiod.LINE_REQ_EV_FALLING_EDGE, flagsgpiod.LINE_REQ_FLAG_BIAS_PULL_UP) while True: if line_in.event_wait(sec5): event line_in.event_read() print(key pressed:, event.type) line_in.release() chip.close()如果你用的是libgpiod 2.xPython API改成了request_lines这种风格更多信息建议直接查本机帮助python3 -c import gpiod; print(dir(gpiod))这个差异让很多人困惑我之前也在这上面浪费过几个小时所以真心建议跑Python之前先确认版本再查对应API别拿旧博客代码直接粘贴。边沿事件这个场景在实际项目里很常见比如按键触发拍照、门磁触发告警。事件监听本身是阻塞式的配合队列和回调函数就能把GPIO事件包装成业务消息。4.3 用systemd让GPIO服务开机自启产品一旦做完总不能每次开机都手动跑一遍程序。我习惯把GPIO逻辑做成一个systemd服务。写一个unit文件比如/etc/systemd/system/gpio-demo.service[Unit] DescriptionGPIO Demo Service Aftermulti-user.target [Service] Typesimple ExecStart/usr/local/bin/gpio_demo Restarton-failure Usergpio Groupgpio [Install] WantedBymulti-user.target然后启用sudo systemctl daemon-reload sudo systemctl enable --now gpio-demo这里有几个细节。ExecStart务必写绝对路径systemd不会自动去PATH里找程序。User和Group选对否则程序没有权限打开/dev/gpiochip节点服务会不停重启。Restarton-failure可以让程序崩溃后自动拉起但对于GPIO服务要小心如果引脚状态需要初始化崩溃重启后程序逻辑要能正确处理“上电一瞬间引脚电平不确定”的情况。还有一个经验不要在shell脚本或者Python里sleep一轮来做“延时启动”有依赖就在unit里写After和RequiresGPIO服务对时序敏感系统服务管理器比你手写sleep可靠得多。5. 引脚不够用I2C/串口扩展出更多GPIO5.1 什么时候需要扩展GPIO板载GPIO数量经常不够用尤其做稍微复杂一点的工控项目时可能同时要控制继电器、读按键、接传感器板子引脚两下就分完了。这时候外挂扩展芯片是非常经济的选择。最常用的扩展方案是I2C GPIO扩展典型芯片就是PCF8574和MCP23017。PCF8574通过I2C接口提供8个准双向IO口成本很低接线也简单一共就4根线VCC、GND、SDA、SCL。MCP23017可以提供16个IO口还支持中断输出适合需要事件通知的场景。5.2 PCF8574接线与i2cdetect识别硬件连接先把PCF8574的VCC接到板子的3.3V或5V看芯片型号和数据手册SDA和SCL分别接到I2C总线的对应引脚。I2C总线需要上拉电阻很多开发板自带上拉如果你是自己手工搭的电路SDA和SCL各接一个4.7kΩ到VCC。这个上拉电阻很重要不接的话I2C经常间歇性失效而且特别难排查。软件层面先确保I2C工具已安装sudo apt install i2c-tools然后用i2cdetect扫描总线sudo i2cdetect -y 1这里-y 1里的1是I2C总线号具体要看你的板子有的板子是0。如果芯片接线正确扫描结果里会出现一个地址。PCF8574的默认地址范围是0x20到0x27PCF8574A是0x38到0x3F具体是多少取决于A0、A1、A2三个地址引脚的接法。比如A0、A1、A2全部接地PCF8574的地址就是0x20。5.3 扩展芯片被识别为独立gpiochip后怎么用如果你的内核已经编译了pcf857x驱动插上芯片后系统会自动把它注册成一个gpiochip。确认方法gpiodetect这时候你会看到多出来一个chip名字类似“pcf857x 1-0020”。控制它的引脚和板载GPIO一样只是chip名换成对应的gpiochip号。比如gpioset -m wait gpiochip1 01上面命令把扩展芯片的第0号引脚输出高电平。需要注意的是PCF8574的引脚作为输入时有个特点它是一个准双向IO口输出高电平其实是“释放引脚”要靠外部上拉才能读到高电平。所以如果你接了一个按键按键一端接PCF8574的引脚另一端接GND想读按键状态得先在代码里把该引脚输出1然后再读输入。这个坑特别隐蔽我记得第一次调PCF8574时怎么读都是0后来看数据手册才发现要先写1。如果你不想依赖内核驱动也可以直接用i2cset和i2cget操作芯片寄存器但这样做就失去了gpiod统一管理的便利而且需要考虑总线访问冲突。我还是推荐用gpiod方式。5.4 聊聊UART串口转16路GPIO扩展除了I2C还有一个常见需求是“1路UART串口转16路GPIO扩展芯片”。这种芯片通过UART串口通信把16路引脚的状态打包成寄存器用串口指令来读写。好处是很多设备都有UART接线也简单两根线TX/RX或者四根线加流控就能扩展出十几路IO。缺点也同样明显通过串口轮询引脚状态实时性比I2C扩展还要差一些而且每个厂商的协议格式都不一样驱动基本靠厂商提供。如果你的项目对实时性要求不高IO数量多这种方案可以考虑如果要求毫秒级响应还是优先选I2C/SPI扩展或者干脆换一个引脚更多的SoC。6. 高级但危险/dev/mem直接操作寄存器6.1 什么场景才值得碰寄存器大多数时候gpiod已经够用了。但偶尔会遇到极其追求时序、或者芯片驱动支持不完善的场景这时候有人会考虑/dev/mem直接映射物理地址去操作GPIO控制器的寄存器。这个做法本质上就是绕过内核的GPIO子系统用户空间直接读写硬件寄存器。它能做到很低的延迟能精确控制某几个引脚在同一时刻的电平变化比如做并口协议模拟、DS18B20这种单总线时序。但代价是你得清楚知道自己在干什么。6.2 mmap映射与一个输出高电平的最小示例以BCM2835为例树莓派1/Zero等平台GPIO控制器基地址是0x3F200000。我写一个最基本的示例把某个引脚配置为输出并拉高。注意不同SoC的基地址和寄存器偏移完全不同动手前务必查芯片手册。#include fcntl.h #include sys/mman.h #include unistd.h #define BCM2835_GPIO_BASE 0x3F200000UL #define GPIO_GPFSEL1 0x04 #define GPIO_GPSET0 0x1C #define GPIO_GPCLR0 0x28 int main(void) { int fd open(/dev/mem, O_RDWR | O_SYNC); if (fd 0) return 1; void *map mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BCM2835_GPIO_BASE); if (map MAP_FAILED) return 1; volatile unsigned int *gpio (volatile unsigned int *)map; // 假设操作GPIO17GPFSEL1寄存器中的bit21~23控制GPIO17功能 // 先清零再置为001即输出模式。注意要用读改写别一把覆盖掉其他引脚 gpio[1] ~(7 21); gpio[1] | (1 21); // 拉高GPIO17 gpio[7] (1 17); sleep(2); // 拉低GPIO17 gpio[10] (1 17); munmap(map, 0x1000); close(fd); return 0; }代码里gpio[1]就是寄存器地址GPFSEL10x04除以4gpio[7]是GPSET00x1C除以4gpio[10]是GPCLR00x28除以4。为什么用volatile因为mmap映射的内存如果不加volatile编译器可能优化掉你的写入操作这在寄存器操作里是致命问题。O_SYNC标志也很重要它能避免写操作被page cache挡住保证数据实际落到设备上。6.3 devmem/devmem2与它背后的限制如果你不想写C代码也可以直接用devmem工具。很多系统自带没有就装sudo apt install devmem2比如在树莓派上把GPIO17拉高先配置成输出再置位# 读GPFSEL1修改后写回 sudo devmem 0x3F200004 32 # 置位GPSET0 sudo devmem 0x3F20001C 32 0x00020000devmem最大的问题是不够安全。你物理地址写错一位轻则读到错误数据重则直接让系统死机。而且现代内核开启了CONFIG_STRICT_DEVMEM很多地址段的访问会被拒绝。所以devmem适合在调试阶段快速验证某个寄存器不适合作为产品方案。6.4 为什么我不建议在真实环境里用这招直接操作寄存器对GPIO子系统来说是“越权访问”。你绕过了内核的资源管理内核完全不知道你在操作某个引脚它可能继续把这个引脚当作普通GPIO来配置也可能在某个时刻重新初始化这个引脚最终结果就是系统行为不可控。还有个更现实的问题不同SoC的寄存器布局、复用配置、基地址都不同你在树莓派上写的寄存器代码换到RK3588上完全失效。而gpiod这套基于设备树的接口换了芯片只要内核驱动写得好应用层代码几乎不用改。从维护角度看寄存器方案是给自己挖坑。所以我的建议是寄存器方式只用来做芯片验证、学习原理、或者临时排查某些极端问题正式产品一律用libgpiod。7. 高频问题与排查速查表最后把实操中高频遇到的问题整理成一张表按“现象-原因-处理”的思路来写这样排查的时候可以快速定位。现象常见原因排查与处理提示Operation not permitted当前用户没有设备权限用usermod加gpio组配置udev规则重新登录找不到/dev/gpiochip0内核未注册GPIO控制器或平台无GPIOdmesg查GPIO驱动核对板子设备树配置/sys/class/gpio目录为空内核关闭了CONFIG_GPIO_SYSFS改用/dev/gpiochipNlibgpiod不要纠结旧接口gpioset执行成功但引脚没反应引脚被复用、active-low相反、硬件没接对gpioinfo看consumer和方向检查硬件连线LED亮一下就灭gpioset没加-m wait进程退出后引脚释放加-m wait参数或用后台方式保持按键读数乱跳输入悬空、没有上拉/下拉用内部上拉或外接10kΩ上拉程序里加消抖gpiomon事件重复触发多次机械抖动没有去抖加-b参数比如gpiomon -b 100I2C扫描不到扩展芯片地址不对、上拉电阻缺失、总线号错误核对A0-A2引脚地址补上拉电阻试其他总线号PCF8574输入读不到高电平准双向IO特性需要先写1再读代码里先把引脚置1再读输入devmem访问被拒绝CONFIG_STRICT_DEVMEM限制确认地址范围谨慎使用开发板可改内核参数但别上生产7.1 “Operation not permitted”基本都是权限问题这个报错十有八九是用户组或udev规则没配好。先确认自己能打开设备ls -l /dev/gpiochip0如果权限是crw-rw----而且属组是gpio说明规则没问题再看当前用户是否在gpio组里groups不在组里就usermod -aG gpio $USER然后重新登录。这条其实是最多新手卡住的地方配置一次以后就舒服了。7.2 找不到/dev/gpiochip0或/sys/class/gpio为空先明确平台类型。x86普通桌面机没有可直接访问的GPIO控制器很正常别硬找。如果是开发板用dmesg查GPIO驱动dmesg | grep -i gpio如果没有任何输出很可能是内核配置里把GPIO子系统裁掉了或者设备树没有使能对应控制器。这时候重新编译内核或修改设备树是唯一出路但这个过程比较长建议先在官方内核文档里确认自己的板子默认支不支持。7.3 gpioset执行成功但引脚没反应这种问题最迷惑人因为软件层面完全正常。我的排查顺序是先用gpioinfo看目标引脚的consumer和方向确认它不是被其他驱动复用然后看active-low状态别把高低电平理解反最后回到硬件用万用表量引脚电平如果一直不变再查是焊接问题还是外部电路把引脚拉死了。有一次我调一块板子gpioset怎么设都没反应折腾一下午最后发现是设备树里把另一个外设的片选引脚和这个GPIO重叠了内核根本没把这个引脚注册成普通GPIO。所以遇到这种问题别只盯着软件设备树和硬件电路都要过一遍。7.4 按键抖动导致误触发按键、继电器触点这类机械信号边沿会有一段几十毫秒的不稳定区域直接监听原始边沿肯定会有重复事件。软件上gpiomon的-b参数是最快的解法程序里可以在事件回调里做10~50ms的软件消抖逻辑就是“事件发生后先不处理等一段时间再确认电平状态”。硬件上也可以加RC滤波电路但调起来不如软件消抖方便。7.5 寄存器映射失败或系统不稳定/dev/mem操作寄存器时遇到拒绝访问或者写完寄存器后系统卡死基本就是触碰了内核正在管理的重要资源。我的建议是不要硬杠先回到gpiod除非你确实在调试一个没有任何驱动支持的芯片并且非常清楚每个寄存器的含义。系统不稳的时候快速重启往往比重试更高效。最后再说点个人体会。Linux下控制GPIO这件事说难不难说简单也不简单。我踩过的坑按频率排序大概是权限大于引脚复用大于active-low搞反大于是不是忘了加-m wait这个参数。刚开始接触的朋友建议从命令行工具入手先让灯亮起来再写代码不要上来就抱着寄存器手册啃。七分在硬件连接两分在内核配置一分才是软件把前面几样都捋顺了Linux侧的命令其实就那几行。
返回列表