分享

Pinctrl 基础配置讲解

 charlie_linux 2024-08-25 发布于广东

一、历史

在zephyr很早期阶段(2016年)年就引入了pinmux, 但由于早期没有统一的抽象,有少部分soc的设备树支持pin配置(nrf),绝大部分soc都是通过board下面的pinmux直接调用hal的PIN MUX接口(imx),因此Zephyr中的pinmux并不统一,导致添加不同soc的board的时候需要有不同的处理方式。Zephyr project在github.com/zephyrprojec,正式提出修改:

2.7.99到3.1之间pinctrl和pinmux共存

3.1完成pinctrl驱动,所有的board和外设驱动都切换为pinctrl

3.2后弃用pinmux,但保存代码

3.4后移除pinmux代码:github.com/zephyrproject到目前的状态,zephyr已经完全移除了pinmux,并启用了pinctrl,所有的soc均可以通过设备树进行引脚配置,因此掌握pinctrl是变成了使用zephyr的一项基础要求。

背景知识

pinctrl主要有两种功能:

  1. soc的引脚多路复用, 例如某个引脚是作为I2C输出还是作为SPI输出

  2. 引脚硬件的配置,例如某个引脚是上拉还是下拉等等

不同的soc多路复用的方式不一样常见的复用和配置方式如下面两图(源之zephyr文档) 一种是集中方式,一个alt硬件控制一组复用功能,NXP和ST的soc采用这种方式。

另一种是分布方式,复用影视和配置由多个硬件控制,ESP和NRF的soc采用这种方式。


以上两种方式对使用者最直观的感受就是NXP/ST的soc的一个PIN只能按照spec映射到特定的几种片上设备上的特定功能,例如只能映射连接到SPI/I2C/UART的TX,不能映射到RX或者是SDHC控制器上。 而ESP和NRF的SOC就更为灵活一个PIN可以映射到任意片上设备上。

不同的soc其pin的硬件配置也不一样,例如一些支持硬件消抖动,有一些不支持。

Zephyr Pinctrl的简介

在Zephyr中将不同的soc的引脚控制差异全部局限在设备树中 没有pinctrl前rt1062需要一个pinmux.c文件,里面放了大量的代码来配置硬件的复用,例如一个串口的就长这样:

IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_12_LPUART1_TX, 0);
IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_13_LPUART1_RX, 0);

IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_12_LPUART1_TX,
 IOMUXC_SW_PAD_CTL_PAD_PKE_MASK |
 IOMUXC_SW_PAD_CTL_PAD_SPEED(2) |
 IOMUXC_SW_PAD_CTL_PAD_DSE(6));

IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_13_LPUART1_RX,
 IOMUXC_SW_PAD_CTL_PAD_PKE_MASK |
 IOMUXC_SW_PAD_CTL_PAD_SPEED(2) |
 IOMUXC_SW_PAD_CTL_PAD_DSE(6));

有了pinctrl后,只需要修改设备树即可,就变成了这样。

pinmux_lpuart1: pinmux_lpuart1 {
 group0 {
  pinmux = <&iomuxc_gpio_ad_b0_13_lpuart1_rx>,
   <&iomuxc_gpio_ad_b0_12_lpuart1_tx>;
  drive-strength = "r0-6";
  slew-rate = "slow";
  nxp,speed = "100-mhz";
 };
};
&lpuart1 {
 status = "okay";
 current-speed = <115200>;
 pinctrl-0 = <&pinmux_lpuart1>;
 pinctrl-1 = <&pinmux_lpuart1_sleep>;
 pinctrl-names = "default", "sleep";
};

设备树中将pinmux和引脚配置分离为不同的node,另外设备树中pinctrl和片上设备绑定要更为简便,设备更能支持多种状态,可以在运行时切换。

设备树说明分析

NXP RT1062 UART示例

参考zephyr/boards/arm/mm_feather目录中设备树文件mm_feather.dts和mm_feather-pinctrl.dtsi

mm_feather-pinctrl.dtsi中定义了引脚的复用和配置

pinmux_lpuart1: pinmux_lpuart1 {
 group0 {
  pinmux = <&iomuxc_gpio_ad_b0_13_lpuart1_rx>,
   <&iomuxc_gpio_ad_b0_12_lpuart1_tx>;
  drive-strength = "r0-6";
  slew-rate = "slow";
  nxp,speed = "100-mhz";
 };
};

pinmux_lpuart1_sleep: pinmux_lpuart1_sleep {
 group0 {
  pinmux = <&iomuxc_gpio_ad_b0_13_gpio1_io13>;
  drive-strength = "r0-6";
  bias-pull-up;
  bias-pull-up-value = "100k";
  slew-rate = "slow";
  nxp,speed = "100-mhz";
 };
 group1 {
  pinmux = <&iomuxc_gpio_ad_b0_12_lpuart1_tx>;
  drive-strength = "r0-6";
  slew-rate = "slow";
  nxp,speed = "100-mhz";
 };
;

上面设备树定义了两组不同的状态 pinmux_lpuart1为正常的工作状态: URART1的RX使用GPIO_AD_B0_13, TX使用GPIO_AD_B0_12, RX/TX翻转速率(slew-rate)都为slow, PIN的反应速度(nxp,speed)为100M, 驱动能力(drive-strength)为r0-6 pinmux_lpuart1_sleep为soc休眠时的工作状态: 此时TX配置维持不变,RX变为GPIO,使用100K上拉(bias-pull-up/bias-pull-up-value),为的是在休眠期间可以被RX中断唤醒。 上面pinmux_lpuart1中的rx和tx拥有相同的pin配置可以放到同一个group中,这样可以减少设备树的长度。而pinmux_lpuart1_sleep中rx和tx的pin配置不一样,需要分为两个group配置 mm_feather.dts中将pinctrl的节点和lpuart1绑定

&lpuart1 {
 status = "okay";
 current-speed = <115200>;
 pinctrl-0 = <&pinmux_lpuart1>;
 pinctrl-1 = <&pinmux_lpuart1_sleep>;
 pinctrl-names = "default", "sleep";
};

pinctrl-0和pinctrl-1分别绑定pinmux_lpuart1和pinmux_lpuart1_sleep,在uart的设备驱动代码中通过pinctrl-names指定的default和sleep对Pinctrl节点的状态进行引用

ESP32 UART示例

参考zephyr/boards/riscv/esp32c3_devkitm中设备树文件:esp32c3_devkitm.dts和esp32c3_devkitm-pinctrl.dtsi esp32c3_devkitm-pinctrl.dtsi中定义了引脚的复用和和配置

uart0_default: uart0_default {
 group1 {
  pinmux = <UART0_TX_GPIO21>;
 };
 group2 {
  pinmux = <UART0_RX_GPIO20>;
  bias-pull-up;
 };
};

上面设备树只定义了一种状态即工作状态,这意味着串口驱动在睡眠模式下并不需要改变Pin的配置 UART0的TX口使用GPIO21, RX口使用GPIO20, RX口使用上拉 esp32c3_devkitm.dts中将pinctrl的节点和uart0绑定

&uart0 {
 status = "okay";
 current-speed = <115200>;
 pinctrl-0 = <&uart0_default>;
 pinctrl-names = "default";
};

设备树的形式

从前面的说明可以看到设备树被拆分开来,dtsi中对引脚进行复用和配置,dts中进行设备绑定。这样做在dts中比较单一,当有硬件上的改动时,只用专注修改Pinctrl的dtsi。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多