7. GPIO控制

GPIO是General Purpose I/O的缩写,即通用输入输出端口,简单来说就是MCU/CPU可控制的引脚, 这些引脚通常有多种功能,最基本的是高低电平输入检测和输出,部分引脚还会与主控器的片上外设绑定, 如作为串口、I2C、网络、电压检测的通讯引脚。

Linux提供了GPIO子系统驱动框架,使用该驱动框架即可灵活地控制板子上的GPIO。

7.1. 方式一:使用GPIO sysfs接口控制IO

在Linux中,最常见的读写GPIO方式就是用GPIO sysfs interface, 是通过操作 /sys/class/gpio 目录下的 exportunexportgpio{N}/direction, gpio{N} /value (用实际引脚号替代{N})等文件实现的,经常出现shell脚本里面。 在kernel 4.8开始,加入了libgpiod的支持;而原有基于sysfs的访问方式,将被逐渐放弃。

使用方式请参考以下链接: 《野火Linux基础教程-控制蜂鸣器》

7.2. 方式二:使用libgpiod控制IO

libgpiod是一种字符设备接口,GPIO访问控制是通过操作字符设备文件(比如 /dev/gpiodchip0 )实现的, 并通过libgpiod提供一些命令工具、c库以及python封装。

想要使用libgpiod,需要在开发板上安装libgpiod库。

1
2
3
4
5
 #安装libgpiod库及头文件
 sudo apt install  libgpiod-dev

 #安装gpiod 命令行工具
 sudo apt install gpiod

7.2.1. 命令行控制

常用的命令行如下,可使用 -h 查看命令相对应的使用说明

libgpiod命令

命令

作用

使用举例

说明

gpiodetect

列出所有的GPIO控制器

gpiodetect(无参数)

列出所有的GPIO控制器

gpioinfo

列出gpio控制器的引脚情况

gpioinfo 4

列出gpio4控制器引脚组情况

gpioset

设置gpio

gpioset 4 19=0

设置gpio4组编号19引脚为低电平

gpioget

获取gpio引脚状态

gpioget 4 1

获取gpio4组编号1的引脚状态

gpiomon

监控gpio的状态

gpiomon 4 1

监控gpio4组编号1的引脚状态

重要

不是所有的引脚都能够使用libgpiod控制,例如led之类的一些已经被使用的引脚。

7.2.2. 使用libgpiod编程

若想要在PC上交叉编译出能运行在板子的应用程序,交叉链接时使用的libgpiod库要与开发板的匹配, 可以直接把开发板的库拷贝到PC上链接使用。

开发板安装好ligpiod-dev后,可以通过以下命令找到具体的头文件和库文件:

1
2
3
4
5
6
 # 在开发板上查找libgpiod库
 dpkg -L libgpiod-dev
 # 以下是输出
 /usr/include/gpiod.h
 /usr/lib/arm-linux-gnueabihf/libgpiod.a
 /usr/lib/arm-linux-gnueabihf/libgpiod.so

查找结果中的gpiod.h、libgpiod.so和libgpiod.a就是开发板使用的头文件、动态和静态链接库, 它是debian 10 buster默认apt安装的版本。

为方便使用,我们已拷贝到配套代码中:debian buster 的libgpiod头文件和库

常用的libgpiod API(C库)如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 //成员变量
 struct gpiod_chip;      //GPIO组句柄
 struct gpiod_line;      //GPIO引脚句柄

 //获取GPIO控制器(GPIO组)
 struct gpiod_chip *gpiod_chip_open(const char *path);

 //获取GPIO引脚
 struct gpiod_line * gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset);

 //设置引脚方向为输入模式
 int gpiod_line_request_input(struct gpiod_line *line,const char *consumer);

 //设置引脚为输出模式
 int gpiod_line_request_output(struct gpiod_line *line,const char *consumer, int default_val)

 //设置引脚的高低电平
 int gpiod_line_set_value(struct gpiod_line *line, int value);

 //读取引脚状态
 int gpiod_line_get_value(struct gpiod_line *line);

 //释放GPIO引脚
 void gpiod_line_release(struct gpiod_line *line);

 //关闭GPIO组句柄并释放所有分配的资源。
 void gpiod_chip_close(struct gpiod_chip *chip);

根据上面libgpiod提供API,编写了以下控制蜂鸣器的程序(蜂鸣器的所在的引脚为PC13),其源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 #include <gpiod.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>

 int main(int argc, char **argv)
 {
     int i;
     int ret;

     struct gpiod_chip * chip;      //GPIO控制器句柄
     struct gpiod_line * line;      //GPIO引脚句柄

     /*获取GPIO控制器*/
     chip = gpiod_chip_open("/dev/gpiochip2");
     if(chip == NULL)
     {
         printf("gpiod_chip_open error\n");
         return -1;
     }

     /*获取GPIO引脚*/
     line = gpiod_chip_get_line(chip, 13);
     if(line == NULL)
     {
         printf("gpiod_chip_get_line error\n");
         goto release_line;
     }

     /*设置GPIO为输出模式*/
     ret = gpiod_line_request_output(line,"beep",0);
     if(ret < 0)
     {
         printf("gpiod_line_request_output error\n");
         goto release_chip;
     }

     for(i = 0;i<10;i++)
     {
         gpiod_line_set_value(line,1);
         usleep(500000);  //延时0.5s
         gpiod_line_set_value(line,0);
         usleep(500000);
     }

 release_line:
     /*释放GPIO引脚*/
     gpiod_line_release(line);

 release_chip:
     /*释放GPIO控制器*/
     gpiod_chip_close(chip);


     return 0;
 }

编译程序时需指定libgpiod库及路径头文件路径(不使用交叉编译时并不需要指定路径)。

1
2
3
4
5
 #方式一:手动编译,需要指定libgpiod库的头文件路径以及动态库路径
 arm-linux-gnueabihf-gcc beep beep.c  -lgpiod -L./libgpiod/ -I./libgpiod/

 #方式二:直接用本文档配套的代码,它包含了makefile,在Source/libgpio_beep目录下直接通过make编译
 make