stm32f103学习笔记-5-寄存器结构体定义
第一部分:上章笔记回顾——从#define到结构体
下面代码展示了两种访问STM32寄存器的方法:
方法一:使用#define进行直接地址访问(上章笔记讲述的方式)
#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
// ... 其他寄存器定义
这是什么?
这是一种最基础的寄存器映射。通过计算每个寄存器相对于外设基地址的偏移量,直接定义了一个指向该内存地址的指针。操作这些宏,就等于直接读写对应的物理寄存器。
-
优点:直观,最接近硬件。
-
缺点:
-
代码冗长:每个寄存器都需要一行
#define。 -
容易出错:偏移地址需要手动计算和填写,容易写错。
-
可读性差:在代码中使用时,是一串独立的宏,不能很好地体现寄存器属于哪个外设。
-
GPIOB_BASE端口B外设的基地址,上一章节已经定义好了,这个地址在C语言中,是指针且为32位,指向1个4个字节的内存单元,指针P即为GPIOB_BASE的地址,同时也指向GPIOB_BASE的第一个地址。
方法二:使用结构体进行系统化访问(本章笔记采用的方法)
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef struct {
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
} GPIO_TypeDef;
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
这是什么?
这是一种高级的寄存器映射方法。它利用了C语言结构体的特性:结构体内的成员在内存中是连续且顺序存放的。
-
定义了一个
GPIO_TypeDef结构体,其成员变量(CRL,CRH...)的顺序和类型,完全按照官方手册中GPIO寄存器的排列顺序来设计。 -
然后将
GPIOB_BASE(GPIOB端口的基础地址)强制类型转换为一个指向GPIO_TypeDef结构体的指针GPIOB。
这样做的神奇效果是:
GPIOB->CRL就等价于 *(unsigned int*)(GPIOB_BASE + 0x00)
GPIOB->ODR就等价于 *(unsigned int*)(GPIOB_BASE + 0x0C)
编译器会自动根据结构体成员的顺序和类型来计算偏移地址。
GPIOB是GPIO_TypeDef类型的指针,指向整个一大段内存,内存里有GPIOB下面的所有寄存器
第二部分:这种转换带来的巨大好处
-
代码极其简洁
一行
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)就完成了之前7行代码的工作。对于有数十个寄存器的大型外设(如USART、TIMER),这种优势是压倒性的。 -
可读性和可维护性大幅提升
-
GPIOB->ODR = 0xFFFF;一看就知道是在操作GPIOB的输出数据寄存器。 -
GPIOA->CRL |= 0x00000001;一看就知道是在配置GPIOA的某个引脚。代码的意图非常清晰,更像是在描述“做什么”,而不是“怎么做”。
-
-
避免人为错误
不再需要手动计算和填写
0x00,0x04,0x08等偏移量。只要结构体定义正确,所有地址计算都由编译器自动完成,彻底避免了计算错误和笔误。 -
为过渡到官方固件库打下坚实基础
官方STM32固件库(Standard Peripheral Library)以及现在主流的HAL/LL库,其最核心的基础正是——用结构体来描述整个外设。
第三部分:GPIOB的内涵详解
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)这行代码定义了一个宏。
它的内涵是:
GPIOB是一个常量指针,其值被固定为 GPIOB_BASE(GPIOB外设的基地址),并且这个指针被解释为指向 GPIO_TypeDef结构体类型的。
可以这样理解:
-
GPIOB:是整个GPIOB外设的“句柄”或“总管”。 -
GPIOB->CRL:是命令“总管”去操作其内部的端口配置低寄存器。 -
GPIOB->IDR:是命令“总管”去读取其内部的输入数据寄存器。
这样能够以一个整体视角来管理和操作一整个外设,而不是面对一堆分散的地址。
第四部分:如何参考官方手册写程序
学习方法:
-
找到外设章节:比如要用GPIO,就看第8章;要用USART,就看第25章。
-
找到寄存器描述小节:例如8.2 GPIO寄存器描述。这里会逐个寄存器详细说明其功能、每一个比特位的作用、复位值等。这是你编程时配置什么值的依据。
-
找到寄存器地址映射表:例如8.5节的表52。这是定义结构体的根本依据。
GPIO_TypeDef结构体里的成员顺序,必须严格照搬这个表格的顺序。
|
偏移地址 |
寄存器名称 |
你的结构体成员 |
|---|---|---|
|
0x00 |
CRL |
|
|
0x04 |
CRH |
|
|
0x08 |
IDR |
|
|
0x0C |
ODR |
|
|
0x10 |
BSRR |
|
|
0x14 |
BRR |
|
|
0x18 |
LCKR |
|

更多推荐


所有评论(0)