1. 访问被控单元
STM32地址总线为32位,寻址的最大空间数为4G(2^32 = 4G),地址范围:0x00000000 ~ 0xFFFFFFFF;
FLASH、SRAM、FSMC、片上外设和核内外设被有序组织在4G空间上,对其地址分配如下,通过对地址的读写即可访问被控单元;
Block0用于设计片内FLASH、Block1用于设计片内SRAM、Block2即用于片内外设
2. Block2
Block2用于设计片上外设,片上外设根据总线速度不同分别挂载在AHB、APB1、APB2上,Block2也被划分成三部分;
外设的行为与Block2上一个个内存单元绑定,通过操作这些内存单元就能驱动外设;
3. 寄存器
寄存器即Block2上的内存单元,STM32规定这些内存单元大小为4字节,针对内存单元对应的功能给其取名xxx寄存器;
以官方手册中的GPIOx_ODR寄存器为例:对Block2中某4个字节写入不同的值,将影响GPIO引脚pin0~pin15输出电平的高低,因此官方把这四个字节称为GPIOx_ODR(端口输出数据寄存器)
4. 寄存器地址
要操作寄存器,首先要明确寄存器对应的内存单元的地址,常常通过“基地址+偏移地址”的方式确定寄存器地址
(1)基地址与偏移地址
被动单元有序的组织在4G空间上,外设即在Block2上有序排列,假设第一个外设PERPH_0的地址为0xA,外设PERPH_1相对PERPH_0的偏移为0xB,那么0xA+0xB即PERPH_1的地址,这里0xA就是基地址,0xB就是偏移地址
(2)STM32外设地址偏移规律
Block2中按速度依次划分APB1、APB2和AHB总线区域,每个区域的基地址称为总线基地址;
APB1总线基地址就是Block2的起始地址,也叫做外设基地址,APB2相对APB1基地址偏移0x0001 0000,AHB相对APB1基地址偏移0x0001 8000
外设常按所属区域,即按所挂载总线的基地址设置区域内偏移;
寄存器按所属外设,设置相对外设基地址的偏移;
(3)举例
以GPIOx_ODR为例,在官方手册中其地址描述为:
GPIO挂载在总线APB2上,APB2总线基地址为0x4001 0000,GPIO分为A~E组,外设GPIOB相对APB2总线的地址偏移为0x0000 0C00,寄存器GPIOB_ODR相对外设GPIOB的地址偏移为0x0C,因此0x4001 0C0C即为寄存器GPIOB_ODR的地址
上述对基地址与偏移地址的说明是为了更好的理解STM32外设的地址划分,实际官方手册没有这么简略,手册中直接给出了外设的基地址,只需要用外设基地址+寄存器偏移即可:
5、编程实现
熟悉C语言的话,知道通过*解引用,即可对地址对应空间读写:
*(unsigned int *)(0x4001 0C0C) = 0xFF; // 对GPIOB_ODR的低16位赋值1
为增加代码可读性,可以优化:
#define GPIOB_BASE ((unsigned int)0x4001 0C00)
#define GPIOB_ODR *(unsigned int *) (GPIOB_BASE + 0X0C)
GPIOB_ODR = 0XFF;
通过结构体,优化访问:
typedef struct{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint16_t LCKR;
} GPIO_TypeDef;
#define GPIOB_BASE ((unsigned int)0x4001 0C00)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
GPIOB->ODR = 0xFF;