前言

寄存器映射是学习MCU的重中之重,是操作寄存器、实现外设功能的基础。


一、什么是寄存器?

寄存器是单片机内部一种特殊的内存,可以实现对单片机各个功能的控制
简单来说:寄存器就是单片机内部的控制机构

二、什么是寄存器映射?

寄存器是特殊的存储器,给寄存器地址命名的过程,就叫寄存器映射。
寄存器映射

三、什么是寄存器位表、以及位功能描述

想要控制寄存器,首先要能看懂参考手册中的寄存器说明,寄存器说明通常包含寄存器位表及位功能描述。
打开参考手册,以GPIOx_ODR寄存器为例。
GPIOx_ODR寄存器

1.寄存器位表

寄存器位表是描述MCU(如STM32)中外设寄存器每一位(Bit)功能、权限及定义的详细说明表格。它通常包含位编号、位名称以及读写权限,是硬件编程的关键参考依据,如下图所示。
位表

2.位功能描述

位功能描述:解释该位的具体作用,例如控制GPIO引脚电平等。若对输出数据寄存器GPIOA_ODR的第0位写1,则PA0端口输出高电平(3.3V),对GPIOA_ODR的第15位写0,则PA15端口输出低电平(0V)。
位功能描述

四、寄存器地址计算

寄存器地址可分为3个部分
1、总线基地址(BUS_BASE_ADDR)
2、外设基于总线基地址的偏移量(PERIPH_OFFSET)
3、寄存器相对外设基地址的偏移量(REG_OFFSET)
寄存器地址=FBUS_BASE_ADDR+ PERIPH_OFFSET+ REG_OFFSET

1.总线基地址(BUS_BASE_ADDR)

总线基地址包括AHB总线,以及与AHB总线桥接的APB1总线、APB2总线的基地址
总线基地址
如何找总线基地址
打开参考手册第2.3节存储器映射
在映射表中找到AHB总线的最下面一行,起始地址0x4001 8000即为AHB总线的基地址。APB1总线、APB2总线的基地址可以通过同样的方式找到。
AHB总线地址
其中,APB1总线的基地址,也叫外设基地址(PERIPH_BASE)
打开数据手册的Memory mapping可知,在Block2对应的外设寄存器,其起始地址即为0x4000 0000。
外设基地址
上表中AHB总线、APB1总线、APB2总线的偏移量,都是相对于外设基地址(APB1总线)来说的。
APB1总线基地址为0x4000 0000,它相对自己的偏移量为0;
APB2总线基地址为0x4001 0000,因此它相对APB1总线的偏移量为0x1 0000;
AHB总线基地址为0x4001 8000,因此它相对APB1总线的偏移量为0x1 8000。

2.外设基于总线基地址的偏移量(PERIPH_OFFSET)

以GPIO为例,由寄存器映射表可知,GPIO都是挂载在APB2总线上的
GPIO外设基地址及偏移量
需要注意的是,此表的偏移量是相对APB2总线基地址(APB2PERIPH_BASE)来说的。
APB2总线基地址
以GPIOA为例,从寄存器映射表可知GPIOA的起始地址为0x4001 0800,因此其相对APB2总线基地址的偏移量为0x800。

3.寄存器相对外设基地址的偏移量(REG_OFFSET)

以GPIOA为例,相关寄存器如下
GPIOA外设基地址及偏移量
此表的偏移量是相对于GPIOA外设基地址(GPIOA_BASE)来说的。
以寄存器GPIOA_ODR为例,其地址为0x4001 080C,因此其相对GPIOA起始地址0x4001 0800的偏移量为0x0C。
通常来说,寄存器相对外设基地址的偏移量会直接在参考手册的寄存器说明中直接给出。
寄存器地址偏移
小结,以GPIOA_ODR为例计算寄存器地址:
1、获取外设挂在哪个总线上面?查:系统结构图,GPIOA挂载在APB2总线上
2、获取总线基地址,APB2总线基地址:0X40010000
3、获取外设地址偏移量,GPIOA相对APB2总线偏移量是:0X800
4、获取寄存器地址偏移量,ODR相对GPIOA外设基地址的偏移量是:0X0C
因此,根据公式:寄存器地址=FBUS_BASE_ADDR+ PERIPH_OFFSET+ REG_OFFSET
有ODR=0X4001 0000+0X800+0X0C=0X4001 080C

五、寄存器映射方法

1.直接操作寄存器地址

*(unsigned int *)(0x4001 080C)=0XFFFF;

unsigned int占用4个字节,即32位。地址0x4001 080C对应GPIOA_ODR寄存器,对其赋值0XFFFF,就是控制PA0~PA15端口输出高电平。

2.定义一个名字后再操作

#define  GPIOA_ODR    *(unsigned int *)(0x4001 080C)
GPIOA_ODR = 0XFFFF;

3.使用结构体,完成对寄存器的映射

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;
#define  GPIOA_BASE     0X4001 0800
#define  GPIOA    	  	((GPIO_TypeDef*)GPIOA BASE)
// &GPIOA->CRH: 0X4001 0800
// &GPIOA->IDR: 0X4001 0804
// &GPIOA->CRL: 0X4001 0808
// &GPIOA->ODR: 0X4001 080C
GPIOA->ODR = 0XFFFF;

以GPIOA为例,通过使用结构体,一次性映射7个寄存器,每个寄存器占用4Byte,该结构体共占用28Byte。
关于总线及寄存器的基地址,可以在stm32f103xe.h文件中找到,使用结构体对寄存器映射是操作库函数的基础。

六、思考

1.为什么STM32F103的外设寄存器大多是32位的?

答:为了与内核架构相匹配
32位处理器核心​​:Cortex-M3是一个32位处理器,其内部数据路径、ALU(算术逻辑单元)和总线接口均为32位宽。令寄存器位宽与内核位宽一致,可以最大化利用硬件资源,避免数据截断或拼接带来的性能损耗。
​​哈佛总线结构​​:Cortex-M3采用改进型哈佛架构,指令总线(I-Code)和数据总线(D-Code)均为32位,32位寄存器能直接匹配总线传输效率,实现单周期完成数据加载或存储。
但需要注意的是,STM32F103的寄存器并不全是32位的,其位宽取决于寄存器类型和功能。

2.为什么可以使用结构体对寄存器映射?

答:因为结构体在内存中是以连续形式存在的。结构体中的各个成员在内存中按照声明的顺序依次存放,它们占据连续的内存空间。每个成员会被分配到特定的地址位置,可能存在内存对齐导致的填充字节,但整体上结构体的内存布局是连续的。
在使用结构体对寄存器映射时,寄存器大多是32位,且寄存器相对外设基地址的偏移量是对齐的,因此结构体成员变量可以以4字节对齐,分配一片连续的内存空间。结构体所占用内存大小为4Byte*结构体成员变量的个数。


总结

寄存器映射是STM32底层开发的核心技术,理解地址计算和映射方法后,可灵活选择直接操作、宏定义或结构体方式编写高效硬件控制代码。实际项目中建议结合库函数与寄存器操作,平衡可维护性与性能。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐