当前位置: 首页 > news >正文

Linux学习笔记--GPIO子系统和PinCtrl子系统

1. 设备树节点

1.1 PinCtrl

作用:模拟硬件引脚控制器,管理引脚复用和配置

virtual_pincontroller {compatible = "100ask,virtual_pinctrl";  // 匹配虚拟pinctrl驱动myled_pin: myled_pin {                  // 引脚配置节点,标签为myled_pinfunctions = "gpio";                 // 引脚功能:GPIO模式groups = "pin0";                    // 引脚组:pin0configs = <0x11223344>;             // 电气特性配置值};
};

对应驱动:需要实现 100ask,virtual_pinctrl 兼容的pinctrl驱动

1.2 GPIO控制器 

gpio_virt: virtual_gpiocontroller {compatible = "100ask,virtual_gpio";  // 匹配虚拟GPIO驱动gpio-controller;                     // 声明这是GPIO控制器#gpio-cells = <2>;                   // GPIO说明符有2个cellngpios = <4>;                        // 提供4个GPIO
};

作用:提供GPIO操作接口

关键属性

  • #gpio-cells = <2>:引用此GPIO时需要2个参数

    • 参数1:GPIO编号(0-3)

    • 参数2:GPIO标志(如GPIO_ACTIVE_LOW)

对应驱动:需要实现 100ask,virtual_gpio 兼容的GPIO驱动

1.3 LED设备 

myled {compatible = "100ask,leddrv";           // 匹配LED驱动led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>;  // 引用GPIO控制器pinctrl-names = "default";              // 引脚状态名称pinctrl-0 = <&myled_pin>;               // 引用引脚配置
};

作用:具体的LED设备,使用前面定义的资源

关键引用

  • &gpio_virt:引用GPIO控制器标签

  • &myled_pin:引用引脚配置标签

2. 节点关系图

virtual_pincontroller          gpio_virt↓                              ↓myled_pin                    GPIO控制器↓                              ↓pinctrl-0 ────────┐               ↓↓               ↓myled (LED设备) ──┘↓led-gpios引用

4. 交互流程

4.1 系统启动和驱动加载顺序

1. 虚拟pinctrl驱动加载 → 注册pinctrl设备
2. 虚拟GPIO驱动加载 → 注册GPIO控制器  
3. LED驱动加载 → 设备匹配和初始化

4.2 LED驱动probe函数执行流程

static int led_probe(struct platform_device *pdev)
{// 1. 内核自动应用pinctrl配置//    在probe调用前,内核会自动调用pinctrl_select_state()//    将myled_pin配置应用到硬件// 2. 获取GPIO描述符struct gpio_desc *led_gpio;led_gpio = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);// 这会解析: led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>// 3. 配置GPIO方向(如果需要)gpiod_direction_output(led_gpio, 0);// 4. 使用GPIO控制LEDgpiod_set_value(led_gpio, 1);  // 根据GPIO_ACTIVE_LOW,实际输出低电平
}

4.3 具体的引用解析过程

设备树引用解析

led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>;

解析为:

  • &gpio_virt → 找到 gpio_virt: virtual_gpiocontroller 节点

  • 0 → GPIO编号0

  • GPIO_ACTIVE_LOW → 低电平有效标志

5. 对应的驱动实现框架

5.1 pinctrl驱动

static const struct of_device_id virtual_pinctrl_of_match[] = {{ .compatible = "100ask,virtual_pinctrl" },{}
};static struct platform_driver virtual_pinctrl_driver = {.driver = {.name = "virtual_pinctrl",.of_match_table = virtual_pinctrl_of_match,},.probe = virtual_pinctrl_probe,
};

5.2 虚拟GPIO驱动

static const struct of_device_id virtual_gpio_of_match[] = {{ .compatible = "100ask,virtual_gpio" },{}
};static struct platform_driver virtual_gpio_driver = {.driver = {.name = "virtual_gpio",.of_match_table = virtual_gpio_of_match,},.probe = virtual_gpio_probe,
};

5.3 LED驱动

static const struct of_device_id led_drv_of_match[] = {{ .compatible = "100ask,leddrv" },{}
};static struct platform_driver led_driver = {.driver = {.name = "led_drv",.of_match_table = led_drv_of_match,},.probe = led_probe,
};

6. 在系统中的体现

6.1 加载后的sysfs结构

/sys/class/gpio/
└── gpiochipXXX/          # 虚拟GPIO控制器├── base → 480├── label → "virtual_gpiocontroller"└── ngpio → 4/sys/class/leds/
└── myled/               # LED设备/sys/kernel/debug/pinctrl/
└── virtual_pincontroller/  # 虚拟引脚控制器

6.2 用户空间使用

# 导出GPIO
echo 480 > /sys/class/gpio/export      # GPIO 480 (虚拟GPIO0)
echo out > /sys/class/gpio/gpio480/direction
echo 1 > /sys/class/gpio/gpio480/value  # 控制LED

7. 完整的工作流程

步骤1:设备树解析

  • 内核解析设备树,建立设备节点关系

  • 识别 virtual_pincontrollergpio_virtmyled 节点

步骤2:驱动匹配和加载

// pinctrl驱动匹配virtual_pincontroller
// GPIO驱动匹配gpio_virt  
// LED驱动匹配myled

步骤3:LED设备初始化

// 1. 内核自动调用pinctrl,应用myled_pin配置
// 2. LED驱动probe函数执行
// 3. 获取GPIO描述符,控制LED

步骤4:正常运行

  • 用户空间或内核其他部分可以通过GPIO接口控制LED

  • 所有硬件抽象层正常工作

gpio-ranges属性

基本语法:

gpio-ranges = <&pinctrl_phandle gpio_base pin_base count>;

参数解释:

  • &pinctrl_phandle:指向Pinctrl控制器的句柄

  • gpio_base:GPIO控制器中的起始GPIO编号

  • pin_base:Pinctrl控制器中的起始引脚编号

  • count:映射的引脚数量

完整的设备树

传统方式(需要显式pinctrl配置)

// Pinctrl控制器
pinctrlA: pinctrl@40020000 {compatible = "vendor,pinctrl";reg = <0x40020000 0x1000>;// 引脚配置uart1_pins: uart1-pins {pins = "PIOA0", "PIOA1";function = "uart1";};gpio_pins: gpio-pins {pins = "PIOA2", "PIOA3";function = "gpio";};
};// GPIO控制器
gpioA: gpio@40021000 {compatible = "vendor,gpio";reg = <0x40021000 0x1000>;gpio-controller;#gpio-cells = <2>;ngpios = <16>;// 没有gpio-ranges,需要显式配置pinctrl
};// 使用GPIO的设备
led {compatible = "vendor,led";led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;pinctrl-names = "default";pinctrl-0 = <&gpio_pins>;  // 必须显式配置
};

使用gpio-ranges的方式

// Pinctrl控制器
pinctrlA: pinctrl@40020000 {compatible = "vendor,pinctrl";reg = <0x40020000 0x1000>;// 注意:这里不需要定义gpio-pins了
};// GPIO控制器
gpioA: gpio@40021000 {compatible = "vendor,gpio";reg = <0x40021000 0x1000>;gpio-controller;#gpio-cells = <2>;ngpios = <16>;// 建立GPIO和Pinctrl的映射关系gpio-ranges = <&pinctrlA 0 128 12>;  // GPIO0-11 → Pinctrl引脚128-139
};// 使用GPIO的设备
led {compatible = "vendor,led";led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;// 不需要pinctrl-0,GPIO子系统自动处理
};

映射关系的内部实现

内核中的映射处理

当GPIO控制器注册时:

int gpiochip_add(struct gpio_chip *chip)
{// 解析gpio-ranges属性ret = gpiochip_add_pin_range(chip, dev_name(&pdev->dev),0, 0, chip->ngpio);// 建立映射关系pinctrl_add_gpio_range(chip->pinctrl, &range);
}

GPIO请求时的自动处理

struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)
{// 1. 获取GPIO描述符desc = of_get_named_gpiod_flags(dev->of_node, con_id, 0, &flags);// 2. 自动通过pinctrl配置引脚为GPIO功能ret = pinctrl_request_gpio(desc);// 3. 配置GPIO方向gpiod_direction_output(desc, flags & GPIOD_OUT_HIGH ? 1 : 0);return desc;
}

复杂的映射场景

多个不连续的映射范围

gpioA: gpio@40021000 {compatible = "vendor,gpio";gpio-controller;#gpio-cells = <2>;ngpios = <32>;// 多个映射范围gpio-ranges =<&pinctrlA 0  128 8>,   // GPIO0-7   → 引脚128-135<&pinctrlA 8  200 4>,   // GPIO8-11  → 引脚200-203  <&pinctrlA 12 240 16>;  // GPIO12-27 → 引脚240-255
};

多个Pinctrl控制器的映射

gpioA: gpio@40021000 {compatible = "vendor,gpio";gpio-controller;#gpio-cells = <2>;ngpios = <32>;// 映射到不同的Pinctrl控制器gpio-ranges =<&pinctrlA 0 128 16>,   // GPIO0-15  → PinctrlA的128-143<&pinctrlB 16 64 16>;   // GPIO16-31 → PinctrlB的64-79
};

驱动中的实际效果

使用gpio-ranges前的代码

static int led_probe(struct platform_device *pdev)
{struct gpio_desc *led;// 需要确保设备树中有pinctrl-0配置led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);if (IS_ERR(led)) {// 可能因为pinctrl配置失败return PTR_ERR(led);}return 0;
}

使用gpio-ranges后的代码

static int led_probe(struct platform_device *pdev)
{struct gpio_desc *led;// 直接获取GPIO,不需要关心pinctrl配置led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);// GPIO子系统自动处理引脚复用return 0;
}

验证映射关系

在sysfs中查看映射

# 查看GPIO范围
cat /sys/kernel/debug/gpio# 查看pinctrl映射
cat /sys/kernel/debug/pinctrl/pinctrl-handles# 查看具体的引脚状态
cat /sys/kernel/debug/pinctrl/<pinctrl>/pinmux-pins

在驱动中调试

// 在GPIO驱动probe函数中添加调试信息
static int gpio_probe(struct platform_device *pdev)
{struct gpio_chip *chip;// 解析gpio-rangesret = gpiochip_add_pin_range(chip, dev_name(&pdev->dev), 0, 0, chip->ngpio);if (ret) {dev_info(&pdev->dev, "Added pin range: GPIO%d-%d -> Pin%d-%d\n",range.base, range.base + range.npins - 1,range.pin_base, range.pin_base + range.npins - 1);}
}

1. 功能定位对比

第一段代码:显式引脚配置

uart1_pins: uart1-pins {pins = "PIOA0", "PIOA1";function = "uart1";
};gpio_pins: gpio-pins {pins = "PIOA2", "PIOA3";function = "gpio";
};

作用:为特定功能显式定义引脚配置

第二段代码:GPIO-Pinctrl映射

gpio-ranges = <&pinctrlA 0 128 12>;

2. 使用方式的根本区别

方式A:功能驱动配置(第一段代码)

// 设备树
my_uart_device {compatible = "vendor,uart";pinctrl-names = "default";pinctrl-0 = <&uart1_pins>;  // 显式选择UART功能
};my_led_device {compatible = "vendor,led";  pinctrl-names = "default";pinctrl-0 = <&gpio_pins>;   // 显式选择GPIO功能led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;
};

方式B:自动GPIO配置(第二段代码)

// 设备树  
my_led_device {compatible = "vendor,led";// 不需要pinctrl-0配置!led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;  // 自动配置为GPIO
};

3. 底层机制对比

方式A的底层流程:

设备probe↓
pinctrl-0应用 → 配置为指定功能↓
GPIO操作(如果配置的是GPIO功能)

方式B的底层流程:

设备probe↓
GPIO获取请求↓
通过gpio-ranges自动映射 → 自动配置为GPIO功能↓
GPIO操作

4. 具体关联分析

4.1 它们是互斥的设计选择

在实际项目中,你通常选择其中一种方式

选择方式A(显式配置)时:

// 需要定义详细的功能配置
pinctrlA: pinctrl {uart1_pins: uart1-pins { ... };gpio_pins: gpio-pins { ... };i2c1_pins: i2c1-pins { ... };
};gpioA: gpio {// 不需要gpio-ranges// 或者有gpio-ranges但不依赖它
};

选择方式B(自动映射)时:

// Pinctrl中可能不定义GPIO功能配置
pinctrlA: pinctrl {uart1_pins: uart1-pins { ... };  // 只有非GPIO功能i2c1_pins: i2c1-pins { ... };    // 只有非GPIO功能// 没有gpio-pins!
};gpioA: gpio {gpio-ranges = <&pinctrlA 0 128 12>;  // 关键映射
};

4.2 混合使用的情况

有些系统可能同时使用两种方式:

pinctrlA: pinctrl {// 为复杂功能定义显式配置uart1_pins: uart1-pins {pins = "PIOA0", "PIOA1";function = "uart1";bias-pull-up;  // 需要特殊电气特性};// 为GPIO定义特殊配置gpio_strong_pins: gpio-strong {pins = "PIOA2", "PIOA3"; function = "gpio";drive-strength = <20>;  // 强驱动强度};
};gpioA: gpio {gpio-ranges = <&pinctrlA 0 128 12>;  // 通用映射// 特殊GPIO可以引用显式配置special-gpio {gpio-ranges = <&pinctrlA 4 132 2>;  // 特定范围的映射};
};

5. 实际芯片中的差异

芯片类型1:需要显式配置(如某些老款芯片)

// 必须为每个GPIO使用显式pinctrl
pinctrl {led1_pins: led1-pins {pins = "GPIO0_5";function = "gpio";};led2_pins: led2-pins {pins = "GPIO0_6"; function = "gpio";};
};leds {led1 {pinctrl-0 = <&led1_pins>;gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;};led2 {pinctrl-0 = <&led2_pins>;gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;};
};

芯片类型2:支持自动映射(如STM32)

// 通过gpio-ranges自动处理
pinctrl {// 可能只有非GPIO功能的配置uart1_pins: uart1-pins {pins = "PA9", "PA10";function = "uart1";};
};gpioA: gpio@50000000 {gpio-ranges = <&pinctrl 0 0 16>;  // GPIOA0-15 -> 引脚0-15
};leds {led1 {// 不需要pinctrl-0!gpios = <&gpioA 5 GPIO_ACTIVE_HIGH>;  // 自动配置为GPIO};
};

7. 在驱动代码中的体现

使用显式配置的驱动:

static int device_probe(struct platform_device *pdev)
{// 内核会在probe前自动应用pinctrl-0// 驱动假设引脚已正确配置struct gpio_desc *gpio;gpio = gpiod_get(&pdev->dev, "signal", GPIOD_OUT_LOW);// 这里只是使用已配置好的GPIO
}

使用自动映射的驱动:

static int device_probe(struct platform_device *pdev)  
{// 驱动不关心引脚如何配置struct gpio_desc *gpio;gpio = gpiod_get(&pdev->dev, "signal", GPIOD_OUT_LOW);// GPIO获取过程中自动配置引脚功能
}

http://www.dtcms.com/a/499138.html

相关文章:

  • SpringBoot外部配置打包
  • 通达信--超级盘口
  • 基于单片机的开尔文电路电阻测量WIFI上传设计
  • 矽塔 SA8210 输入耐压36V 6A过流保护阈值 过压/过流保护芯片 SOT23-6/DFN2X2-8
  • 永年做网站收集链接 做网站
  • Linux----权限
  • 深入理解 PHP 框架里的设计模式
  • 西安网站工作室做外贸网站哪家的好
  • 如何用python写一个有字数上限的文字区块链?
  • 算能 CV184 智能相机整体方案介绍
  • 广州皮具网站建设湖南手机版建站系统信息
  • 大型网站建设哪里济南兴田德润实惠吗临沂市网站建设公司
  • Linux系统:线程介绍与POSIX线程库实现线程控制
  • ITP新增安全测试模块:构建自动化安全防护体系
  • 【C++/Lua联合开发】 (二) Lua调用C++函数
  • 基于Simulink的混动汽车模型建模与仿真,包含发动机管理,电机,电池管理以及混动汽车物理模型等
  • 网站备案都需要什么网站群项目建设实施进度计划
  • 数据库的事务和索引
  • W5500 esp32 micropython 驱动测试 网线直连电脑静态IP设置
  • 1panel docker开启swap内存
  • 动态规划的“降维”艺术:二维矩阵中的建筑奇迹——最大矩形
  • switch语句在汇编层面的几种优化方式 ,为什么能进行优化
  • Apache Spark算法开发指导-特征转换VectorIndexer
  • 企业网站的高跳出率应该如何解决广州物流网站开发
  • Docker 与 K8s 网络模型全解析
  • 【算法与数据结构】拓扑排序实战(栈+邻接表+环判断,附可运行代码)
  • AWS Elastic Load Balancing(ELB)—— 多站点负载均衡的正确打开方式
  • 如何用域名建网站主流建站公司
  • 企业网站模板源代码jz做网站
  • 深入 Spring 内核:解密 15 种设计模式的实战应用与底层实现