汇编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模块框架图:

../../_images/image-20200528112439356.png

使能GPIO模块的时钟

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

../../_images/image-20200528111936917.png

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

    ../../_images/image-20200528112406457.png

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

    ../../_images/image-20200528112545064.png

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

    ../../_images/image-20200528112632155.png

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

    ../../_images/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功能。

../../_images/image-20200528114526303.png

../../_images/image-20200528133554270.png

设置上下拉电阻等参数

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

../../_images/image-20200528135713459.png

GPIO模块输入输出设置

内部框图如下:

../../_images/image-20200528140303517.png

只关心三个寄存器:

  • GPIOx_GDIR :设置引脚方向,每位对应一个引脚 1 output 0 input

    ../../_images/image-20200528140504162.png

  • GPIOx_GDIR :设置输出引脚的电平,每位对应一个引脚 1 高电平, 0 低电平

    ../../_images/image-20200528140544881.png

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

    ../../_images/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模块框图

../../_images/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../../../_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../../../_images/image-20200528144139260.png

实验

硬件分析

如图所示,下面要把RGB灯的LED_R点亮,只需把GPIO_4(GPIO1_IO04)拉低即可。

../../_images/1627566646705.png

../../_images/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