# 汇编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模块框架图: ![](media/image-20200528112439356.png) ### 使能GPIO模块的时钟 CCM可以用来设置是否使能GPIO模块的时钟,通过设置CCM_CCGRy寄存器中的2位来决定GPIOx模块的时钟是否使能。 ![](media/image-20200528111936917.png) | 值 | 说明 | | ---- | ------------------------------------------------------------ | | 00 | 一直关闭时钟 | | 01 | 该GPIO 模块在 CPU run mode 情况下是使能的;在 WAIT 或 STOP 模式下,关闭 | | 10 | 保留 | | 11 | 一直使能时钟 | - **GPIO2**模块的时钟使能有**CCM_CCGR0**寄存器的位决定 ![](media/image-20200528112406457.png) - **GPIO1和GPIO5**模块的时钟使能有**CCM_CCGR1**寄存器决定 ![](media/image-20200528112545064.png) - **GPIO3**模块的时钟使能有**CCM_CCGR2**寄存器决定 ![](media/image-20200528112632155.png) - **GPIO4**模块的时钟使能有**CCM_CCGR2**寄存器决定 ![](media/image-20200528112705015.png) ### 选择复用功能 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功能。 ![](media/image-20200528114526303.png) ![](media/image-20200528133554270.png) ### 设置上下拉电阻等参数 **IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00** 寄存器是设置GPIO1_IO00引脚的上下拉等参数的。其他的引脚也是类似,看数据手册即可。 ![](media/image-20200528135713459.png) ### GPIO模块输入输出设置 内部框图如下: ![](media/image-20200528140303517.png) 只关心三个寄存器: - `GPIOx_GDIR` :设置引脚方向,每位对应一个引脚 1 output 0 input ![](media/image-20200528140504162.png) - GPIOx_GDIR :设置输出引脚的电平,每位对应一个引脚 1 高电平, 0 低电平 ![](media/image-20200528140544881.png) - GPIOx_PSR :读取引脚的电平,每位对应一个引脚 1 高电平, 0 低电平 ![](media/image-20200528140603236.png) ### 编程步骤 #### 读GPIO引脚 1. 设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了 2. 设置IOMUX来选择引脚用于GPIO 3. 设置GPIOx_GDIR中某位为0,把该引脚设置为输入功能 4. 读GPIOx_DR或GPIOx_PSR得到某位的值(读GPIOx_DR返回的是GPIOx_PSR的值) #### 写GPIO引脚 1. 设置CCM_CCGRx寄存器中某位使能对应的GPIO模块 // 默认是使能的,上图省略了 2. 设置IOMUX来选择引脚用于GPIO 3. 设置GPIOx_GDIR中某位为1,把该引脚设置为输出功能 4. 写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模块框图 ![](media/image-20200528144426090.png) GPIO的控制涉及 4 大模块: CRU 、 PMU 、 GRF 、 GPIO 模块本身 - CRU :用于设置是否向 GPIO 模块提供时钟; - PMU : - GRF : - GPIO : 所以要想使用GPIO就得有以下步骤: - ### 1.2.2. 使能GPIO 时钟 CRU 用于设置是否向 GPIO 模块提供时钟:内部框图如下: ![../../../_images/image-20200528143100621.png](media/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](media/image-20200528144139260.png) ## 实验 ### 硬件分析 如图所示,下面要把RGB灯的LED_R点亮,只需把GPIO_4(GPIO1_IO04)拉低即可。 ![](media/1627566646705.png) ![](media/1627566836405.png) ### 源码文件分析 参考【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 ```