RPi4+CentOS7环境下RPi.GPIO边缘检测失败求助
问题背景
在RPi4上运行CentOS7(内核版本5.4.206-v8.1.el7),使用RPi.GPIO-0.7.1-py3.9配置GPIO边缘检测时,无论选择哪个引脚都会抛出错误:
RPi.GPIO.add_event_detect(self.pin, RPi.GPIO.BOTH, self._on_event) RuntimeError: Failed to add edge detection
即使使用sudo运行也无法解决。已调整/dev/gpiomem权限为:
crw-rw----. 1 root gpio 246, 0 Jan 1 1970 /dev/gpiomem
GPIO输出模式(如点亮LED)可正常工作,但边缘检测始终失败。
通过strace追踪代码,发现关键错误日志:
faccessat(AT_FDCWD, "/sys/class/gpio/gpio6", F_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/sys/class/gpio/export", O_WRONLY) = -1 EACCES (Permission denied)
额外信息:
- 原以为RPi.GPIO已基于GPIO字符设备而非sysfs,但对实现细节存疑
/sys/class/gpio目录下存在export、gpiochip446、gpiochip454、unexport,疑惑为何要创建/sys/class/gpio/gpio6而非基于控制器基址计算- 使用bcm2835库的event.c示例可正常检测按键,但因技术原因必须使用RPi.GPIO
解决步骤
1. 明确RPi.GPIO的边缘检测依赖
RPi.GPIO-0.7.1版本的边缘检测仍依赖sysfs接口,而非仅使用/dev/gpiomem。输出模式可以通过/dev/gpiomem正常工作,但边缘检测需要操作/sys/class/gpio下的文件,这是权限问题的核心原因。
2. 修复sysfs GPIO权限
方法一:临时调整权限(重启后失效)
直接修改/sys/class/gpio/export和/sys/class/gpio/unexport的权限,让普通用户可写:
sudo chmod 666 /sys/class/gpio/export /sys/class/gpio/unexport
修改后重新运行Python代码(无需sudo),边缘检测即可正常工作。
方法二:永久配置权限(通过udev规则)
创建udev规则文件/etc/udev/rules.d/99-gpio.rules:
SUBSYSTEM=="gpio", ACTION=="add", RUN+="/bin/chmod 666 /sys/class/gpio/export /sys/class/gpio/unexport" SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", RUN+="/bin/chmod 666 /sys/class/gpio/%k/value /sys/class/gpio/%k/edge"
保存后重新加载udev规则:
sudo udevadm control --reload-rules sudo udevadm trigger
该配置会在系统重启后自动生效,无需每次手动修改权限。
3. 关于引脚编号的说明
RPi.GPIO使用BCM编号时,会直接将引脚号(如6)映射到sysfs的gpio6,但RPi4的CentOS7内核中GPIO控制器存在基址偏移。不过RPi.GPIO-0.7.1版本会自动处理编号映射,只要修复sysfs权限让export操作成功,就能自动创建对应的gpio目录,无需手动计算基址偏移后的编号。
4. 验证修复
运行测试代码:
import RPi.GPIO as GPIO X=6 # Set pin X in GPIO mode GPIO.setmode(GPIO.BCM) GPIO.setup(X, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Define a callback function to detect edge events def my_callback(channel): print(f"Edge event detected on pin {X}") # Set the callback function GPIO.add_event_detect(X, GPIO.RISING, callback=my_callback) # Main loop try: while True: pass # Exit gracefully except KeyboardInterrupt: GPIO.cleanup()
此时边缘检测应该能正常触发回调函数。
内容的提问来源于stack exchange,提问作者user20980603

