在本章中,笔者会逐步介绍在IMX6ULL中的五种LED点灯方式——
(1)纯汇编语言;
(2)汇编混合C语言;
(3)STM32风格;
(4)基于NXP官方提供SDK;
(5)最终版本的ledc_bsp,并以此为基础进行后续的裸机外设开发。
1.纯汇编语言
Cortex-A7常用汇编指令:
(1) MOV
MOV R0,R1 //将数据从一个寄存器拷贝到另一个寄存器
MOV R0,#0XFF //或将一个立即数传到寄存器里面;
(2) MRS 读特殊寄存器
MRS R0,CPSR //将特殊寄存器(CPSR/SPSR)里面的传给通用寄存器
(3) MSR 写特殊寄存器
MSR CPSR,R0 //将通用寄存器的传给特殊寄存器(CPSR/SPSR)
(4) LDR 将立即数加载到寄存器中
LDR R0, =0X0209C004; //即R0=0X0209C004,将寄存器地址给R0
LDR R1, [R0]; //将R0地址0X0209C004里面的数据给R1
(5) STR 将数据写入存储器中
LDR R0, =0X0209C004; //即R0=0X0209C004,将寄存器地址给R0
LDR R1, =0X20000002; //即R1=0X20000002,将要写入的值给R1
STR R1,[R0]; //将R1的值写入R0的地址中(STR、LDR作用相反 )
对比STM32和IMX6ULL的GPIO初始化
STM32 IO初始化流程:
(1) 使能GPIO时钟
(2) 设置IO复用,将其复用为GPIO
(3) 配置GPIO的电气属性
(4) 使用GPIO,输出高/低电平
由此对比,IMX6ULL的GPIO初始化也可以根据以上步骤进行套用。
IMX6ULL IO初始化流程:
(1) 使能时钟CCGR0-CCGR6,即IMX6ULL所有外设时钟使能;为简单化,设置CCGR0-CCGR6这7个寄存器均为0XFFFFFFFF,相当于使能所有外设时钟。
/*
以CCGR0的使能时钟举例,将CCGR0这一寄存器的地址0X020C4068作为立即数加载至r0寄存器中,
将0XFFFFFFFF这一立即数加载至r1寄存器中,str指令将r1这一立即数,写入r0所指向地址,
则此时0X020C4068地址里被写入0XFFFFFFFF,CCGR0总时钟源使能成功,此后CCGR1~6各时钟使能同理。
*/
ldr r0, =0X020C4068 /* CCGR0 */
ldr r1, =0XFFFFFFFF
str r1, [r0]
ldr r0, =0X020C406C /* CCGR1 */
str r1, [r0]
ldr r0, =0X020C4070 /* CCGR2 */
str r1, [r0]
ldr r0, =0X020C4074 /* CCGR3 */
str r1, [r0]
ldr r0, =0X020C4078 /* CCGR4 */
str r1, [r0]
ldr r0, =0X020C407C /* CCGR5 */
str r1, [r0]
ldr r0, =0X020C4080 /* CCGR6 */
str r1, [r0]
(2) 设置IO复用
查询数据手册可知
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00的Address: 20E_005Ch
GPIO1_IO00: 20E_0000h base + 5Ch offset = 20E_005Ch
GPIO1_IO01: 20E_0000h base + 60h offset = 20E_0060h
GPIO1_IO02: 20E_0000h base + 64h offset = 20E_0064h
GPIO1_IO03: 20E_0000h base + 68h offset = 20E_0068h
由此可以看出,这一寄存器每个占四字节。此时设置IO复用,将IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的bit3~0设置为0101,即为5,此时GPIO_IO03复用为GPIO了。
/* 2、设置GPIO1_IO03复用为GPIO1_IO03 */
ldr r0, =0X020E0068 /* 将寄存器地址加载到r0中 */
ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */
str r1,[r0] /* 将0101写入复用SW_MUX的地址0X020E0068,设置复用完成*/
(3)配置GPIO的电气属性
将寄存器IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03进行配置,其中包括压摆率、速度、驱动能力、开漏、上下拉等。
/* 3、配置GPIO1_IO03的IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */
ldr r1, =0X10B0
str r1,[r0]
(4) 使用GPIO,设置输入输出。
设置GPIO1_GDIR寄存器bit3位1,设置为输出模式。(配置DR/GDIR)
如上图所示,设置为SW_MUX_CTL_PAD和SW_PAD_CTL_PAD后,就要设置GPIO的DR、GDIR、PSR、ICR1/ICR2、IMR、ISR、EDGE_SEL了。
DR——DataBits 数据寄存器
DR为32位寄存器。DR寄存器中每位都对应一个GPIO。
当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。
当 GPIO被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。
GDIR——Gpio Direction IO工作方向寄存器
GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。同样的,每个 IO 对应一个位,如果要设置 GPIO 为输入的话就设置相应的位为 0,如果要设置为输出的话就设置为 1。
比如要设置 GPIO1_IO00 为输入,那么 GPIO1.GDIR=0。
PSR——Pad Stauts Bits 状态寄存器
PSR读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值。功能和输入状态下的 DR 寄存器一样。
ICR1/ICR2——Interrupt Control 中断控制寄存器
IMR——Interrupt Mask 中断屏蔽寄存器
IMR 寄存器用来控制 GPIO 的中断禁止和使能,如果使能某个 GPIO 的中断,那么设置相应的位为 1 即可,反之,如果要禁止中断,那么就设置相应的位为 0 即可。例如,要使能 GPIO1_IO00 的中断,那么就可以设置 GPIO1.MIR=1 即可。
ISR——Interrupt Status 中断状态寄存器
ISR 寄存器也是 32 位寄存器,一个 GPIO 对应一个位,只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1。所以,我们可以通过读取 ISR 寄存器来判断 GPIO 中断是否发生,相当于 ISR 中的这些位就是中断标志位。
当我们处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写 1 清零。
EDGE_SEL——edge select 边沿选择寄存器
EDGE_SEL 寄存器用来设置边沿中断,这个寄存器会覆盖 ICR1 和 ICR2 的设置,同样是一个 GPIO 对应一个位。如果相应的位被置 1,那么就相当与设置了对应的 GPIO 是上升沿和下降沿(双边沿)触发。
例如,我们设置GPIO1.EDGE_SEL=1,那么就表示 GPIO1_IO01 是双边沿触发中断,无论 GFPIO1_CR1 的设置为多少,都是双边沿触发。
######小结
以上,GPIO的DR、GDIR、PSR、ICR1/ICR2、IMR、ISR、EDGE_SEL均简要进行介绍。这7类8个寄存器为gpio最常用的寄存器。
- DR:数据存储;
- GDIR:io方向;
- PSR:状态寄存,等效输入状态的dr;
- ICR1/ICR2:中断控制;
- IMR:中断屏蔽;
- ISR:中断状态;
- EDGE_SEL:边沿选择;
故在此点灯方式中,仅需设置GDIR为输出模式,并设置GPIO1_IO03输出低电平。
即GDIR=1,DR=0。
/* 4、设置GPIO1_IO03为输出 */
ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR的地址 */
ldr r1, =0X0000008 /*转二进制则为1000,此时设置为输出模式*/
str r1,[r0]
/* 5、打开LED0
* 设置GPIO1_IO03输出低电平
*/
ldr r0, =0X0209C000 /*寄存器GPIO1_DR */
ldr r1, =0
str r1,[r0]
并且其中Makefile如下,笔者将在第(3)部分继续讲解Makefile的相关使用注意事项。
led.bin:led.s
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
rm -rf *.o led.bin led.elf led.dis
总结
这是极其生硬且难啃的一章,需要对系统时钟、GPIO的复用、GPIO的电气属性,作为GPIO的输入输出以及相关的8个寄存器进行学习。是从STM32到Linux嵌入式的拦路虎,需要对数据手册以及相关的知识进行学习,并且形成博客以此输出知识从而达到加深记忆的目的。