项目场景:
在眺望电子TW-IMX6DL-EVM开发板上复用GPIO引脚,并分享其原理。
编译环境及开发包: 主机:ubuntu18.04 交叉编译器:arm-linux-gnueabihf-gcc 开发板:TW-IMX6DL-EVM Linux:Linux-4.1.15
注意:本文章的所有命令涉及到的路径均为眺望电子官方环境路径,需根据自身实际环境改变。本文所演示的平台来自于眺望电子 iMX6 ARM嵌入式平台,这是一个基于NXP iMX6 ARM处理器,支持单核/双核Cortex-A9的核心板。
解决方案:
1、GPIO操作原理
1.1 GPIO寄存器
每一个GPIO有8个寄存器需要配置,每一个GPIO寄存器功能如下: GPIO_DR(Data Register)数据寄存器 当GDIR设置为输出时,写DR的内容用来驱动GPIO的引脚,读DR的内容则返回存储在DR中的值。 当GDIR设置为输入时,读DR返回给定IO 引脚的状态(PSR data),而不是DR data。 GPIO_GDIR(Data Direction Register)方向控制寄存器 控制GPIO引脚的方向,1作为输出,0作为输入,寄存器中的每一位标识一个特定pad的方向。 仅当相应的引脚被设置为GPIO,GDIR才起作用。 GPIO_PSR(pad status register)状态寄存器 32-bit的只读寄存器。寄存器中的每一位都存储相应pad的值。 GPIO_ICR1,ICR2中断控制寄存器 两个32-bit寄存器,寄存器中每两位控制一条中断线,ICR1控制中断0~15,ICR2控制中断16~31。其可设置的值为:
- 00中断是低电平触发触发
- 01中断是高电平触发
- 10中断是上升沿触发
- 11中断时下降沿触发
IMR(Interrupt Mask Register)中断屏蔽寄存器 32-bit寄存器,每一位是相应中断线的屏蔽位, 0中断被屏蔽,1中断被使能。 GPIO_ ISR(Interrupt Status Register)中断状态寄存器 32bit register,每一位用于指定对应的中断线是否有中断发生,当一个中断发生,这个寄存器中的相应位被设置为1。 GPIO_EDGE_SEL(Edge Select Register) 32bit 寄存器,覆盖ICR寄存器的配置,选择edge 作为中断触发的条件。
1.2 IO功能操作
从引脚读取数据: 配置IOMUX选择GPIO模式; 配置GPIO的GPDR为输入; 从PSR寄存器读取数据(也可以从DR读取,此时会返回PSR内的值);
写数据到引脚: 配置IOMUX选择GPIO模式; 配置GPIO的GPDR为输出; 写值到Data Register(DR);
中断控制: 除了通用的input/output功能外,GPIO内部的edge-detect逻辑反映一个被配置为input的pad是否发生了状态转换。
2.内核GPIO操作
设备树imx6qdl-sabresd.dtsi是TW-AC6-EVM设备的配置文件。其中GPIO的配置在pinctrl_hog:hoggrp标签下。 程序清单1 引脚复用为gpio功能 目录:arch/arm/boot/dts/imx6qdl-sabresd.dtsi
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
imx6qdl-sabresd {
pinctrl_hog: hoggrp {
fsl,pins = <
MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x80000000
MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x80000000
MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000
MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x80000000
MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0
MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000
MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x80000000
MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000
MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000
MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000
MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x80000000
MX6QDL_PAD_SD1_CMD__GPIO1_IO18 0x80000000
MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x80000000
MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000
MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x80000000
MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000
MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000
MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000
MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000
MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000
>;
};
程序清单1中的MX6QDL_PAD类型宏定义在arch/arm/boot/dts/imx6q-pinfunc.h文件中。
#define MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x090 0x3a4 0x000 0x5 0x0
宏的各项含义为:
0x090 IOMUXC_SW_MUX_CTL_PAD_EIM_DATA16 mux寄存器偏移地址
0x3a4 IOMUXC_SW_PAD_CTL_PAD_EIM_DATA16 pad寄存器偏移地址
0x000 input寄存器偏移地址
0x5 ALT5 — Select signal GPIO3_IO16. mux的模式
0x0 input类型
0x80000000 IOMUXC_SW_MUX_CTL_PAD_EIM_DATA16的值 设置为芯片默认
之后会在内核初始化时调用drivers/pinctrl/pinctrl-imx.c中imx_pinctrl_parse_groups函数读取fsl.pin属性值,并保存在lisk指针变量中,紧接着,分别读取list中的值mux_reg、conf_reg、input_reg的值并设置为mux_mode、config、input_val。更多关于设备树gpio的配置资料,请阅读Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt。
若是在文件系统中GPIO的value一直为0,则需要在arch/arm/boot/dts/imx6q-pinfunc.h文件中修改MX6QDL_PAD_EIM_D16__GPIO3_IO16的值,在上一节可知可以通过修改引脚功能寄存器IOMUXC_SW_MUX的SION位为1设置为软件强行配置模式。修改结果如下:
#define MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x144 0x514 0x000 0x15 0x0
3.示例
3.1CAN1总线引脚复用为GPIO
打开“IMX6DQRM.pdf”数据手册的“26.2 External Signals”章节可以看到CAN总线I/O信号.
打开imx6qdl-sabresd.dtsi内核设备树文件,搜索can1可以得到如程序清单2的内容: 程序清单2 can1相关代码 目录:arch/arm/boot/dts/imx6qdl-sabresd.dtsi
&can1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_flexcan1>;
status = "okay";
};
pinctrl_flexcan1: flexcan1grp {
fsl,pins = <
MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x17059
MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x17059
>;
};
把程序清单2的内容中的两个引脚(即fsl,pins下的两个引脚)删除掉或者注释,以防重复使用该引脚。 注:引脚复用的原则是:在设备树中添加想要实现的该引脚的功能,需要把以前的复用功能去掉,无复用功能直接添加即可。
从程序清单2中可以得到can1读引脚使用图1的I/O信号是KEY_ROW2。KEY_ROW2是一个引脚独一无二的标志,由此判断设备树中的引脚是否为同一引脚。 在imx6q-pinfunc.h头文件中搜索KEY_ROW2,可以得到如程序清单3内容:
程序清单3 KEY_ROW2引脚复用 目录:arch/arm/boot/dts/imx6q-pinfunc.h
#define MX6QDL_PAD_KEY_ROW2__ECSPI1_SS2 0x20c 0x5dc 0x808 0x0 0x1
#define MX6QDL_PAD_KEY_ROW2__ENET_TX_DATA2 0x20c 0x5dc 0x000 0x1 0x0
#define MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x20c 0x5dc 0x7e4 0x2 0x0
#define MX6QDL_PAD_KEY_ROW2__KEY_ROW2 0x20c 0x5dc 0x000 0x3 0x0
#define MX6QDL_PAD_KEY_ROW2__SD2_VSELECT 0x20c 0x5dc 0x000 0x4 0x0
#define MX6QDL_PAD_KEY_ROW2__GPIO4_IO11 0x20c 0x5dc 0x000 0x5 0x0
#define MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x20c 0x5dc 0x88c 0x6 0x1
可以看到引脚KEY_ROW2除了可以复用FLEXCAN1_RX还可以复用为GPIO4_IO11功能。一般可以从引脚的命名后缀可以看出该引脚的作用,如MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX该引脚复用为can读引脚,MX6QDL_PAD_KEY_ROW2__GPIO4_IO11该引脚复用为GPIO。 在imx6qdl-sabresd.dtsi设备树文件的pinctrl_hog: hoggrp标签下添加GPIO引脚复用: 程序清单4 KEY_ROW2引脚复用成GPIO 目录:arch/arm/boot/dts/imx6qdl-sabresd.dtsi
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog>;
imx6qdl-sabresd {
pinctrl_hog: hoggrp {
fsl,pins = <
...
MX6QDL_PAD_KEY_ROW2__GPIO4_IO11 0x80000000 /* 引脚KEY_ROW2复用为GPIO4_IO11 */
>;
};
...
};
复用成gpio,后面的config值默认是0x80000000,为快速配置,如果复用成其它功能相关的值可以参考设备树中默认配置的值,如串口uart配置的值为0x1b0b1
|