刚拿到ArmSoM-W3开发板时,我最先折腾的就是GPIO控制。作为嵌入式开发的"Hello World",GPIO操作看似简单,但要在RK3588上玩转,还得先摸清它的脾气。这块芯片的GPIO控制器设计挺有意思,总共5个bank,每个bank管理32个IO,编号规则是控制器(bank)+端口(port)+引脚序号(pin)。比如GPIO0_C0,就是bank0的C端口第0个引脚。
实际开发中踩的第一个坑是复用功能配置。记得有次调试PWM,死活不出波形,后来发现是引脚复用没设对。RK3588的引脚可以复用成多种功能,比如GPIO0_C0这个脚,既能当普通GPIO用,也能配置为PWM1_M0、I2C2_SDA_M0等。在dts里配置时,rockchip,pins那行最后的数字就是功能选择,3表示GPIO模式,其他数字对应不同功能。
新手常问的问题是如何计算GPIO编号。sysfs接口用的编号公式是:(bank-1)32 + port8 + pin。比如GPIO1_D3对应的编号就是(1-1)32 + 38 + 3 = 27。不过现在更推荐用libgpiod库,它直接用bank和pin定位,省去计算的麻烦。
给自定义外设配GPIO时,得先在dts里声明。我习惯在arch/arm64/boot/dts/rockchip/rk3588s-armsom-w3.dts里添加节点,保持与板级dts隔离。一个完整的GPIO配置包含三要素:
最近做传感器项目时,发现上拉电阻配置很关键。测量按键信号时用&pcfg_pull_up,而I2C总线最好设成&pcfg_pull_none。曾有个诡异现象:I2C设备时好时坏,最后发现是GPIO误配了上拉。
以控制LED为例,完整dts配置如下:
c复制&pinctrl {
leds {
led_work: led-work {
rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
/ {
gpio-leds {
compatible = "gpio-leds";
work_led: work-led {
label = "work";
gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
};
};
调试时遇到过gpio-leds驱动不生效的情况,后来发现是没在Makefile里配置CONFIG_LEDS_GPIO。建议在kernel菜单里勾选:
code复制Device Drivers
-> LED Support
-> LED Support for GPIO connected LEDs
在写字符设备驱动时,获取GPIO的经典做法:
c复制#include <linux/gpio/consumer.h>
struct gpio_desc *sensor_irq;
sensor_irq = gpiod_get(&pdev->dev, "irq", GPIOD_IN);
if (IS_ERR(sensor_irq)) {
dev_err(&pdev->dev, "Failed to get IRQ GPIO\n");
return PTR_ERR(sensor_irq);
}
gpiod_get的第三个参数特别重要:
曾经调试触摸屏时,中断死活不触发,最后发现是漏了gpiod_direction_input()调用。其实gpiod_get()已经设置了方向,但有些老驱动还保留显式设置。
GPIO中断配置代码模板:
c复制irq = gpiod_to_irq(sensor_irq);
ret = request_irq(irq, sensor_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"sensor_irq", NULL);
关键点:
实测发现RK3588的中断响应延迟在微秒级,比软件轮询效率高两个数量级。但要注意避免在中断上下文做耗时操作,曾经因为打印调试信息导致系统卡死。
虽然老套但直接有效,操作流程:
bash复制# 导出GPIO
echo 27 > /sys/class/gpio/export
# 设置方向
echo out > /sys/class/gpio/gpio27/direction
# 输出高电平
echo 1 > /sys/class/gpio/gpio27/value
写了个自动化脚本gpio-ctl.sh:
bash复制#!/bin/bash
GPIO=$1
ACTION=$2
if [ ! -d "/sys/class/gpio/gpio$GPIO" ]; then
echo $GPIO > /sys/class/gpio/export
sleep 0.1
fi
echo $ACTION > /sys/class/gpio/gpio$GPIO/direction
使用时直接./gpio-ctl.sh 27 out,比手动操作省事多了。不过要注意权限问题,普通用户可能需要sudo。
新项目建议用libgpiod,示例代码:
c复制#include <gpiod.h>
struct gpiod_chip *chip;
struct gpiod_line *line;
chip = gpiod_chip_open("/dev/gpiochip0");
line = gpiod_chip_get_line(chip, 27);
gpiod_line_request_output(line, "example", 0);
gpiod_line_set_value(line, 1);
优势很明显:
实测性能比sysfs高10倍以上,特别适合高频操作。配套工具gpiodetect、gpioinfo调试起来也很方便。
最头疼的问题莫过于GPIO死活不按预期工作。我的排查清单:
有个记忆深刻的案例:配置成输出的GPIO始终为高阻态,最后发现是电源域没打开。RK3588的GPIO分属不同电源域,在dts里要确保相关power-domain节点状态正常。
性能优化方面,GPIO批量操作能显著提升效率。比如用gpiod_line_set_value_bulk()同时控制多个GPIO,比单个操作快5倍。中断处理中可以用gpiod_line_get_value_bulk()快速读取多个输入状态。