RK3576开发板GPIO驱动入门教程,基于触觉智能开发板教学

科创之家 2026-08-09 50人围观

一、GPIO基本概念

1.1什么是GPIO?

GPIO(General Purpose Input/Output,通用输入输出)是嵌入式系统中最基础的外设接口。每个GPIO引脚可以被配置为:

输入模式:读取外部信号(如按键、传感器

输出模式:控制外部设备(如LED、继电器)

中断模式:检测边沿变化触发中断

1.2 RK3576 GPIO资源

RK3576芯片共有5组GPIO(GPIO0~GPIO4),每组包含32个引脚(A0-A7, B0-B7, C0-C7, D0-D7)。

从设备树可以看到:

gpio0 = &gpio0; gpio1 = &gpio1; gpio2 = &gpio2; gpio3 = &gpio3; gpio4 = &gpio4;

1.3 GPIO命名规则

RK平台使用GPIOx_PYz的命名方式:

x: GPIO组号(0-4)

Y:端口(A、B、C、D)

z:引脚号(0-7)

例如:GPIO0_PB4表示GPIO0组的B端口第4号引脚

二、GPIO工作原理

2.1内部结构

图片

2.2工作模式

模式

说明

典型应用

输入模式

高阻态,读取外部电平

按键检测

输出模式

驱动外部电路

LED控制

中断模式

边沿/电平触发

事件通知

复用模式

作为其他功能(UART/I2C等)

引脚复用

三、GPIO设备树配置

3.1在设备树中添加GPIO节点

编辑设备树文件(如ido-evb7609-v1a.dtsi):

// 定义 LED 设备节点 / { leds: leds { status = "okay"; compatible = "gpio-leds"; pinctrl-names = "default"; pinctrl-0 = <&leds_gpio>; work_led: work { gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; }; hub_rst { gpios = <&gpio4 RK_PD0 GPIO_ACTIVE_HIGH>; default-state="on"; }; }; // USB 电源控制 cgpio:cgpios{ status = "okay"; compatible = "cust,cgpio"; usb_pwr { //SD-card、USB-A、Wi-Fi gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>; init-status = <1>; }; }; };

3.2 pinctrl引脚配置

&pinctrl { // LED 引脚配置 led_pins: led-pins { rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; }; // 按键引脚配置 key_pins: key-pins { rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>; }; };

3.3 GPIO上下拉配置详解

RK3576的GPIO支持多种上下拉配置,通过rockchip,pins属性的最后一个参数指定。

上下拉配置类型

配置

设备树值

说明

典型应用

上拉

&pcfg_pull_up

引脚默认上拉到高电平

按键输入、I2C SDA/SCL

下拉

&pcfg_pull_down

引脚默认下拉到低电平

复位信号、使能信号

无上下拉

&pcfg_pull_none

浮空输入,无上拉电阻

LED输出、GPIO输出

保持

&pcfg_pull_bus

总线保持器

低功耗场景

实际设备树配置示例

&pinctrl { /* LED 引脚 - 无上拉(输出模式不需要) */ leds_gpio: leds-gpio { rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>, /* work_led */ <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>; /* hub_rst */ }; /* USB 电源 - 无上拉 */ usb_pwr: usb-pwr { rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>; }; /* 耳机检测 - 上拉(按键检测) */ hp_det: hp-det { rockchip,pins = <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>; }; /* WiFi 唤醒 - 下拉 */ wifi_host_wake_irq: wifi-host-wake-irq { rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_down>; }; /* 按键输入 - 上拉 */ adc_keys_pins: adc-keys-pins { rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>; }; /* I2C 引脚 - 上拉(I2C 标准需要) */ i2c2m0_xfer: i2c2m0-xfer { rockchip,pins = <1 RK_PB3 9 &pcfg_pull_up>, /* i2c2_sda_m0 */ <1 RK_PB4 9 &pcfg_pull_up>; /* i2c2_scl_m0 */ }; };

上下拉选择原则

注意事项

1.输出模式:通常使用&pcfg_pull_none,因为GPIO输出是推挽驱动

2.输入按键:必须使用&pcfg_pull_up,确保按键未按下时读到高电平

3.I2C总线:必须使用&pcfg_pull_up,I2C是开漏输出需要上拉电阻

4.中断引脚:根据触发方式选择,上升沿触发用下拉,下降沿触发用上拉

四、GPIO驱动代码示例

4.1内核空间GPIO操作

#include #include #include struct my_gpio_dev { int gpio_led; int gpio_key; }; static int my_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct my_gpio_dev *priv; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* 从设备树获取 GPIO 描述符 */ priv->gpio_led = of_get_named_gpio(np, "led-gpios", 0); if (!gpio_is_valid(priv->gpio_led)) { dev_err(&pdev->dev, "Failed to get LED GPIO\n"); return priv->gpio_led; } /* 申请并配置 GPIO */ ret = devm_gpio_request_one(&pdev->dev, priv->gpio_led, GPIOF_OUT_INIT_LOW, "led"); if (ret) { dev_err(&pdev->dev, "Failed to request LED GPIO\n"); return ret; } /* 设置 GPIO 输出高电平(点亮 LED) */ gpio_set_value(priv->gpio_led, 1); /* 按键 GPIO 配置为输入 */ priv->gpio_key = of_get_named_gpio(np, "key-gpios", 0); ret = devm_gpio_request_one(&pdev->dev, priv->gpio_key, GPIOF_IN, "key"); if (ret) return ret; /* 读取 GPIO 输入值 */ int val = gpio_get_value(priv->gpio_key); dev_info(&pdev->dev, "Key value: %d\n", val); return 0; } static const struct of_device_id my_gpio_of_match[] = { { .compatible = "mycompany,my-gpio-device", }, { } }; static struct platform_driver my_gpio_driver = { .probe = my_gpio_probe, .driver = { .name = "my-gpio", .of_match_table = my_gpio_of_match, }, };

4.2 GPIO中断处理

static irqreturn_t gpio_irq_handler(intirq, void *dev_id) { struct my_gpio_dev *priv = dev_id; /* 消抖处理 */ priv->key_pressed = !priv->key_pressed; return IRQ_HANDLED; } static int setup_gpio_irq(struct platform_device *pdev, struct my_gpio_dev *priv) { int irq, ret; /* 将 GPIO 转换为 IRQ */ irq = gpio_to_irq(priv->gpio_key); if (irq < 0) return irq; /* 申请中断 */ ret = devm_request_irq(&pdev->dev, irq, gpio_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "gpio-key", priv); if (ret) dev_err(&pdev->dev, "Failed to request IRQ\n"); return ret; }

4.3用户空间GPIO操作(sysfs)

# 导出 GPIO(以 GPIO4_PA2 为例) # 计算 GPIO 编号:4 * 32 + 1 * 8 + 2 = 138 echo 138 > /sys/class/gpio/export # 设置方向为输出 echo out > /sys/class/gpio/gpio138/direction # 设置输出高电平(点亮 LED) echo 1 > /sys/class/gpio/gpio138/value # 设置输出低电平(熄灭 LED) echo 0 > /sys/class/gpio/gpio138/value # 释放 GPIO echo 138 > /sys/class/gpio/unexport

五、调试方法

5.1查看GPIO状态

# 查看 GPIO 占用情况 cat /sys/kernel/debug/gpio # 查看 pinmux 配置 cat /sys/kernel/debug/pinctrl/pinctrl-rockchip/pinmux-pins # 查看 pin 状态 cat /sys/kernel/debug/pinctrl/pinctrl-rockchip/pins

5.2常用调试命令

# 查看中断统计 cat /proc/interrupts | grep gpio # 使用 devmem 直接读取寄存器 devmem 0xFE740000 # GPIO0 基地址 # 使用 io 命令(如有) io -r -4 0xFE740000

六、常见问题

6.1 GPIO申请失败

检查GPIO是否已被其他驱动占用

查看设备树中的status = "disabled"是否未使能

6.2 GPIO输出无效

确认pinctrl配置正确

检查IO电压域是否正常

确认GPIO未被复用为其他功能

6.3 GPIO中断不触发

检查中断触发方式(上升沿/下降沿/双边沿/高电平/低电平)

确认GPIO已正确配置为输入

检查中断是否被正确映射

七、进阶学习

1.GPIO子系统:学习Linux的GPIO子系统架构

2.GPIO字符设备:使用/dev/gpiochip0接口

3.libgpiod:用户空间GPIO库

4.Device Tree:深入理解设备树语法和绑定文档

参考文档

[RKDocs/common/gpio/](RKDocs/common/gpio/)

[Documentation/devicetree/bindings/gpio/gpio.txt](kernel-6.1/Documentation/devicetree/bindings/gpio/gpio.txt)

[Documentation/gpio/gpio.txt](kernel-6.1/Documentation/gpio/gpio.txt)

本教程适用于触觉智能RK3576)开发板Purple Pi OH2 ,基于Android 14 / Linux 6.1内核

  • 随机文章
  • 热门文章
  • 热评文章
不容错过
Powered By Z-BlogPHP