设备树之GPIO
2026/8/26 17:45:03 网站建设 项目流程

1、示例全景图

┌──────────────────────────────────────────────
│ 物理芯片上有 16 个引脚:PA0 ~ PA15 │
│ Pin Controller 决定:PA0 今天是 GPIO?还是 UART?还是 SPI? │
│ GPIO Controller 决定:PA0 既然是 GPIO,那现在是高电平还是低电平? │
└───────────────────────────────────────────────

2、Pin Controller 节点

pinctrl: pinctrl@50000000 { compatible = "vendor,stm32-pinctrl"; reg = <0x50000000 0x4000>; /* 定义一组引脚配置:PA0~PA3 配成 GPIO 模式 */ gpioa_pins: gpioa-pins { pins = "PA0", "PA1", "PA2", "PA3"; function = "gpio"; /* ← 身份:GPIO */ drive-strength = <8>; /* 驱动能力 */ }; /* 另一组:PA4~PA5 配成 UART 模式 */ uart1_pins: uart1-pins { pins = "PA4", "PA5"; function = "uart1_tx_rx"; /* ← 身份:UART */ bias-pull-up; /* 内部上拉 */ }; };

通俗说:

这是"引脚户口本管理处"。gpioa_pins给 PA0~PA3 发了GPIO 身份证uart1_pins给 PA4~PA5 发了UART 身份证。同一个引脚不能同时领两个身份。

3、GPIO Controller 节点

gpioa: gpio@50010000 { compatible = "vendor,stm32-gpio"; reg = <0x50010000 0x400>; gpio-controller; /* ← 我是 GPIO 控制器 */ #gpio-cells = <2>; /* 引用我需要 2 个 cell */ /* 关键!告诉内核:我的 0~15 号线 = pinctrl 的 PA0~PA15 */ gpio-ranges = <&pinctrl 0 0 16>; ngpios = <16>; };

通俗说:

这是"灯的开关总控台"。它有 16 个开关(0~15 号线)。但它不知道这些开关对应哪个物理引脚,所以通过gpio-ranges去问 Pin Controller:"我的 0 号线对应你的 PA0,1 号线对应 PA1……"

| 数字 | 值 | 含义 | | ---------- | ------- | ------------------------------- | | `&pinctrl` | 引用 | 找这个 Pin Controller 要引脚 | | `0` | GPIO 偏移 | 从 GPIO 控制器的第 0 号线开始 | | `0` | Pin 偏移 | 对应 Pin Controller 的第 0 号引脚(PA0) | | `16` | 数量 | 连续映射 16 条线(0~15 → PA0~PA15) |

Pin 偏移芯片封装上物理引脚的全局编号(PA0=0, PA1=1, PB0=16...)

  • Pin 偏移(Index)‌:在pinctrl中使用,直接对应硬件寄存器的位位置(0-15)。
  • GPIO 偏移(Base/Number)‌:在gpio子系统使用,可能涉及全局编号计算(例如 GPIOA 的基号是 0,GPIOB 的基号是 16,那么 PB5 的全局 GPIO 号是 16+5=21)。
/dts-v1/; #include "stm32mp157.dtsi" #include "stm32mp15xxac-pinctrl.dtsi" / { model = "STM32MP157 Demo Board"; compatible = "st,stm32mp157-demo", "st,stm32mp157"; /* ============================================================ * 【GPIO 控制器提供者 - Provider】 * 驱动代码中通过 devm_gpiochip_add_data() 向内核注册 * 消费者通过 <&my_gpio_expander N flags> 引用其中的 GPIO * ============================================================ */ my_gpio_expander: gpio-expander@20 { compatible = "vendor,my-gpio-exp"; reg = <0x20>; /* I2C 地址 0x20 */ status = "okay"; /* 声明本节点是一个 GPIO 控制器 */ gpio-controller; #gpio-cells = <2>; /* 消费者引用格式: <&ctrl pin flags> */ ngpios = <8>; /* 该芯片提供 8 个 GPIO */ }; /* ============================================================ * 【消费者设备 - Consumer】 * pinctrl: 驱动中用 devm_pinctrl_get_select_default() 获取并应用 * GPIO: 驱动中用 devm_gpiod_get(dev, "reset", ...) 获取 * ============================================================ */ my_device { compatible = "vendor,mydev"; status = "okay"; /* pinctrl 状态定义 */ pinctrl-names = "default", "sleep"; pinctrl-0 = <&mydev_pins_default>; /* 默认状态 */ pinctrl-1 = <&mydev_pins_sleep>; /* 低功耗状态 */ /* GPIO 消费者属性 */ reset-gpios = <&gpioc 6 GPIO_ACTIVE_LOW>; /* devm_gpiod_get(dev, "reset", ...) */ done-gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>; /* devm_gpiod_get(dev, "done", ...) */ }; /* ============================================================ * 【另一个消费者:LED】 * 引用外部 GPIO 控制器(my_gpio_expander)的第 2 号引脚 * 驱动中通过 devm_gpiod_get() 或 gpio-leds 子系统获取 * ============================================================ */ leds { compatible = "gpio-leds"; status_led { /*gpios(没有前缀)用 devm_gpiod_get(dev, NULL, flags) 获取。*/ /*内核已经有 drivers/leds/leds-gpio.c,开机自动会:*/ /*echo 1 > /sys/class/leds/exp-led2/brightness*/ /*echo 0 > /sys/class/leds/exp-led2/brightness*/ gpios = <&my_gpio_expander 2 GPIO_ACTIVE_HIGH>; label = "exp-led2"; default-state = "on"; }; }; }; /* ============================================================ * 【pinctrl 引脚配置】 * 驱动中通过 devm_pinctrl_get_select_default() 自动应用 default 状态 * 通过 pinctrl_lookup_state() + pinctrl_select_state() 手动切换 sleep 状态 * ============================================================ */ &pinctrl { mydev_pins_default: mydev-pins-default { pins1 { pinmux = <STM32_PINMUX('C', 6, GPIO)>; /* PC6 = reset */ drive-push-pull; bias-disable; slew-rate = <0>; }; pins2 { pinmux = <STM32_PINMUX('A', 13, GPIO)>; /* PA13 = done */ drive-push-pull; bias-disable; }; }; mydev_pins_sleep: mydev-pins-sleep { pins { pinmux = <STM32_PINMUX('C', 6, ANALOG)>, <STM32_PINMUX('A', 13, ANALOG)>; }; }; };
#include <linux/module.h> #include <linux/platform_device.h> #include <linux/pinctrl/consumer.h> #include <linux/gpio/consumer.h> struct mydev_priv { struct pinctrl *pinctrl; struct pinctrl_state *sleep_state; struct gpio_desc *reset; struct gpio_desc *done; }; static int mydev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mydev_priv *priv; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* ========== 核心 API 1:获取并应用 pinctrl "default" 状态 ========== */ priv->pinctrl = devm_pinctrl_get_select_default(dev); if (IS_ERR(priv->pinctrl)) return PTR_ERR(priv->pinctrl); /* 可选:查找 sleep 状态,供后续电源管理切换 */ priv->sleep_state = pinctrl_lookup_state(priv->pinctrl, "sleep"); /* ========== 核心 API 2:获取 GPIO 消费者 ========== */ priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(priv->reset)) return PTR_ERR(priv->reset); priv->done = devm_gpiod_get(dev, "done", GPIOD_IN); if (IS_ERR(priv->done)) return PTR_ERR(priv->done); /* 使用 GPIO */ gpiod_set_value(priv->reset, 0); /* 拉低复位 */ msleep(10); gpiod_set_value(priv->reset, 1); /* 释放复位 */ platform_set_drvdata(pdev, priv); dev_info(dev, "mydev probed ok\n"); return 0; } /* ========== 手动切换 pinctrl 状态(进入/退出睡眠) ========== */ static int mydev_suspend(struct device *dev) { struct mydev_priv *priv = dev_get_drvdata(dev); if (!IS_ERR_OR_NULL(priv->sleep_state)) pinctrl_select_state(priv->pinctrl, priv->sleep_state); return 0; } static int mydev_resume(struct device *dev) { struct mydev_priv *priv = dev_get_drvdata(dev); struct pinctrl_state *def; def = pinctrl_lookup_state(priv->pinctrl, "default"); if (!IS_ERR_OR_NULL(def)) pinctrl_select_state(priv->pinctrl, def); return 0; } static const struct dev_pm_ops mydev_pm_ops = { .suspend = mydev_suspend, .resume = mydev_resume, }; static const struct of_device_id mydev_of_match[] = { { .compatible = "vendor,mydev" }, {} }; MODULE_DEVICE_TABLE(of, mydev_of_match); static struct platform_driver mydev_driver = { .probe = mydev_probe, .driver = { .name = "mydev", .of_match_table = mydev_of_match, .pm = &mydev_pm_ops, }, }; module_platform_driver(mydev_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Demo Consumer Device");
my_device { /* 区域 1:pinctrl 状态(一级节点) */ pinctrl-names = "default"; pinctrl-0 = <&mydev_pins_default>; /* 区域 2:GPIO 消费者属性(和 gpiod API 对应) */ reset-gpios = <&gpioc 6 GPIO_ACTIVE_LOW>; // ← devm_gpiod_get(dev, "reset", ...) led-gpios = <&gpioa 13 GPIO_ACTIVE_HIGH>; // ← devm_gpiod_get(dev, "led", ...) }; &pinctrl { /* 区域 3:pinctrl 配置(二级节点) */ mydev_pins_default: mydev-pins-default { // ← 一级状态节点 pins { // ← 二级配置节点 pinmux = <STM32_PINMUX('C', 6, GPIO)>; bias-disable; }; }; };

总结

你在设备树看到属于哪个世界驱动里怎么操作
pinctrl-0 = <&xxx>世界 Adevm_pinctrl_get_select_default(dev)
pinctrl-names = "sleep"世界 A

pinctrl_lookup_state()

pinctrl_select_state()

pins { pinmux = ... }世界 C无 API,内核自动解析
reset-gpios = <...>世界 Bdevm_gpiod_get(dev, "reset", ...)
led-gpios = <...>世界 Bgpiod_set_value(led, 1/0)
类型设备树写法内核消费者你需要写驱动吗
LED`compatible = "gpio-leds"``drivers/leds/leds-gpio.c`❌ 不用写
按键`compatible = "gpio-keys"``drivers/input/keyboard/gpio_keys.c`❌ 不用写
固定电压`compatible ="regulator-fixed"drivers/regulator/fixed.c❌ 不用写
简单 PWM`compatible ="pwm-leds"drivers/leds/leds-pwm.c❌ 不用写
看门狗 GPIO`compatible ="gpio-wdt"drivers/watchdog/gpio_wdt.c❌ 不用写
厂商自定义`compatible = "vendor,mydev"`你自己写的 `mydev.c`✅ 必须写

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询