RT1052 采用的是 Cortex-M7 内核,内核即 CPU,由 ARM 公司设计。
该芯片并没有像传统的 MCU 一样集成内部 FLASH 存储器,因此该芯片必须依靠一个外部 FLASH 长期保存程序代码。
Instruction Cache 指令缓存,i.MX RT 系列芯片中其大小为32KB,内核访问该存储器有着极高的速度。
Data Cache 数据缓存,在 i.MX RT 系列芯片中其大小为 32KB,它与 ICache 的功能类似,起到缓存的作用,区别只是 ICache 专用于存储指令,DCache 专用于存储数据。
灵活RAM。在 i.MX RT 系列芯片中其大小为 512KB,可以把它理解成传统MCU 的内部 SRAM 存储器。
内核使用不同的总线访问这些不同的存储器,因而访问速度有差异:
ICache 和 DCache 是内核自动使用,用户无法访问的。而 ITCM、DTCM 及 OCRAM 是用户可根据具体地址进行访问,开发程序时应根据它们的特性加以利用。
SRAM、片上外设及外部存储器,这些功能部件共同排列在一个4GB 的地址空间内。
在这 4GB 的地址空间中,ARM 已经粗线条的平均分成了 8 个块,,每个块也都规定了用途
大部分块的大小都有 512MB 以上,显然这是非常大的。在这 8 个 Block 里面,有这 3 个块非常重要
根据 ARM 内核的设计,Block0 主要用于存储程序代码,在 i.MX RT1052 芯片内部又把这部分划分了几个类型。
ITCM 是 Instruction Tightly-Coupled Memory 的缩写,译为指令紧耦合内存。所谓紧耦合是指该内存与内核连接紧密,有非常高的访问速度。
ROMCP,这是一小段 ROM 空间,用于存储芯片启动时的加载代码,即 bootloader,bootloader 负责把指令从外部存储器加载至 ITCM。
SEMC 及 FlexSPI 是 RT1052 可用于控制外部并行及串行 NorFlash 的两个外设,此处把它们映射到此代码空间,是为了支持 XIP 功能(即指令直接在 NorFlash 上运行,不需要加载到内部的 ITCM)。
Block1 用于设计片内的 SRAM,也就是芯片运行时的内存,在 i.MX RT1052 芯片内部把这部分划分了两种 RAM 类型
第一种类型为 DTCM,是 Data Tightly-Coupled Memory 的缩写,译为数据紧耦合内存,它跟 ITCM类似,有着极高的访问速度,不过它是专门用来存储程序数据的,即代码中变量的存储位置。
第二种类型为 OCRAM,它是 On-chip RAM 的缩写,即片上内存,可以完全把它理解为传统 MCU的内部 SRAM,它没有像 ITCM 和 DTCM 的专用限制,可用于存储指令和数据(通用目的)。
Block2 用于设计片内的外设,在 RT1052 芯片中,它的外设使用 4 条总线与内核进行连接:
AIPS 是 ARM IP Bus 的缩写,它一边与内核 AHB 总线连接,另一边与片上的各种外设连接
最右一栏是挂载在 AIPS-2 总线上的外设名称。
阴影处为例,它表示一个名为 GPIO1 的外设的内存地址分配情况
在上述存储器 Block2 这块区域,设计的是片上外设。
我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元。
GPIO1 端口的输出数据寄存器 DR 的地址是 0x401B 8000。
C语言访问:
// GPIO1 端口全部输出 高电平
*(unsigned int*)(0x401B8000) = 0xFFFFFFFF;
寄存器访问:
// GPIO1 端口全部输出 高电平
#define GPIO1_DR (unsigned int*)(0x401B8000)
*GPIOF_DR = 0xFFFFFFFF;
为了方便操作,我们干脆把指针操作“*”也定义到寄存器别名里面
// GPIO1 端口全部输出 高电平
#define GPIO1_DR *(unsigned int*)(0x401B8000)
GPIOF_DR = 0xFFFFFFFF;
片上外设区的四条 AIPS 总线挂载着不同的外设,相应总线的最低地址我们称为该总线的基地址。
总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX外设基地址”,也叫 XX 外设的边界地址。
GPIO 是通用输入输出端口的简称,简单来说就是 RT1052 可控制的引脚。
GPIO1 端口的寄存器地址列表:
数据寄存器GPIO1_DR是GPIO1中的首个寄存器,所以它的寄存器地址与GPIO1的外设基地址相同,为0x401B8000。相对GPIO1基地址的偏移为 0。
紧挨着的是方向寄存器GPIO1_GDIR,它的地址也相对 GPIO1_DR 增加了 4,
GPIO2 基地址:0x401B 8000+(2-1)×0x4000 = 0x401B C000
IMR 寄存器相对基地址的偏移为 0x14,所以:
GPIO2_IMR 寄存器地址:0x401B C000 + 0x14 = 0x401B C014
以“GPIO 中断配置寄存器 GPIO_ICR1”为例
偏移地址
寄存器功能说明。
寄存器位表:
配置域功能说明:
详细介绍了寄存器每一个位的功能。
为了方便理解和记忆,我们把外设基地址和寄存器地址都以相应的宏定义起来
/* GPIO - 外设基地址 */
/** GPIO1 外设基地址 */
#define GPIO1_BASE (0x401B8000u)
/** GPIO2 外设基地址 */
#define GPIO2_BASE (0x401BC000u)
/** GPIO3 外设基地址 */
#define GPIO3_BASE (0x401C0000u)
/** GPIO4 外设基地址 */
#define GPIO4_BASE (0x401C4000u)
/** GPIO5 外设基地址 */
#define GPIO5_BASE (0x400C0000u)
在外设基地址上加入各寄存器的地址偏移,得到特定寄存器的地址。
一旦有了具体地址,就可以用指针进行读写操作:
1 /* 控制 GPIO1 引脚 6 配置为高电平引起中断
2 (GPIO1_ICR1 寄存器的 ICR6 设置为 01b ,即 0x01) */
3 /* 先对配置域 ICR6 的 2 个数据位清 0 */
4 *(unsigned int *)GPIO1_ICR1 &= ~(0x3<<(2*6));
5 /* 给配置域 ICR6 的 2 个数据位赋值 01b */
6 *(unsigned int *)GPIO1_ICR1 |= (0x01<<(2*6));
7
8 /* 控制 GPIO1 引脚 6 配置为上升沿引起中断
9 (GPIO1_ICR1 寄存器的 ICR6 设置为 10b ,即 0x02) */
10 /* 先对配置域 ICR6 的 2 个数据位清 0 */
11 *(unsigned int *)GPIO1_ICR1 &= ~(0x3<<(2*6));
12 /* 给配置域 ICR6 的 2 个数据位赋值 10b */
13 *(unsigned int *)GPIO1_ICR1 |= (0x02<<(2*6));
14
15 unsigned int temp;
16 /* 控制 GPIO1 端口所有引脚的电平 ( 读 DR 寄存器 ) */
17 temp = *(unsigned int *)GPIO1_DR;
引入 C 语言中的结构体语法对寄存器进行封装:
1 typedef unsigned int uint32_t; /* 无符号 32 位变量 */
2 typedef unsigned short int uint16_t; /* 无符号 16 位变量 */
3
4 /** GPIO - 寄存器列表 */
5 typedef struct {
6 uint32_t DR; /**< GPIO 数据寄存器 , 地址偏移 : 0x0 */
7 uint32_t GDIR; /**< GPIO 方向寄存器 , 地址偏移 : 0x4 */
8 uint32_t PSR; /**< GPIO 状态寄存器 , 地址偏移 : 0x8 */
9 uint32_t ICR1; /**< GPIO 中断配置寄存器 1, 地址偏移 : 0xC */
10 uint32_t ICR2; /**< GPIO 中断配置寄存器 2, 地址偏移 : 0x10 */
11 uint32_t IMR; /**< GPIO 中断掩码寄存器 , 地址偏移 : 0x14 */
12 uint32_t ISR; /**< GPIO 中断状态寄存器 , 地址偏移 : 0x18 */
13 uint32_t EDGE_SEL; /**< GPIO 边沿选择寄存器 , 地址偏移 : 0x1C */
14 } GPIO_Type;
通过结构体指针访问寄存器:
1 GPIO_Type * GPIOx; // 定义一个 GPIO_Type 型结构体指针变量 GPIOx
2 GPIOx = GPIO1_BASE; // 把指针地址设置为宏 GPIO1_BASE 地址
3 GPIOx->DR = 0xFFFF; // 通过指针访问并修改 GPIO1_DR 寄存器
4 GPIOx->GDIR = 0xFFFFFFFF; // 修改 GPIO1_GDIR 寄存器
5 GPIOx->ICR1 =0xFFFFFFFF; // 修改 GPIO1_ICR1 寄存器
7 uint32_t temp;
8 temp = GPIOx->DR; // 读取 GPIOF_DR 寄存器的值到变量 temp 中
先 用 GPIO_Type 类 型 定 义 一 个 结 构 体 指 针 GPIOx, 并 让 指 针 指 向 地 址GPIO1_BASE(0x401B 8000)
GPIO 端口基地址址针:
1 /* 使用 GPIO_Type 把地址强制转换成指针 */
2 #define GPIO1 ((GPIO_Type *) GPIO1_BASE)
3 #define GPIO2 ((GPIO_Type *) GPIO2_BASE)
4 #define GPIO3 ((GPIO_Type *) GPIO3_BASE)
5 #define GPIO4 ((GPIO_Type *) GPIO4_BASE)
6 #define GPIO5 ((GPIO_Type *) GPIO5_BASE)
把变量 a 的某一位清零,且其它位不变
// 定义一个变量 a = 1001 1111 b ( 二进制数 )
2 unsigned char a = 0x9f;
3
4 // 对 bit2 清零
5
6 a &= ~(1<<2);
7
8 // 括号中的 1 左移两位, (1<<2) 得二进制数: 0000 0100 b
9 // 按位取反, ~(1<<2) 得 1111 1011 b
10 // 假如 a 中原来的值为二进制数: a = 1001 1111 b
11 // 所得的数与 a 作”位与 & ”运算, a = (1001 1111 b)&(1111 1011 b),
12 // 经过运算后, a 的值 a=1001 1011 b
13 // a 的 bit2 位被被零,而其它位不变
要设置寄存器的位参数:
1 //a = 1000 0011 b
2 // 此时对清零后的第 2 组 bit4 、 bit5 设置成二进制数“ 01 b ”
3
4 a |= (1<<2*2);
5 //a = 1001 0011 b ,成功设置了第 2 组的值,其它组不变
对变量的某位取反:
//a = 1001 0011 b
2 // 把 bit6 取反,其它位不变
3
4 a ^=(1<<6);
5 //a = 1101 0011 b