旭日X3派上的普通用户GPIO使用权限设置

旭日X3派开发板给我们提供了40Pin管脚,可以方便我们进行对外部设备的控制:

image.png

image.png

旭日X3派也预置了GPIO Python库Hobot.GPIO,可以方便我们在Python中快速通过GPIo管脚操控外部设备。

挡在root用户权限下,使用Hobot.GPIO或者使用其他方法操控GPIO时,没有任何问题。-
但是总使用root用户,不是一个好主意,我们更期望在普通用户权限下面,也可以使用。

但是,在普通用户环境下,Python中调用Hobot.GPIO,会遇到下面的错误:-

image.png

在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中如下的代码:-

image.png

其中:

  • 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一个已经激活的

经过多位同学配合测试,最终的完整操作步骤如下:

  1. 切换到需要操作的普通用户身份下:

    # 切换身份;如果已经是该用户,则不用切换
    sudo su - sunrise
    
  2. 设置GPIO用户组,并添加当前用户进组:

    # 添加gpio用户组
    sudo groupadd -f -r gpio
    
    # 将当前用户添加到gpio组
    sudo usermod -a -G gpio $USER
    
  3. 设置gpio的udev权限规则

    sudo vim /etc/udev/rules.d/99-gpio.rules
    ### 将前面说明的配置,写入到该文件中 ###
    ### 先按ESC、再输入wq!,回车保存
    
  4. 手工执行设定权限,可不用重启生效:

    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*
    
  5. 重新调用udev规则,使得/etc/udev/rules.d/99-gpio.rules生效

    sudo udevadm control --reload-rules
    
  6. 查看权限,检查是否生效

    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
    
  7. 测试是否生效:先将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灯熄灭

上述操作步骤,得到了多位同学的帮助和验证,在此表示感谢!

附加:

  1. 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
    
  2. 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

hobot.gpio太简单,输入时,上下拉电阻都没法设置

??

新的系统这么操作还是不行。