旭日X3派开发板给我们提供了40Pin管脚,可以方便我们进行对外部设备的控制:
image.png image.png旭日X3派也预置了GPIO Python库Hobot.GPIO,可以方便我们在Python中快速通过GPIo管脚操控外部设备。
挡在root用户权限下,使用Hobot.GPIO或者使用其他方法操控GPIO时,没有任何问题。-
但是总使用root用户,不是一个好主意,我们更期望在普通用户权限下面,也可以使用。
但是,在普通用户环境下,Python中调用Hobot.GPIO,会遇到下面的错误:-
在Linux系统中,可以通过udev来设置设备的权限,使得普通用户也可以使用系统设备,例如GPIO、I2C、存储卡等。-
按照我以往在Ubuntu系统下,通过udev来设置GPIO权限后,发现还是会有这个问题。
经过解包 /usr/local/lib/python3.8/dist-packages/Hobot.GPIO-0.0.2-py3.8.egg ,分析具体提示出错外位置,发现权限错误提示,来自Hobot/GPIO/gpio_pin_data.py中如下的代码:-
其中:
- export和unexport不用说,是gpio fs基础操作需要的文件
- SYSFS_BOARDID_PATH这个,对应的嗯定义为 /sys/class/socinfo/board_id ,在其他Ubuntu环境中,没有要求这个读写的权限,但Hobot.GPIO需要了,于是将这个文件设置为当前用户可读写的权限,问题解决
最终,需要的配置如下:/etc/udev/rules.d/99-gpio.rules
SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip* /sys/class/socinfo/board_id", RUN+="/bin/chmod -R u+rw,g=u /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip* /sys/class/socinfo/board_id"
SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p/value"
SUBSYSTEM=="gpio", KERNEL=="gpio*", ACTION=="change", ENV{TRIGGER}!="none", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p", RUN+="/bin/chmod -R u+rw,g=u /sys%p/value"
上述配置的主要作用在于:
- 系统启动时,设置 /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip* /sys/class/socinfo/board_id的权限,为gpio组可读写
- 当变更gpio管脚时,设定对应管脚的fs文件可读写;例如export一个新的管脚,或者unexport一个已经激活的
经过多位同学配合测试,最终的完整操作步骤如下:
-
切换到需要操作的普通用户身份下:
# 切换身份;如果已经是该用户,则不用切换 sudo su - sunrise
-
设置GPIO用户组,并添加当前用户进组:
# 添加gpio用户组 sudo groupadd -f -r gpio # 将当前用户添加到gpio组 sudo usermod -a -G gpio $USER
-
设置gpio的udev权限规则
sudo vim /etc/udev/rules.d/99-gpio.rules ### 将前面说明的配置,写入到该文件中 ### ### 先按ESC、再输入wq!,回车保存
-
手工执行设定权限,可不用重启生效:
sudo /bin/chgrp -R gpio /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip* /sys/class/socinfo/board_id sudo /bin/chmod -R u+rw,g=u /sys/class/gpio/export /sys/class/gpio/unexport /dev/gpiochip*
-
重新调用udev规则,使得/etc/udev/rules.d/99-gpio.rules生效
sudo udevadm control --reload-rules
-
查看权限,检查是否生效
ls -lh /dev/gpiochip* /sys/class/gpio/{export,unexport} # 以下为输出结果 crw-rw-rw- 1 root gpio 254, 0 11月 9 20:16 /dev/gpiochip0 -rw-rw-rw- 1 root gpio 4.0K 11月 10 19:38 /sys/class/gpio/export -rw-rw-rw- 1 root gpio 4.0K 11月 9 20:16 /sys/class/gpio/unexport
-
测试是否生效:先将GPIO40和旁边的GND接上LED灯,再执行python3编写测试代码:
>>> import Hobot.GPIO as GPIO >>> GPIO.setmode(GPIO.BOARD) >>> GPIO.VERSION '0.0.2' >>> GPIO.model 'X3PI' >>> GPIO.getmode() 'BOARD' >>> GPIO.setup(40, GPIO.OUT) >>> GPIO.output(40, 1) >>> GPIO.output(40, 0) 以上设置正确的情况下:
import Hobot.GPIO as GPIO
将不再提示权限错误。GPIO.output(40, 1)
的时候,LED灯点亮GPIO.output(40, 0)
的时候,LED灯熄灭
上述操作步骤,得到了多位同学的帮助和验证,在此表示感谢!
附加:
-
shell操作GPIO:
echo 108 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio108/direction echo 1 > /sys/class/gpio/gpio108/value echo 0 > /sys/class/gpio/gpio108/value
-
C语言操作GPIO:
#include <fcntl.h> //define O_WRONLY and O_RDONLY #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> //芯片复位引脚: P1_16 #define SYSFS_GPIO_EXPORT "/sys/class/gpio/export" #define SYSFS_GPIO_RST_PIN_NAME "P40" #define SYSFS_GPIO_RST_PIN_VAL "108" #define SYSFS_GPIO_RST_DIR "/sys/class/gpio/gpio108/direction" #define SYSFS_GPIO_RST_DIR_VAL "OUT" #define SYSFS_GPIO_RST_VAL "/sys/class/gpio/gpio108/value" #define SYSFS_GPIO_RST_VAL_H "1" #define SYSFS_GPIO_RST_VAL_L "0" int main() { int fd; //打开端口/sys/class/gpio# echo 108 > export fd = open(SYSFS_GPIO_EXPORT, O_WRONLY); if (fd == -1) { printf("ERR: Radio hard reset pin open error.\n"); return EXIT_FAILURE; } write(fd, SYSFS_GPIO_RST_PIN_VAL, sizeof(SYSFS_GPIO_RST_PIN_VAL)); close(fd); //设置端口方向/sys/class/gpio/gpio108# echo out > direction fd = open(SYSFS_GPIO_RST_DIR, O_WRONLY); if (fd == -1) { printf("ERR: Radio hard reset pin direction open error.\n"); return EXIT_FAILURE; } write(fd, SYSFS_GPIO_RST_DIR_VAL, sizeof(SYSFS_GPIO_RST_DIR_VAL)); close(fd); //输出复位信号: 拉高>100ns fd = open(SYSFS_GPIO_RST_VAL, O_RDWR); if (fd == -1) { printf("ERR: hard reset pin value open error.\n"); return EXIT_FAILURE; } while (1) { write(fd, SYSFS_GPIO_RST_VAL_H, sizeof(SYSFS_GPIO_RST_VAL_H)); usleep(1000*1000); write(fd, SYSFS_GPIO_RST_VAL_L, sizeof(SYSFS_GPIO_RST_VAL_L)); usleep(1000*1000); } close(fd); printf("INFO:hard reset pin value open error.\n"); return 0; }
编写保存为
gpio_test.c
,再编译gcc -o gpio_test gpio_test.c
,然后运行./gpio_test