存储器和寄存器
1、程序存储器
1.1 存储器映射
程序存储器、数据存储器、寄存器和 I/O 端口排列在同一顺序的 4 GB 地址空间内。
存储器本身不具有地址信息,其地址由芯片厂商或用户分配,给存储器分配地址的过程称为存储器映射,若再分配一个地址就叫重映射。
2、存储器空间
ARM将这4GB存储器空间,平均分成8块区域,每块区域大小512MB。
2.1 Block0
Block0 主要用于设计片内FLASH,STM32F103系列芯片内部FLASH最大
512KB
0x0000 0000-0x0007 FFFF:取决于BOOT引脚,是FLASH、系统存储器、SRAM的别名
0x0008 0000-0x07FF FFFF:预留
0x0800 0000-0x0807 FFFF:片内FLASH,编写的程序就放在这一区域(512KB)
0x0808 0000-0x1FFF EFFF:预留
0x1FFF F000-0x1FFF F7FF:系统存储器,里面存放的是 ST 出厂时烧写好的isp 自举程序,用户无法改动。使用串口下载的时候需要用到这部分程序。
0x1FFF F800-0x1FFF F80F:选项字节,用于配置读写保护、BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。当芯片不小心被锁住后,可以从 RAM 里面启动来修改这部分相应的寄存器位。
0x1FFF F810-0x1FFF FFFF:预留。
2.2 Block1
Block1用于设计片内SRAM,STM32F103ZET6的SRAM是64KB
0x2000 0000-0x2000 FFFF:SRAM,容量为64KB
0x2001 0000-0x3FFF FFFF:预留
2.3 Block2
Block2用于设计片内外设,根据外设总线速度的不同,Block2划分为AHB和APB 两部分,APB又分成APB1和APB2总线。
0x4000 0000-0x4000 77FF:APB1总线外设
0x4000 7800-0x4000 FFFF:预留
0x4001 0000-0x4001 3FFF:APB2总线外设
0x4001 4000-0x4001 7FFF:预留
0x4001 8000-0x4002 33FF:AHB总线外设
0x4002 4400-0x5FFF FFFF:预留
在 Block3/4/5 中还包含了 FSMC 扩展区域,这 3 个块可用于扩展外部存储器,如 SRAM,NORFLASH 和 NANDFLASH 等。
3、寄存器
Cortex-M3内核是32位的,所以其存储器内部是以四个字节为一个单元,每个单元对应一个地址,也对应着不同的功能,因此通过对应的地址控制这些单元也就可以控制外设。
寄存器:给特定功能的内存单元取一个别名,可用指针来操作内存单元。
寄存器映射:给已经分配好地址的有特定功能的内存单元取别名的过程。
4、STM32 外设地址映射
片上外设区分为四条总线,根据外设速度的不同,不同总线挂载着不同的外设, APB1挂载低速外设,APB2 和 AHB 挂载高速外设。
每条总线的最低地址称为该总线基地址,同时也是挂载在该总线上首个外设地址。
APB1总线的地址最低,因此片上外设就从这这个地址开始,也称外设基地址。
4.1 总线基地址
存储器映射的Block2 分为4块,每块有一个起始地址,称为基地址。总线基地址与片上外设基地址的偏差,叫做相对基地址的偏移量。
4.2 外设基地址
外设GPIOx都是挂接在APB2总线上,属于高速的外设,而APB2总线的基地址是0x4001 0000,故GPIOA的相对APB2总线的地址偏移是 800。
4.3 外设寄存器地址
GPIO是通用输入输出端口的简称,GPIO有很多个寄存器,每一个都有特定的功能。每个寄存器32bit,占四个字节,这些寄存器都是按顺序依次排列在外设的基地址上。寄存器的位置都以相对该外设基地址的偏移地址来描述。
寄存器位功能说明:
两种寄存器位,分别为BRy 及 BSy,其中的y数值表示的是管脚号,可以是 0-15。如BR0、BS0用于控制 GPIOx 的第0个引脚,若x表示GPIOC,就是控制 GPIOC 的第0引脚,而BR1、BS1就是控制GPIOC第1个引脚。
其中BRy引脚的说明:
0:不会对相应的 ODRx 位执行任何操作;
1:对相应 ODRx 位进行复位。
4.4 C语言封装寄存器
//定义外设基地址
#define PERIPH_BASE ((unsigned int)0x40000000)
//定义APB2总线基地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
//定义GPIOC外设基地址
#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800)
//定义寄存器基地址 以GPIOC为例
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR *(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR *(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR *(unsigned int*)(GPIOC_BASE+0x18)
GPIOC_BSRR 的值是这个寄存器的地址,但编译器不知道它是地址,而把它当做立即数,所以要强制转换为(unsigned int * )指针类型才可以对其操作。然后再在前面加上一个“ * ”作取指针操作,表示对该地址内内容进行写,读操作也同样使用“ * ”取指针操作。
unsigned int temp;
temp =GPIOC_IDR;
C 语言中的结构体对寄存器进行封装,具体代码如下:
typedef unsigned int uint32_t; //无符号 32 位变量
typedef unsigned short int uint16_t; //无符号 16 位变量
// GPIO 寄存器列表
typedef struct
{
uint32_t CRL; //GPIO 端口配置低寄存器 地址偏移: 0x00
uint32_t CRH; //GPIO 端口配置高寄存器 地址偏移: 0x04
uint32_t IDR; //GPIO 数据输入寄存器 地址偏移: 0x08
uint32_t ODR; //GPIO 数据输出寄存器 地址偏移: 0x0C
uint32_t BSRR; //GPIO 位设置/清除寄存器 地址偏移: 0x10
uint32_t BRR; //GPIO 端口位清除寄存器 地址偏移: 0x14
uint16_t LCKR; //GPIO 端口配置锁定寄存器 地址偏移: 0x18
}GPIO_TypeDef;
C 语言的语法规定,结构体内变量的存储空间是连续的,其中 32 位的变量占用 4 个字节,16 位的变量占用 2 个字节。
地址偏移与STM32 GPIO外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器。
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
GPIO_TypeDef * GPIOx; //定义GPIO_TypeDef 型结构体指针GPIOx
GPIOx = GPIOC_BASE; //把指针地址设置为宏 GPIOC_BASE 地址
GPIOx->BSRR = (1 << (16 + 0)); //通过指针访问修改GPIOC_BSRR寄存器
编辑 2023.03.21 23:38 首次编辑
注:本文旨于自己的学习笔记,禁止转载。