GPIO 控制实战(libgpiod)
本篇目标:搞懂”引脚”怎么在软件里表示,并用手头的命令读取一个输入、控制一个输出、监视一个按键。新手做灯、继电器、按键检测,全靠它。
板子出厂固件已内置libgpiod工具,无需编译即可直接用。
什么是 GPIO
GPIO = 通用输入输出引脚。简单理解:芯片上那些”既能当输入(读外界高低电平)又能当输出(对外输出高低电平)”的脚。开发板上很多功能(点亮 LED、读取按键、控制继电器、片选信号)底层都是操作 GPIO。
全志 T153 把 GPIO 按”端口”分组命名:PA、PB、PC、PD …,每个端口最多 32 个脚,记为 PA0、PA1 … PD23 等。
板子上的 GPIO 与命名
设备树里一个 GPIO 这样写(你改设备树时会见到):
enable0-gpios = <&pio PA 20 GPIO_ACTIVE_LOW>; // 指 PA20,低电平有效
reset-gpios = <&pio PG 5 GPIO_ACTIVE_LOW>; // 指 PG5但在命令行(libgpiod)里,不用 PA20 这种名字,而用 gpiochip + 偏移号(offset)。这就引出一个新手最容易懵的点:怎么把 PA20 换成 offset?
引脚编号换算(重点)
当内核把 pio 暴露成单个 gpiochip 时,偏移号计算公式:
offset = (端口字母序号) × 32 + 引脚号
端口序号:A=0, B=1, C=2, D=3, E=4, F=5, G=6, H=7, I=8, J=9, K=10 ...举例:
| 引脚 | 计算 | offset |
|---|---|---|
PA0 |
0×32 + 0 | 0 |
PB4 |
1×32 + 4 | 36 |
PD23 |
3×32 + 23 | 119 |
PG5 |
6×32 + 5 | 197 |
PJ6 |
9×32 + 6 | 294 |
⚠️ 不同内核版本可能把各端口拆成多个 gpiochip(比如每个端口一个 chip)。所以上面算出来的 offset 是”单 chip 假设下”的算法。最稳妥的做法是先用下面的
gpiodetect/gpioinfo看清楚真实的 chip 名和行号,再操作,不要盲猜。
libgpiod 工具一览(已内置)
出厂固件已经带了这组工具(BR2_PACKAGE_LIBGPIOD_TOOLS):
| 命令 | 作用 |
|---|---|
gpiodetect |
列出系统里所有 GPIO 控制器(gpiochipN) |
gpioinfo |
查看某个 chip 的每一根线(line)信息 |
gpioget |
读取某根线电平(0/1) |
gpioset |
设置某根线电平(输出) |
gpiofind |
按”名字”查找 GPIO(前提是内核给线起了名) |
gpiomon |
监视某根线电平变化(边沿中断) |
第一步:看清有哪些控制器
root@YPT153EVB:~# gpiodetect
gpiochip0 [pio] (XX lines) # 通常 pio 是一个大 chiproot@YPT153EVB:~# gpioinfo gpiochip0 | head -20
gpiochip0 - 0: PA0 unused input active-high
gpiochip0 - 1: PA1 unused input active-high
...
gpiochip0 - 119: PD23 "backlight" output active-high # 举例:背光脚gpioinfo 能让你确认某根线当前被谁占用、是输入还是输出。动手前先看一眼,避免和别的功能打架。
实战一:读取一个输入脚(gpioget)
找一个空闲的输入脚接个按键/拨码开关(一端接 GPIO,一端接 GND;按下为低电平)。假设该脚换算后是 gpiochip0 的 line 119(仅举例,请按你实际接线算):
root@YPT153EVB:~# gpioget gpiochip0 119
1 # 松开时读到 1(高),按下读到 0(低)如果读出来是
0一直不变,检查:线是否被别的驱动占用(gpioinfo看 state)、接线是否接触好、是否有上拉。
实战二:控制一个输出脚(gpioset)
找一个空闲输出脚接个 LED(串限流电阻,LED 正极接 GPIO,负极接 GND;或反之看电平逻辑)。假设是 line 36(即 PB4,举例):
# 拉高(LED 亮,取决于硬件接法)
root@YPT153EVB:~# gpioset gpiochip0 36=1
# 拉低(LED 灭)
root@YPT153EVB:~# gpioset gpiochip0 36=0想让它闪几下:
root@YPT153EVB:~# for i in $(seq 1 5); do gpioset gpiochip0 36=1; sleep 0.3; gpioset gpiochip0 36=0; sleep 0.3; done
gpioset默认执行完就释放该线(回到输入)。如果想要”持续输出不释放”,加--mode=wait或--hold-period;一般点灯测试用默认即可。
实战三:监视一个按键(gpiomon)
不想自己轮询,可以让系统在有电平跳变时告诉你:
root@YPT153EVB:~# gpiomon gpiochip0 119
event: FALLING EDGE offset=119 timestamp=[...] # 按下(高->低)
event: RISING EDGE offset=119 timestamp=[...] # 松开(低->高)非常适合做”按键触发某动作”的脚本。
重要:这些脚已经被占用,别乱动
板子很多 GPIO 已经分配给具体外设,不要拿去当普通 GPIO 用,否则会干扰功能:
| 引脚 | 已被用作 |
|---|---|
| PA20 | RGB/HDMI 屏使能(display enable) |
| PD23 | 背光 PWM(pwm2_7) |
| PB9 / PB10 | 调试串口 uart0(TX/RX) |
| PB4 / PB5 | 用户串口 uart8 |
| PK6 / PK9 / PK10 / PK11 | 用户串口 uart3 |
| PG5 | 网口0 PHY 复位(gmac0) |
| PJ6 | 网口1 PHY 复位(gmac1) |
| PA3 | LT8912B(HDMI 桥片)复位 |
| PA21 / PA22 | 触摸屏 gt9xx 的复位/中断 |
要找”真正空闲可玩”的脚,请结合底板丝印和
gpioinfo(看unused)来选,并避开上面这些。
排障
| 现象 | 处理 |
|---|---|
gpiodetect 看不到 chip |
内核未开 GPIO 驱动;确认镜像 |
gpioget 报 Device or resource busy |
该线被别的驱动占用,换脚 |
| 输出不生效 | 确认是输出模式;确认硬件电平/接法 |
| 算的 offset 和实际对不上 | 用 gpioinfo 以真实 chip 为准,别盲算 |
附:真实设备树里的 GPIO 占用(佐证上面的占用表)
下面是从真实设备树里挑出的、与本板 GPIO 占用有关的片段,来源 BoardConfig/BoardType/ypt153evb-board.dts 与 board-base.dtsi:
// 屏使能(RGB 模式低有效 / HDMI 模式高有效)
enable0-gpios = <&pio PA 20 GPIO_ACTIVE_LOW>; // rgb_panel0
// 触摸屏 gt9xx
reset-gpios = <&pio PA 21 GPIO_ACTIVE_LOW>;
irq-gpios = <&pio PA 22 GPIO_ACTIVE_LOW>;
// 网口 PHY 复位
reset-gpios = <&pio PG 5 GPIO_ACTIVE_LOW>; // gmac0
reset-gpios = <&pio PJ 6 GPIO_ACTIVE_LOW>; // gmac1
// 背光使能(注意区别于屏使能 PA20)
enable-gpios = <&pio PD 22 GPIO_ACTIVE_HIGH>; // backlight0
// 串口引脚
pins = "PB9", "PB10"; // uart0(调试串口)
pins = "PB4", "PB5"; // uart8(用户串口)
// 背光 PWM 脚(见《PWM 实战》)
pins = "PD23"; function = "pwm2_7"; // pwm2_7改设备树时,若想把某个脚当普通 GPIO 用,先确认它不在上面这些节点里被占用(否则会和屏/网口/串口/背光冲突)。
最后编辑:陈柏林 更新时间:2026-11-05 14:56