在上一章节我们通过C语言点亮了LED,但是对寄存器进行操作前先要对其在头文件里定义,每个寄存器和对应的地址都要写在头文件里,像下面图里显示的一样
使用起来就是非常不方便,也很容易出错。搞过STM32的朋友们在对相邻的寄存器赋值时是可以使用"GPIOB->ODR"这种指针形式实现的,前提是一个外设的寄存器是相邻的,我们可以借助C语言里的结构体成员地址递增的特点来讲某个外设的所有寄存器写到一个结构体里,然后定义一个结构体指针指向这个外设寄存器的基地址,这样我们就可以通过这个结构体指针来访问这个外设所有寄存器。同理,这节课我们就模仿STM32里的寄存器定义方式来编写I.MX6U的驱。
还是以点亮LED的试验为例,这里就不多说了,只以初始化CCM的过程演示一下。先在I.MX6UL参考手册里查一下CCM的内存映射
可以看出来CCM的内存地址是从0x20C_4000开始的,这个地址也就是我们后面要用到CCM的基地址。 后面每个寄存器的地址都是基于这个地址按照4个字节递增的,但是注意有几个特殊功能寄存器是在这个地址里的,但是并没有显示出来,所以有几个地址是不连贯的,比如403C和4044之间就少了个4040,4048和4054,对应了2个寄存器的地址。所以在后面定义Struct的时候要把这个寄存器空起来。
整个头文件就要重新写一下了,新建个头文件:imx6u.h,里面主要定义几个内容:寄存器组对应的结构体,结构体对应的基地址以及指针。
结构体定义
结构体定义和上面的表一样,把每个寄存器按顺序写下来就行了。
typedef struct { volatile unsigned int CCR; volatile unsigned int CCDR; volatile unsigned int CSR; volatile unsigned int CCSR; volatile unsigned int CACRR; volatile unsigned int CBCDR; volatile unsigned int CBCMR; volatile unsigned int CSCMR1; volatile unsigned int CSCMR2; volatile unsigned int CSCDR1; volatile unsigned int CS1CDR; volatile unsigned int CS2CDR; volatile unsigned int CDCDR; volatile unsigned int CHSCCDR; volatile unsigned int CSCDR2; volatile unsigned int CSCDR3; volatile unsigned int RESERVED_1[2]; volatile unsigned int CDHIPR; volatile unsigned int RESERVED_2[2]; volatile unsigned int CLPCR; volatile unsigned int CISR; volatile unsigned int CIMR; volatile unsigned int CCOSR; volatile unsigned int CGPR; volatile unsigned int CCGR0; volatile unsigned int CCGR1; volatile unsigned int CCGR2; volatile unsigned int CCGR3; volatile unsigned int CCGR4; volatile unsigned int CCGR5; volatile unsigned int CCGR6; volatile unsigned int RESERVED_3[1]; volatile unsigned int CMEOR; } CCM_Type;
可以看下有几个RESERVED,就是对应空起来的寄存器,空了几个就在后面的中括号里填几。
定义基地址
基地址的定义很简单,一句话就够了
#define CCM_BASE (0X020C4000) //CCM基地址
定义指针宏
指针的宏定义也很简单,也是一句话
#define CCM ((CCM_Type *)CCM_BASE)
这样就完成了头文件里的编写。
寄存器调用
在头文件里定义好寄存器以后,我们就可以直接使用了
#include "imx6u.h" int main(void) { CCM->CCGR0 = 0xFFFFFFFF; CCM->CCGR1 = 0xFFFFFFFF; }
这样是不是简单多了,并且后面我们要讲到,对于A系列的芯片,几乎没有厂商给我们提供这个表,还好恩智浦提供了这个寄存器映射文件,但是我们还是要移植一下,下一课就要讲。