裸机开发时,如果要点亮一个 LED,我们要做如下内容:
- 初始化时钟
- 设置引脚复用为哪个功能,配置引脚的电气属性
- 设置引脚的 IO 方向、初始值
有了设备树以后,我们可以通过 pinctrl 和 gpio 子系统来配置上述内容。
- pinctrl 子系统:控制引脚的复用、电气属性
- gpio 子系统:控制GPIO初始化(如设置 IO 方向、初始值)
一、pinctrl 配置节点
pinctrl 子系统用于配置复用为不同功能时的电气属性,这就需要找到一个名为 iomuxc 的节点,这个节点会在两个地方出现:一个是通用 dtsi 设备树文件(如 imx6ull.dtsi),一个是板级设备对应的 dts 文件(如imx6ull-alientek-emmc.dts)。
1、iomuxc 通用节点
在 imx6ull.dtsi 中定义了一个 iomuxc 通用节点,该节点包含了 iomuxc 控制器的基地址,一般用于控制引脚复用。但光从这里似乎看不出来,可以复用为哪些外设,不同板级设备的外设不一样,所以外设相关的具体内容不会放在这里。
注意:IOMUXC 除了 iomuxc 外,还包含 iomuxc_snvs、gpr
2、iomuxc 外设节点
具体的外设节点会放到对应板级设备的 .dts 文件中。&iomuxc 会找到通用节点中的 iomuxc,然后实际编译的时候会将两者的内容合并。后续将在这里为不同功能对应的引脚配置电气属性。
二、添加 pinctrl 节点
1、创建节点
首先在板级设备的 dts 文件中找到 &iomuxc,这里板级设备对应的 dts 文件是 imx6ull-alientek-emmc.dts。我们在 imx6ul-evk 下创建一个节点,在其他地方我们将以 " &别名 " 的方式引用节点。
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {// "别名:节点名 {}" 的格式目的是为了在其他地方通过别名引用这个节点// "节点名 {}" 的格式目的仅仅只是为了描述一些信息,无法在其他地方引用pinctrl_gpiotest: testgrp {// ... };}
}
2、添加配置属性
配置属性解析
接下来我们要添加的属性名为 "fsl,pins" 。以 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 为例,MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 是一个属性名,也是宏定义,定义在 xxx-pinfunc.h 文件中(这里是 imx6ul-pinfunc.h),而 0x17059 是属性值。
- 前半部分 MX6UL_PAD_UART1_RTS_B 是需要配置的寄存器名称
- 后半部分 GPIO1_IO19 是要复用成哪个功能,这里是复用成 GPIO1_IO19 功能来使用。
可以在 imx6ul-pinfunc.h 文件中找到 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 的宏定义,可以看到宏定义的内容分为了五部分:
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
宏定义各个字段 | 宏定义各个字段的值 | 代表含义 |
<mux_reg> | 0x0090 | IOMUXC_SW_MUX_CTL 寄存器的偏移量 |
<conf_reg> | 0x031C | IOMUXC_SW_PAD_CTL 寄存器的偏移量 |
<input_reg> | 0x0000 | input_reg 寄存器(若无该寄存器,默认为 0x0) |
<mux_mode> | 0x5 | IOMUXC_SW_MUX_CTL 寄存器的值 (即选择复用为哪个功能) |
<input_val> | 0x0 | input_reg 寄存器的值 |
添加复用引脚配置属性
现在我们要复用为 GPIO1_IO03,因此需要在 imx6ul-pinfunc.h 文件中找到与 GPIO1_IO03 结尾的宏定义。赋予的电气属性值为 0x10b0。
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_gpiotest: testgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0 /* GPIO1_IO03 */>;};}
}
三、设备树引用 pinctrl 节点
1、pinctrl-names、pinctrl-x 解析
要引用 pinctrl 节点主要用到 pinctrl-names 属性和 pinctrl-x 属性。pinctrl-names 代表当前外设状态的名字,pinctrl-0 表示了当前状态下的配置方案。以下面这个为例
- pinctrl-names 代表状态名为 "default"
- pinctrl-0 代表 "default" 状态下对应的配置方案选择 pinctrl_hog_1,pinctrl_hog_1 就是在 iomuxc 下的一个 pinctrl 节点。
当然,一次可以根据状态不同,配置不同的方案,以下面这个为例:
- pinctrl-names 现在有三种状态,分别是 "default"、"state_100mhz"、"state_200mhz"
- pinctrl-0 对应 "default" 状态的配置方案
- pinctrl-1 对应 "state_100mhz" 状态的配置方案
- pinctrl-2 对应 "state_200mhz" 状态的配置方案
2、引用节点
我们在根节点下新增一个 gpio-led 节点,在该节点中引入 pinctrl 节点
gpio-led {pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpio_leds>; // pinctrl_gpio_leds 是 iomuxc下添加的节点别名status = "okay";
};
四、设备树引入 gpio 子系统
上面已经通过 pinctrl 子系统快速配置引脚复用,而gpio 子系统的主要目的是快捷设置 gpio 引脚的初始值(即初始状态为低电平还是高电平)
基本格式为:
属性名 = <&引脚组 引脚编号 初始状态>
假设要将 gpio1 的第 3 号引脚设置为低电平,具体写法为:
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>
- led-gpio:自定义属性名,后续在驱动代码会通过内核提供的 API 来获取到该属性
- gpio1: 引脚所属组,定义在 imx6ull.dtsi 文件中。
- GPIO_ACTIVE_LOW: 引脚状态的宏定义,表示低电平。高电平对应的宏定义为 GPIO_ACTIVE_HIGH
五、代码整合
最终引入 pinctrl 子系统和 gpio 子系统的节点模板为
gpio-led {pinctrl-names = "default"; // pinctrl 子系统pinctrl-0 = <&pinctrl_gpio_leds>;led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; // gpio 子系统status = "okay";
};
其中 pinctrl_gpio_leds 的定义为:
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_gpiotest: testgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0 /* GPIO1_IO03 */>;};}
}