ELADCMSecondEditionChapterFivePartⅤ

来自百问网嵌入式Linux wiki
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)

__NOTITLE__

具体单板的GPIO操作方法

请使用GIT下载文档后,看下图红框所示目录中各板子对应的文档及图片。
网盘中相同名字的目录下也有对应的视频。
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 012.png
为方便学习,在本文档中也把上述GIT目录中的文档添加进来了。

AM335X的GPIO操作方法

GPIO: General-purpose input/output,通用的输入输出口
PRCM: Power, Reset, and Clock Management (电源、复位、时钟管理器)
CM: Control Module(控制模块) 或 Clock Module (时钟模块)
PRM_PER: Power Reset Module Peripheral Registers(电源/复位模块中关于外设的寄存器)
CM_PER: Clock Module Peripheral Registers (时钟模块中关于外设的寄存器)

AM335X的GPIO模块结构

EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 013.png
有4组GPIO(GPIO0~3),每组有32个GPIO。
GPIO的控制涉及3大模块:PRCM、Control Module、GPIO模块本身。
① PRCM用于使能模块:
GPIO0永远都是使能的,GPIO1~3可单独控制。
PRCM模块给GPIO模块常供电,只需要使能GPIO模块的时钟。
② Control Module用于设置模式(Mode):
设置引脚的Mode(即选择功能)、上下拉电阻等;
每一个GPIO引脚在Control Module中都有一个寄存器,怎么找到这个寄存器?
a. 根据pin number确定pin name
b. 根据pin name在Control Module中确定寄存器
③ GPIO模块内部:
方向:引脚Mode设置为GPIO时,可以继续设置它是输出引脚,还是输入引脚。
数值:对于输出引脚,可以设置寄存器让它输出高、低电平;
对于输入引脚,可以读取寄存器得到引脚的当前电平。

AM335X的GPIO相关寄存器

EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 014.png

set-and-clear协议

假设某个GPIO被设置为输出,怎么设置它的输出电平呢?AM335X中对于每个GPIO模块有一个GPIO_DATAOUT寄存器,其中的每一位对应一个引脚,如下:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 015.png
要设置某一位时,不能影响到其他位,操作方法是:读出原来的值,修改某一位,把新值写回去。需要3个步骤才可以设置某一位的值,这效率太低了!
使用set-and-clear可以只用一个步骤即可修改某一位的值。
当想设置某一位为1时,往DATA_SETDATAOUT寄存器中某位写入1即可,芯片内部会把对应引脚的电平设置为1,并且不会影响其他引脚:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 016.png
当想清除某一位为0时,往DATA_CLEARDATAOUT寄存器中某位写入1即可,芯片内部会把对应引脚的电平设置为0,并且不会影响其他引脚:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 017.png
并非所有的芯片都有set-and-clear功能,TI的AM335X系列芯片有这功能。

RK3288的GPIO操作方法

GPIO: General-purpose input/output,通用的输入输出口
CRU: Clock & Reset Unit (时钟和复位单元)
PMU: Power Managerment Unit (电源管理单元)
GRF: General Register Files (通用寄存器文件)

RK3288的GPIO模块结构

EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 018.png
有9组GPIO(GPIO0~8),每组分为最多4个小组port A/B/C/D,每小组最多8个GPIO。理论上每组GPIO的引脚有32个,但是实际上并没有那么多。比如GPIO0只有GPIO0_A0~A7、GPIO0_B0~B7、GPIO0_C0~C2这些引脚。
GPIO的控制涉及4大模块:CRU、PMU、GRF、GPIO模块本身。
① CRU用于设置是否向GPIO模块提供时钟:
CRU的内部结构如下图所示:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 019.png
可以设置寄存器使能GPIOx的时钟:
a. CRU_CLKGATE17_CON用于控制GPIO0;
b. CRU_CLKGATE14_CON用于控制GPIO1~8
② PMU用于控制电源:
电源管理单元里,有多个电源域(power domain,简称为PM),在一个域下有多个设备。
比如PD_ALIVE,它下面有这些设备:CRU、GRF、GPIO 1~8、TIMER或WDT。
比如PD_PMU,它下面有这些设备:PMU、SRAM(4K)、Secure GRF、GPIO0。
可见,GPIO0、GPIO1~8分属不同的PM。
GPIO0、GPIO1~8都是常供电的,它们是否工作取决于其时钟是否使能。
③ 设置引脚的模式(Mode、功能):
GPIO0比较特殊,为了让其引脚用于GPIO功能,要设置PMU里的相关寄存器。
GPIO1~8类似,为了让其引脚用于GPIO功能,要设置GRF里的相关寄存器。
④ GPIO模块内部:
方向:引脚设置为GPIO时,可以继续设置寄存器GPIO_SWPORTA_DDR确定它是输出引脚,还是输入引脚。
数值:对于输出引脚,可以设置寄存器GPIO_SWPORTA_DR让它输出高、低电平;
对于输入引脚,可以读取寄存器GPIO_EXT_PORTA得到引脚的当前电平。

RK3288的GPIO相关寄存器

EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 020.png

RK3399的GPIO操作方法

GPIO: General-purpose input/output,通用的输入输出口
CRU: Clock & Reset Unit (时钟和复位单元)
PMU: Power Managerment Unit (电源管理单元)
GRF: General Register Files (通用寄存器文件)

RK3399的GPIO模块结构

EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 021.png
有5组GPIO(GPIO0~4),每组分为最多4个小组port A/B/C/D,每小组最多8个GPIO。理论上每组GPIO的引脚有32个,但是实际上并没有那么多。比如GPIO0只有GPIO0_A0~A7、GPIO0_B0~B5这些引脚。
GPIO的控制涉及4大模块:CRU、PMU、GRF、GPIO模块本身。
① CRU用于设置是否向GPIO模块提供时钟
a. PMUCRU_CLKGATE_CON1用于控制GPIO0~1;
b. CRU_CLKGATE_CON31用于控制GPIO2~4
② PMU用于控制电源:
电源管理单元里,有多个电源域(power domain,简称为PM),在一个域下有多个设备。
比如PD_ALIVE,它下面有这些设备:CRU、GRF、GPIO 1~4、TIMER或WDT。
比如PD_PMU,它下面有这些设备:cm0、PMU、SRAM(8K)、Secure GRF、GPIO0、PVTM、I2C。
可见,GPIO0、GPIO1~4分属不同的PM。
GPIO0、GPIO1~4都是常供电的。
③ 设置引脚的模式(Mode、功能):
GPIO0~1比较特殊,为了让其引脚用于GPIO功能,要设置PMU里的相关寄存器。
GPIO2~4类似,为了让其引脚用于GPIO功能,要设置GRF里的相关寄存器。
④ GPIO模块内部:
方向:引脚设置为GPIO时,可以继续设置寄存器GPIO_SWPORTA_DDR确定它是输出引脚,还是输入引脚。
数值:对于输出引脚,可以设置寄存器GPIO_SWPORTA_DR让它输出高、低电平;
对于输入引脚,可以读取寄存器GPIO_EXT_PORTA得到引脚的当前电平。

RK3399的GPIO相关寄存器

EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 022.png

IMX6ULL的GPIO操作方法

CCM: Clock Controller Module (时钟控制模块)
IOMUXC : IOMUX Controller,IO复用控制器
GPIO: General-purpose input/output,通用的输入输出口

IMX6ULL的GPIO模块结构

参考资料:芯片手册《Chapter 26: General Purpose Input/Output (GPIO)》
有5组GPIO(GPIO1~GPIO5),每组引脚最多有32个,但是可能实际上并没有那么多。
GPIO1有32个引脚:GPIO1_IO0~GPIO1_IO31;
GPIO2有22个引脚:GPIO2_IO0~GPIO2_IO21;
GPIO3有29个引脚:GPIO3_IO0~GPIO3_IO28;
GPIO4有29个引脚:GPIO4_IO0~GPIO4_IO28;
GPIO5有12个引脚:GPIO5_IO0~GPIO5_IO11;
GPIO的控制涉及4大模块:CCM、IOMUXC、GPIO模块本身,框图如下:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 023.png

CCM用于设置是否向GPIO模块提供时钟

参考资料:芯片手册《Chapter 18: Clock Controller Module (CCM)》
GPIOx要用CCM_CCGRy寄存器中的2位来决定该组GPIO是否使能。哪组GPIO用哪个CCM_CCGR寄存器来设置,请看上图红框部分。
CCM_CCGR寄存器中某2位的取值含义如下:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 024.png
① 00:该GPIO模块全程被关闭
② 01:该GPIO模块在CPU run mode情况下是使能的;在WAIT或STOP模式下,关闭
③ 10:保留
④ 11:该GPIO模块全程使能
GPIO2时钟控制:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 025.png
GPIO1、GPIO5时钟控制:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 026.png
GPIO3时钟控制:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 027.png
GPIO4时钟控制:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 028.png

IOMUXC:引脚的模式(Mode、功能)

参考资料:芯片手册《Chapter 32: IOMUX Controller (IOMUXC)》。
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 029.png
对于某个/某组引脚,IOMUXC中有2个寄存器用来设置它:
① 选择功能:
IOMUXC_SW_MUX_CTL_PAD_<PADNAME> :Mux pad xxx,选择某个pad的功能
IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>:Mux grp xxx,选择某组引脚的功能
某个引脚,或是某组预设的引脚,都有8个可选的模式(alternate (ALT) MUX_MODE)。
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 030.png
比如:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 031.png
② 设置上下拉电阻等参数:
IOMUXC_SW_PAD_CTL_PAD_<PADNAME> : pad pad xxx,设置某个pad的参数
IOMUXC_SW_PAD_CTL_GRP_<GROUP NAME>:pad grp xxx,设置某组引脚的参数
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 032.png
比如:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 033.png

GPIO模块内部

框图如下:
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 034.png
我们暂时只需要关心3个寄存器:
① GPIOx_GDIR:设置引脚方向,每位对应一个引脚,1-output,0-input
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 035.png
② GPIOx_GDIR:设置输出引脚的电平,每位对应一个引脚,1-高电平,0-低电平
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 036.png
③ GPIOx_PSR:读取引脚的电平,每位对应一个引脚,1-高电平,0-低电平
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 037.png

怎么编程

读GPIO
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 038.png
翻译一下:
① 设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了
② 设置IOMUX来选择引脚用于GPIO
③ 设置GPIOx_GDIR中某位为0,把该引脚设置为输入功能
④ 读GPIOx_DR或GPIOx_PSR得到某位的值(读GPIOx_DR返回的是GPIOx_PSR的值)
写GPIO
EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive 039.png
翻译一下:
① 设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了
② 设置IOMUX来选择引脚用于GPIO
③ 设置GPIOx_GDIR中某位为1,把该引脚设置为输出功能
④ 写GPIOx_DR某位的值
需要注意的是,你可以设置该引脚的loopback功能,这样就可以从GPIOx_PSR中读到引脚的有实电平;你从GPIOx_DR中读回的只是上次设置的值,它并不能反应引脚的真实电平,比如可能因为硬件故障导致该引脚跟地短路了,你通过设置GPIOx_DR让它输出高电平并不会起效果。