[MCU] 【国产RISC-V Linux板 昉·星光VisionFive试用报告】GPIO开发基础:从原理到实战

HonestQiao   2022-6-11 21:39 楼主

昉·星光VisionFive开发板上,提供了40Pin IO口,可以供我们在实际开发中使用。

6.后面.jpg

这些IO口的具体功能定义,可以通过官方的资料了解:

14.40针.png

在Linux系统中,GPIO驱动启用后,对应的挂载点在/sys/class/gpio

这里需要说明一下,在Linux系统上,对于常规的文件,用文件路径来访问文件,这个很好理解。

同样的,对于内外设备,Linux系统上,也把这些各种设备,当成特殊形式的文件来访问。

例如,要查看cpu的信息,那么cat /proc/cpuinfo即可。

而GPIO设备的话,其挂载点就是/sys/class/gpio。

 

通过官方40Pin的详细文档,我们可以了解每个引脚具体的挂载点:

15.40针具体定义.png

从上图中,我们可以看到,GPIO0引脚,其对应的sys为448,那么,在系统中,其对应的挂载点,就是 /sys/class/gpio/gpio448

依次类推,我们可以得到,GPIO2引脚,其对应的sys为450,那么其挂载点,就是/sys/class/gpio/gpio450

 

但是在Linux系统中,具体GPIO的引脚,可能不会开机自动挂载,需要我们先激活,才能使用。

要激活具体的GPIO,也需要使用到一个特殊文件,那就是 /sys/class/gpio/export

如果要激活GPIO1,也就是/sys/class/gpio/gpio448,我们只需要执行下面的命令,即可激活:

echo 448 > /sys/class/gpio/export

该命令相当于告诉 /sys/class/gpio/export,请帮我激活gpio448

执行完该命令后,ls -l /sys/class/gpio,就可以看到gpio448存在了。

提醒:上述命令,需要在root权限下执行,否则执行时会提示没有权限。

 

激活GPIO0引脚后,我们还需要设置该引脚的功能,是输入,还是输出,那么使用下面的命令,操作/sys/class/gpio/gpio448/direction这个特殊文件即可:

如果是输出,也就是要输出高低电平,例如点亮LED,就使用:

echo out > /sys/class/gpio/gpio448/direction

上面的命令,就相当于告诉 /sys/class/gpio/gpio448/direction ,我要把你gpio448设置为out,也就是输出了。

 

如果是输入,例如接按键,获取按键状态,就使用:

echo in > /sys/class/gpio/gpio448/direction

上面的命令,就相当于告诉 /sys/class/gpio/gpio448/direction ,我要把你gpio448设置为in,也就是输入了。

 

设置好了GPIO0对应的功能后,我们就能具体使用了。

这个时侯,我们又要使用到 /sys/class/gpio/gpio448/value 这个特殊文件了。

例如,如果设置好输出,已经在GPIO0上接好了LED,现在要点亮LED了,就执行:

echo 1 > /sys/class/gpio/gpio448/value

这样就表示输出高电平

 

如果要熄灭对应的LED,就执行:

echo 0 > /sys/class/gpio/gpio448/value

这样就表示输出低电平了

 

如果设置好输入,在GPIO0上接好了普通按键,现在要获取按键输入状态,就执行:

cat /sys/class/gpio/gpio448/value

那么,显示1,表示按键按下;显示0,则表示按键松开。

 

在上面的讲解中,我们一共用到了下面的文件:

  • /sys/class/gpio/export:通知激活GPIO引脚对应的sys挂载点
  • /sys/class/gpio/gpio448/direction:通知该GPIO是输入in还是输出out
  • /sys/class/gpio/gpio448/value:输出或者输入高低电平

我们对这几个特殊文件的操作,也就是使用了基础的echo、cat指令,这样的指令,对任何一个普通文件,也可以操作。

所以本质上,对Linux而言,普通文件是文件,而这些GPIO挂载点也是文件,只不过,具体的功能有所不同罢了。

 

那么,如果你还懂一点bash脚本,会循环的,我命而已通过下面的方式,来闪烁GPIO0上连接的LED,具体指令如下:

# 激活GPIO0 - 448
echo 448 > /sys/class/gpio/export

# 设置GPIO0 - 448 为输出
echo out > /sys/class/gpio/gpio448/direction

# 循环10次:点亮LED,延时1秒,在关闭LED,再延时1秒
for i in 1 2 3 4 5 6 7 8 9 10
do
  echo 1 /sys/class/gpio/gpio448/value
  sleep 1
  echo 0 /sys/class/gpio/gpio448/value
  sleep 1
done

如果已经连接好LED到GPIO0,那么执行后,就能看到实际效果了。

小作业:参考上面的讲解,在控制一个LED的基础上,控制3个LED,类似下面的效果:

点灯视频

 

在上面的讲解中,我们在bash环境下,用echo来写入数据到对应的GPIO对应的文件中,从而操控LED引脚。

那么,到了C语言中,我们要如何操作呢?

实际上,也非常简单,你就把他们当作普通的文件,打开,然后写入或者读取内容就好了。

以下为一段通过GPIO0来闪烁LED的代码,基本功能和上面演示的bash脚本闪烁LED类似:

#include <stdlib.h>
#include <stdio.h>  
#include <string.h>
#include <unistd.h>
#include <fcntl.h>   //define O_WRONLY and O_RDONLY  

//芯片复位引脚: P1_16
#define SYSFS_GPIO_EXPORT           "/sys/class/gpio/export"  
#define SYSFS_GPIO_RST_PIN_VAL      "448"   
#define SYSFS_GPIO_RST_DIR          "/sys/class/gpio/gpio448/direction"
#define SYSFS_GPIO_RST_DIR_VAL      "OUT"  
#define SYSFS_GPIO_RST_VAL          "/sys/class/gpio/gpio448/value"
#define SYSFS_GPIO_RST_VAL_H        "1"
#define SYSFS_GPIO_RST_VAL_L        "0"

int main() 
{ 
    int fd; 

         //打开端口/sys/class/gpio# echo 448 > 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/gpio448# 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: Radio 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(1000000);
                   write(fd, SYSFS_GPIO_RST_VAL_L, sizeof(SYSFS_GPIO_RST_VAL_L));
                   usleep(1000000);
         }
         close(fd);

         printf("INFO: Radio hard reset pin value open error.\n");
         return 0;

}  

上面C代码的步骤,和我们在bash脚本中操作的步骤一致,都是操作前面说的三个特殊GPIO挂载点对应的文件。

  • 首先,是open了/sys/class/gpio/export,然后写入448,表示要激活GPIO0对应的sys挂载点
  • 其次,是open了/sys/class/gpio/gpio448/direction,写入了OUT,表示设置GPIO0为输入
  • 然后,是open了/sys/class/gpio/gpio448/value,准备循环写入0和1,来点亮和熄灭LED
  • 最后,是循环;再循环中,先写入1,点亮LED,然后延时1秒(usleep 1000000微秒),再写入0,熄灭LED,再延时1秒

编译该c代码,并执行,就能看到和之前bash脚本执行,同样的结果了。

提醒:上述编译结果的运行,需要在root权限下执行,否则执行时会提示没有权限。

小作业:参考上面的讲解,在控制一个LED的基础上,控制3个LED,达到类似上面bash脚本控制3个LED的效果。
 

通过上面的讲解,我们能够学习到,如果控制GPIO0,同样的方法,你也可以控制GPIO2、GPIO4等所有你能控制的GPIO,他们无非是不同的文件而已。

不管是在bash脚本中,还是在C语言、C++语言,或者是Python中,都可以按照同样的操作文件的方式来进行操作即可。

 

小作业:通过控制LED点亮和熄灭的时间间隔,来实现LED灯的渐亮渐灭效果,也就是呼吸灯效果。

本帖最后由 HonestQiao 于 2022-6-11 22:01 编辑

回复评论 (1)

学习了,一篇基础的教学,感谢乔帮主的分享!希望有更精彩呈现!
点赞  2022-6-13 08:34
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复