汇编LED实验
IMX6ULL GPIO操作说明
GPIO模块资源说明
参考资料:芯片手册《Chapter 26 : General Purpose Input/Output (GPIO)》
一共5组GPIO,每组最多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模块分为3大模块:CCM 、 IOMUXC 、 GPIO模块本身;
CCM:设置GPIO模块时钟使能;
IOMUXC :复用功能选择;
GPIO:设置输入输出上下拉等功能。
所以要想使用GPIO,需要三步:
通过CCM模块设置GPIOx时钟使能
通过IOMUXC:选择GPIOx引脚的复用功能,以及上下拉等功能
通过GPIO模块本身,设置输入输出,读取或者设置输出高低电平
下图是整个GPIO模块框架图:

使能GPIO模块的时钟
CCM可以用来设置是否使能GPIO模块的时钟,通过设置CCM_CCGRy寄存器中的2位来决定GPIOx模块的时钟是否使能。

| 值 | 说明 |
|---|---|
| 00 | 一直关闭时钟 |
| 01 | 该GPIO 模块在 CPU run mode 情况下是使能的;在 WAIT 或 STOP 模式下,关闭 |
| 10 | 保留 |
| 11 | 一直使能时钟 |
GPIO2模块的时钟使能有CCM_CCGR0寄存器的位决定

GPIO1和GPIO5模块的时钟使能有CCM_CCGR1寄存器决定

GPIO3模块的时钟使能有CCM_CCGR2寄存器决定

GPIO4模块的时钟使能有CCM_CCGR2寄存器决定

选择复用功能
IOMUXC模块可以设置引脚的模式 Mode 、功能。参考资料:芯片手册《Chapter 3 2 : IOMUX Controller (IOMU XC) 》
对于某个某组引脚, IOMUXC 中有 2 个寄存器用来设置它:
IOMUXC_SW_MUX…… :设置复用功能
IOMUXC_SW_PAD……:设置上下拉等参数
选择复用功能
IOMUXC_SW_MUX_CTL_PAD_x :某个引脚的功能寄存器
IOMUXC_SW_MUX_CTL_GRP_:某组引脚的功能寄存器
无论是哪个引脚,或是哪组预设的引脚,都有8 个可选的模式 alternate (ALT) MUX_MODE。
例如GPIO1_IO00引脚,当把IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00寄存器的MUX_MODE(bit【0-3】)的位设置为0101(ALT5) 时,GPIO1_IO00引脚被设置为了GPIO功能。


设置上下拉电阻等参数
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 寄存器是设置GPIO1_IO00引脚的上下拉等参数的。其他的引脚也是类似,看数据手册即可。

GPIO模块输入输出设置
内部框图如下:

只关心三个寄存器:
GPIOx_GDIR:设置引脚方向,每位对应一个引脚 1 output 0 input
GPIOx_GDIR :设置输出引脚的电平,每位对应一个引脚 1 高电平, 0 低电平

GPIOx_PSR :读取引脚的电平,每位对应一个引脚 1 高电平, 0 低电平

编程步骤
读GPIO引脚
设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了
设置IOMUX来选择引脚用于GPIO
设置GPIOx_GDIR中某位为0,把该引脚设置为输入功能
读GPIOx_DR或GPIOx_PSR得到某位的值(读GPIOx_DR返回的是GPIOx_PSR的值)
写GPIO引脚
设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了
设置IOMUX来选择引脚用于GPIO
设置GPIOx_GDIR中某位为1,把该引脚设置为输出功能
写GPIOx_DR某位的值
需要注意的是,你可以设置该引脚的loopback功能,这样就可以从GPIOx_PSR中读到引脚的有实电平;你从GPIOx_DR中读回的只是上次设置的值,它并不能反应引脚的真实电平,比如可能因为硬件故障导致该引脚跟地短路了,你通过设置GPIOx_DR让它输出高电平并不会起效果。
RK3288 GPIO操作说明
GPIO模块资源说明
一共9组(GPIO0-GPIO8),每组又分为四个小组port A、B、C、D,每个小组最多8个IO,所以理论上一组GPIO最多32个,实际上每组可能没有这么多可用的。比如 GPIO0 只有 GPIO0_A0 -A7 、 GPIO0_B0-B7 、 GPIO0_C0 -C2 这些引脚。
RK3288 GPIO模块框图

GPIO的控制涉及 4 大模块: CRU 、 PMU 、 GRF 、 GPIO 模块本身
CRU :用于设置是否向 GPIO 模块提供时钟;
PMU :
GRF :
GPIO :
所以要想使用GPIO就得有以下步骤:
1.2.2. 使能GPIO 时钟
CRU 用于设置是否向 GPIO 模块提供时钟:内部框图如下:
../../../_images/image-20200528143100621.png
可以设置寄存器使能GPIOx 的时钟:
CRU_CLKGATE17_CON用于控制 GPIO0
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 分属不同的 PMU 。GPIO0、 GPIO1~8 都是常供电的 ,它们是否工作取决于其时钟是否使能。
设置引脚的模式 (Mode 、功能GPIO0)
GPIO0比较特殊,为了让其引脚用于 GPIO 功能,要设置 PMU 里的相关寄存器。 GPIO1-8 类似,为了让其引脚用于 GPIO 功能,要设置 GRF 里的相关寄存器。
GPIO模块内部
方向:引脚设置为GPIO 时,可以继续设置寄存器 GPIO_SWPORTA_DDR 确定它是输出引脚,还是输入引 脚。 数值:对于输出引脚,可以设置寄存器GPIO_SWPORTA_DR 让它输出高、低电平 对于输入引脚,可以读取寄存器 GPIO_EXT_PORTA 得到引脚的当前电平 。
RK3288相关寄存器偏移地址
../../../_images/image-20200528144139260.png
实验
硬件分析
如图所示,下面要把RGB灯的LED_R点亮,只需把GPIO_4(GPIO1_IO04)拉低即可。


源码文件分析
参考【01.led_asm】
start.S文件分析
.text //代码段
.align 4 //设置字节对齐
.global _start //定义全局变量
_start: //程序的开始
b reset //跳转到reset标号处
reset:
mrc p15, 0, r0, c1, c0, 0 /*读取CP15系统控制寄存器 */
bic r0, r0, #(0x1 << 12) /* 清除第12位(I位)禁用 I Cache */
bic r0, r0, #(0x1 << 2) /* 清除第 2位(C位)禁用 D Cache */
bic r0, r0, #0x2 /* 清除第 1位(A位)禁止严格对齐 */
bic r0, r0, #(0x1 << 11) /* 清除第11位(Z位)分支预测 */
bic r0, r0, #0x1 /* 清除第 0位(M位)禁用 MMU */
mcr p15, 0, r0, c1, c0, 0 /* 将修改后的值写回CP15寄存器 */
ldr sp, =0x80200000 /* .lds中设置链接地址为0x8010 0000,设置栈顶0x8020 0000 故预留1M的空间 */
bl light_led /* 绝对跳转,程序在片内RAM中执行 */
/*进入死循环*/
loop:
b loop
/*CCM_CCGR1 时钟使能寄存器地址,默认时钟全部开启*/
#define gpio1_clock_enible_ccm_ccgr1 0x20C406C
/*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 寄存器地址,用于设置GPIO1_iIO04的复用功能*/
#define gpio1_io04_mux_ctl_register 0x20E006C
/*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04寄存器地址,用于设置GPIO的PAD属性*/
#define gpio1_io04_pad_ctl_register 0x20E02F8
/*GPIO1_GDIR寄存器,用于设置GPIO为输入或者输出*/
#define gpio1_gdir_register 0x0209C004
/*GPIO1_DR寄存器,用于设置GPIO输出的电平状态*/
#define gpio1_dr_register 0x0209C000
light_led:
/*开启GPIO1的时钟*/
ldr r0, =gpio1_clock_enible_ccm_ccgr1
ldr r1, =0xFFFFFFFF
str r1, [r0]
/*将PAD引脚复用为GPIO*/
ldr r0, =gpio1_io04_mux_ctl_register
ldr r1, =0x5
str r1, [r0]
/*设置GPIO PAD属性*/
ldr r0, =gpio1_io04_pad_ctl_register
ldr r1, =0x1F838
str r1, [r0]
/*将GPIO_GDIR.[4] 设置为1, gpio1_io04设置为输出模式*/
ldr r0, =gpio1_gdir_register
ldr r1, =0x10
str r1, [r0]
/*将GPIO_DR 设置为0, gpio1全部输出为低电平*/
ldr r0, =gpio1_dr_register
ldr r1, =0x0
str r1, [r0]
/*跳出light_led函数,返回跳转位置*/
mov pc, lr