存储器与寄存器
存储器映射
存储器本身不具备地址信息,是被分配的,这个过程叫做存储器映射,再被映射一次,则是重映射
4G区域平均分为8块,每块是512MB,非常大,STM32只用了其中一部分
block0、block1、block2这三块是重点,block2主要是外设部分,block0主要是flash部分
block0从下往上看
第一块:引脚
第二块:预留
第三块:512kb 存放程序的 flash (0x0800 0000~0x0807 FFFF)
第四块:预留
第五块:系统存储器 无法更改
第六块:选项字节 RAM 若芯片被锁住时,则在这里修改
block1从下往上看
第一块:SRAM 64kb (0x2000 0000~0x2000 FFFF)
第二块:预留
block2主要是设置片内的外设部分
AHB:高速总线 72M
APB:APB1频率低 36M;APB2:72M
总线上面可以挂接很多外设,每个外设也会有自己的地址范围,例如TIM3挂在APB1总线上,它的地址范围为:(0x4000 0400~0x4000 07FF)则TIM3的基地址为0x4000 0400
block3、block4、block5包括FSMC扩展区域
以上就是存储器的映射图
寄存器和寄存器映射
我们把每个单元的功能作为名,给这个内存取一个别名,这个别名就是我们经常说的寄存器,然后通过C语言指针来操作这些寄存器即可。
给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射
是一个存储空间,32个bit位,4个字节,地址很容易索引到
改变寄存器的某些位置的值,就可以改变GPIO(硬件)的性质,是程序和硬件沟通的桥梁
对寄存器的读写操作就是对GPIO(硬件)的映射
外设的寄存器就分布在外设的对应地址上,以GPIOB端口为例,其他端口同理
每个寄存器都是32位的,占用4个字节(一个字节是8位的)
GPIOB的地址范围为:(0x4001 0C00~0x4001 0FFF)
STM32的中文参考手册,找到GPIO寄存器描述部分
图中第一个寄存器为:端口配置低寄存器(GPIOB_CRL)
第二个寄存器为:端口配置高寄存器(GPIOB_CRH),一个寄存器为4位,则偏移量为0x04)
以第五个寄存器为例:(GPIOB_BSRR)端口位设置/清除寄存器
地址偏移:0x10,则寄存器地址为:(0x4001 0C10)
复位值为:0x0000 0000 x为A~E,表示每个端口
0~31代表每个位,共32位,四个字节
w表示只能写 r代表只能读 rw表示既能读,也能写
如何访问STM32寄存器内容
总线和外设基地址封装
例子:使用GPIOB第五个引脚输出低电平
我们知道GPIOB是挂在APB2高速总线上的,该总线的基地址是0x4001 0000,GPIOB的偏移地址为0x0000 0C00,则GPIOB的外设基地址为0x4001 0C00
block2的外设基地址是0x4000 0000,即PERIPH_BASE
APB2总线的偏移量是0x0001 0000,则APB2PERIPH_BASE为PERIPH_BASE+0x0001 0000
GPIOB端口相对于APB2总线的基地址的偏移量为0x0000 0C00,则GPIOB_BASE为APB2PERIPH_BASE+0x0C00
GPIOB端口中寄存器的地址,每个寄存器的偏移量分别为0x00、0x04、0x08、0x0C、0x10、0x14、0x18
#define PERIPH_BASE ((unsigned int)0x40000000) //外设基地址
#define APB2PERIPH_BASE (PERIPH_BASE+0x00010000) //APB2总线的基地址
#define GPIOB_BASE (APB2PERIPH_BASE+0x0C00) //GPIOB端口的基地址
#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE+0x00)
#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE+0x04)
#define GPIOB_TDR *(unsigned int*)(GPIOB_BASE+0x08)
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE+0x10)
#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE+0x14)
#define GPIOB_LCKR *(unsigned int*)(GPIOB_BASE+0x18)
GPIOB_BASE+0x00这个只是一个值,不是地址,通过unsigned int*,将它强制定义为一个无符号整型的指针,然后再加一个指针,访问它的内容,指针的指针,就是地址里面的值
例如要控制GPIOB5输出一个低电平或者高电平
*(unsigned int*)GPIOB_BSRR = (0x01<<(16+5)); //输出低电平
*(unsigned int*)GPIOB_BSRR = (0x01<<5); //输出高电平
首先先将GPIOB_BSRR通过unsigned int*变成一个地址,然后通过一个指针符号,变成地址的值
从GPIOB_BSRR寄存器中32位的设置可以看出:高16位是用来置零的,也就是输出低电平的;低16位是用来置一的,也就是输出高电平的
要输出低电平时,因为是第五个引脚,所以是+5;因为是要输出低电平,且高16位是置零的,所以是16+5
因为0对对应的位不产生影响,设置1会置零或置一,所以是0x01
例如要读取GPIOB管脚所有电平
unsigned int temp;
temp *(unsigned int *)GPIOB_IDR
GPIOB_IDR是端口输入数据寄存器,将GPIOB_IDR的值通过unsigned int *变成地址,然后再加一个指针变成地址的值,然后保存到temp中存储
寄存器封装
typedef unsigned int uint32_t; //重定义无符号整型为 uint32_t 4个字节
typedef unsigned short int uint16_t; //2个字节
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t TDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
//设置GPIOB5为低电平
GPIO_TypeDef * GPIOx; //定义一个指针变量
GPIOx = GPIOB_BASE; //前面已经定义过了,只是一个值,强制变成一个地址
GPIOx->BSRR = (1<<(16+5)); //结构体的语法访问BSRR成员变量
//使用宏定义来设置
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
GPIOB->BSRR = (1<<(16+5));
typedef是重定义,定义了一个结构体,地址是按顺序存储的,只需设置结构体的首地址即可
后期是使用ST官方提供的一个库,会更方面