2. GPIO控制¶
GPIO是General Purpose I/O的缩写,即通用输入输出端口,简单来说就是MCU/CPU可控制的引脚, 这些引脚通常有多种功能,最基本的是高低电平输入检测和输出,部分引脚还会与主控器的片上外设绑定, 如作为串口、I2C、网络、电压检测的通讯引脚。
Linux提供了GPIO子系统驱动框架,使用该驱动框架即可灵活地控制板卡上的GPIO。
2.1. GPIO性能¶
灌电流与拉电流为0-20mA可配置
2.2. GPIO命名¶
Allwinner Pin的ID按照 控制器端口(port)+索引序号(pin) 组成。H618的GPIO资源如下:
控制器端口有 PC、PF、PG、PH、PI、PL
控制器端口与具体控制器端口有关,比如PG就有20个索引序号
注意
下表多数引脚已经被系统使用,测试时请使用26Pin排针中引脚
控制器端口 |
索引序号(索引号) |
PC |
0,1,2,3,······,15,16 |
PF |
0,1,2,3,4,5,6 |
PG |
0,1,2,3,······,18,19 |
PH |
0,1,2,3,······,9,10 |
PI |
0,1,2,3,······,15,16 |
PL |
0,1 |
2.3. 引脚号计算¶
2.3.2. AllwinnerPin¶
AllwinnerPin是一款全志芯片引脚计算器软件。
获取方式: 网盘资料下载链接 -> 6-开发软件 -> AllwinnerPin.zip
正向计算(GPIO->引脚号):

反向计算(引脚号->GPIO):

超过最大引脚数量报错:

2.4. 使用GPIO sysfs接口控制IO¶
2.4.1. 命令行的方式¶
在Linux中,最常见的读写GPIO方式就是用GPIO sysfs interface, 是通过操作 /sys/class/gpio 目录下的 export 、 unexport 、gpio{N}/direction, gpio{N} /value (用实际引脚号替代{N})等文件实现的,经常出现shell脚本里面。 在kernel 4.8开始,加入了libgpiod的支持;而原有基于sysfs的访问方式,将被逐渐放弃。
引脚 |
控制器 |
索引号 |
计算结果 |
---|---|---|---|
PC3 |
PC |
3 |
67 (32 x 2 + 3) |
PG16 |
PG |
16 |
208 (32 x 6 + 16) |
PH7 |
PH |
7 |
231 (32 x 7 + 7) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #以下所有操作均需要打开管理者权限使用
#使能引脚PC3
echo 67 > /sys/class/gpio/export
#设置引脚为输入模式
echo in > /sys/class/gpio/gpio67/direction
#读取引脚的值
cat /sys/class/gpio/gpio67/value
#设置引脚为输出模式
echo out > /sys/class/gpio/gpio67/direction
#设置引脚为低电平
echo 0 > /sys/class/gpio/gpio67/value
#设置引脚为高电平
echo 1 > /sys/class/gpio/gpio67/value
#复位引脚
echo 67 > /sys/class/gpio/unexport
|
2.5. 使用libgpiod控制IO¶
从Linux 4.8版本开始,Linux引入了新的gpio操作方式,GPIO字符设备。libgpiod是一种字符设备接口,GPIO访问控制是通过操作字符设备文件(比如 /dev/gpiodchip0 )实现的, 并通过libgpiod提供一些命令工具、c库以及python封装。
想要使用libgpiod,需要在板卡上安装libgpiod库。
1 2 | #安装gpiod 命令行工具
sudo apt install -y gpiod
|
gpiod工具的使用方法与sysfs接口的不同,gpiod是以控制器为单位,然后再详细到端口号和索引号,即gpiod使用两个数据确定引脚
引脚 |
控制器 |
索引号 |
计算结果 |
---|---|---|---|
PC3 |
PC |
3 |
67 (32 x 2 + 3) |
PG16 |
PG |
16 |
208 (32 x 6 + 16) |
PH7 |
PH |
7 |
231 (32 x 7 + 7) |
重要
Allwinner Pin的ID按照 控制器端口(port)+索引序号(pin) 组成。其中端口号和索引号会合并成一个数值传入到gpiod里去,并不是所有的引脚都能够使用libgpiod控制,因为有些引脚已经被系统占用。
常用的命令行如下,可使用 -h 查看命令相对应的使用说明,-h 查看版本信息。
2.5.1. gpiodetect¶
gpiodetect 命令用于列出系统上存在的所有 gpiochip,以及它们的名称、标签和 GPIO lines。
语法:
1 | gpiodetect [OPTIONS]
|
选项[OPTIONS]:
-h, –help :查看帮助并退出
-v, –version :查看版本信息并退出
示例:
1 2 3 | cat@lubancat:~$ sudo gpiodetect
gpiochip0 [7022000.pinctrl] (32 lines)
gpiochip1 [300b000.pinctrl] (288 lines)
|
三列数据分别是 gpiochip 的名称(name)、标签(label)和行数(lines)。其中 gpiochip1
为后面需要用到的GPIO 控制器组。
2.5.2. gpioinfo¶
gpioinfo 命令用于列出指定 gpiochip 的所有 line,以及它们的名称、使用者、方向、活动状态和其他标志。
语法:
1 | gpioinfo [OPTIONS] <gpiochip> ...
|
选项[OPTIONS]:
-h, –help :查看帮助并退出
-v, –version :查看版本信息并退出
参数:
<gpiochip>:指定 gpiochip,如 gpiochip0、gpiochip1,可同时输入多个参数。如果没有参数,则查询所有 gpiochip 的所有 line 的信息。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #查询gpiochip1
cat@lubancat:~$ sudo gpioinfo gpiochip1
gpiochip1 - 288 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed unused input active-high
······
#可简化成sudo gpioinfo 1
cat@lubancat:~$ sudo gpioinfo 1
gpiochip1 - 288 lines:
line 0: unnamed unused input active-high
line 1: unnamed unused input active-high
line 2: unnamed unused input active-high
······
|
2.5.3. gpioset¶
gpioset 命令用于设置指定的 GPIO line 的值。
语法:
1 | gpioset [OPTIONS] <chip name/number> <offset1>=<value1> <offset2>=<value2> ...
|
选项[OPTIONS]:
-l, –active-low :设置低电平为有效电平
-B, –bias=[as-is|disable|pull-down|pull-up] :设置 bias(默认使用 as-is)
-D, –drive=[push-pull|open-drain|open-source] :设置驱动模式(默认使用 push-pull)
-m, –mode=[exit|wait|time|signal] :设置完成后的动作模式
-s, –sec=SEC :当使用 –mode=time 选项时,指定等到的时间(单位:秒)
-u, –usec=USEC :当使用 –mode=time 选项时,指定等到的时间(单位:微秒)
-b, –background :设置完成后与控制终端分离
-h, –help :查看帮助并退出
-v, –version :查看版本信息并退出
参数:
<chip name/number>:指定 gpiochip,要使用板卡GPIO时需指定为gpiochip1/1。
<offset>:行内偏移量,这里参数应该为上述GPIO的计算结果,如PC3引脚为67。
<value>:参数为0和1,当高电平为有效电平(默认)时,1为高,0为低。
示例:
1 2 3 4 5 | #设置PF6、PC3、PH7为高电平
cat@lubancat:~$ sudo gpioset 1 166=1 67=1 231=1
#设置PF6、PC3、PH7为低电平
cat@lubancat:~$ sudo gpioset 1 166=0 67=0 231=0
|
2.5.4. gpioget¶
gpioget 命令用于读取指定 GPIO line 的值。
语法:
1 | gpioget [OPTIONS] <chip name/number> <offset 1> <offset 2> ...
|
选项[OPTIONS]:
-l, –active-low :设置低电平为有效电平
-B, –bias=[as-is|disable|pull-down|pull-up] :设置 bias(默认使用 as-is)
-h, –help :查看帮助并退出
-v, –version :查看版本信息并退出
参数:
<chip name/number>:指定 gpiochip,要使用板卡GPIO时需指定为gpiochip1/1。
<offset>:行内偏移量,这里参数应该为上述GPIO的计算结果,如PC3引脚为67。
示例:
1 2 3 | #查看PF6、PC3、PH7引脚电平
cat@lubancat:~$ sudo gpioget 1 166 67 231
1 1 1
|
2.5.5. gpiomon¶
gpiomon 命令用于等待指定 GPIO line 上的事件,或指定要监视的事件。
语法:
1 | gpiomon [OPTIONS] <chip name/number> <offset 1> <offset 2> ...
|
选项[OPTIONS]:
-l, –active-low :设置低电平为有效电平
-B, –bias=[as-is|disable|pull-down|pull-up] :设置 bias(默认使用 as-is)
-n, –num-events=NUM :处理完 NUM 个事件后退出
-s, –silent :不打印事件信息
-r, –rising-edge :只处理上升沿事件
-f, –falling-edge :只处理下降沿事件
-b, –line-buffered :将标准输出设置为行缓冲
-F, –format=FMT :指定输出格式(%o 为 GPIO 行内偏移量,%e 为事件类型,%s 为事件时间戳秒数部分,%n 为事件时间戳纳秒部分)
-h, –help :查看帮助并退出
-v, –version :查看版本信息并退出
参数:
<chip name/number>:指定 gpiochip,要使用板卡GPIO时需指定为gpiochip1/1。
<offset>:行内偏移量,这里参数应该为上述GPIO的计算结果,如PC3引脚为67。
示例:
1 2 3 4 5 6 7 8 | #监控PF6、PC3、PH7引脚
cat@lubancat:~$ sudo gpiomon 1 166 67 231
event: RISING EDGE offset: 166 timestamp: [1706690108.777860215]
event: RISING EDGE offset: 67 timestamp: [1706690108.778006581]
event: FALLING EDGE offset: 67 timestamp: [1706690128.227108044]
event: RISING EDGE offset: 67 timestamp: [1706690130.955410448]
event: FALLING EDGE offset: 231 timestamp: [1706690155.876559287]
event: RISING EDGE offset: 231 timestamp: [1706690161.451626611]
|
2.6. C程序点灯¶
下面点灯程序以调用libgpiod库为例,演示板卡为LubanCat-A1。
2.6.1. 代码获取¶
1 2 3 4 5 6 | #如之前有获取则可跳过
#获取仓库
git clone https://gitee.com/LubanCat/lubancat_allwinner_code_storage
#代码所在的位置
lubancat_allwinner_code_storage/quick_start/gpio
|
2.6.2. 安装libgpiod库¶
1 | sudo apt install libgpiod-dev
|
2.6.3. 示例代码¶
代码较长复制粘贴容易乱序,可以下载我们提供的源码 led.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 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 | #include <stdio.h>
#include <unistd.h>
#include <gpiod.h>
#define msleep(t) usleep((t)*1000)
int main(int argc, char const *argv[])
{
struct gpiod_chip *gpiochip1;
struct gpiod_line *led;
struct gpiod_line_request_config config;
int req;
/* 板载LED灯:PF6=(F-1)*32+6=(6-1)*32+6=166 */
int PF6=166;
/* 打开 GPIO 控制器 */
gpiochip1 = gpiod_chip_open("/dev/gpiochip1");
if (!gpiochip1)
{
printf("gpiochip open error\n");
return -1;
}
/* 获取PF6引脚 */
led = gpiod_chip_get_line(gpiochip1, PF6);
if (!led)
{
gpiod_chip_close(gpiochip1);
printf("led get error.\n");
return -1;
}
/* 配置引脚 输出模式 name为“led666” 初始电平为high*/
req = gpiod_line_request_output(led, "led666", 1);
if (req)
{
fprintf(stderr, "led request error.\n");
return -1;
}
while (1)
{
/* 设置引脚电平 */
gpiod_line_set_value(led, 1);
printf("led close\n"); //板载LED为低电平触发
msleep(500);
gpiod_line_set_value(led, 0);
printf("led open\n");
msleep(500);
}
return 0;
}
|
2.6.4. 编译运行¶
1 2 3 4 | #编译,需要指明所需静态链接库,加上“-lgpiod”或者“-l gpiod”都行
gcc led.c -o led -lgpiod
#运行
./led
|
演示效果如下:

小技巧
此时板载LED灯会以1秒为闪烁周期进行闪烁
2.7. FAQs¶
- Q1:当使用GPIO时出现
gpioset: error setting the GPIO line values: Device or resource busy
或者-bash: echo: 写错误: 设备或资源忙
A1:说明GPIO被占用了,占用的原因可能是设备树里把该引脚作为gpio或者其他复用功能被使用了。