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 是一个大 chip
root@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 用,先确认它不在上面这些节点里被占用(否则会和屏/网口/串口/背光冲突)。

作者:Mai  创建时间:2026-10-20 16:46
最后编辑:陈柏林  更新时间:2026-11-05 14:56
上一篇:
下一篇: