这一章在干嘛?
复习 STM32 嵌入式开发必备的 C 语言(位操作、宏定义、结构体、指针)+ 寄存器本质 + STM32F407 系统架构 + 存储器与寄存器映射。读完后面对 HAL 库「为什么这样封装」会有恍然大悟的感觉。
5.1 C 语言基础知识复习
5.1.1 位操作
嵌入式天天和 bit 打交道:清零 &= ~BIT、置位 |= BIT、取反 ^= BIT、读取 (x & BIT) ? 1 : 0。
5.1.2 define 宏定义
#define PI 3.14 文本替换;带参数 #define MAX(a,b) ((a)>(b)?(a):(b)) 注意整体加括号避免运算符优先级坑。
5.1.3 ifdef 条件编译
头文件防重包含 #ifndef __STM32_H\n#define __STM32_H ... #endif;区分调试与发布 #ifdef DEBUG ... #endif。
5.1.4 extern 外部声明
extern uint8_t g_flag; 告诉编译器「这变量在别的文件里定义」,不要分配空间。多文件工程必备。
5.1.5 typedef 类型别名
typedef unsigned int uint32_t; 让代码可移植、易读。C99/C11 的 <stdint.h> 已经替我们做好:uint8_t/uint16_t/uint32_t/uint64_t/int8_t/.../int64_t。
5.1.6 结构体
把相关变量打包:struct GPIO_TypeDef { uint32_t MODER; uint32_t OTYPER; ... };。访问用 .(普通变量)或 ->(指针变量)。
5.1.7 指针
STM32 大量用「外设寄存器结构体指针」:#define GPIOA ((GPIO_TypeDef *)0x40020000),然后 GPIOA->BSRR = 0x01; 就是点亮 PA0。指针就是地址,地址就是数字,没什么神秘的。
C 语言建议: 把这 7 节内容当字典,不懂就翻,3 天后自然就熟了。
5.2 寄存器基础知识
寄存器 = 特殊功能的内存单元。本质:
(*(unsigned int *)(0x40010C0C)) = 0xFFFF; // 把 GPIOB->ODR 设为 0xFFFF
但可读性差,所以改用结构体:
GPIOB->ODR = 0xFFFF; // 实质等价上一行
寄存器按 32 位组织,每位或几位代表一个开关/参数。命名约定:xxxx_CR1/CR2/CR3 配置寄存器、xxxx_SR 状态寄存器、xxxx_DR 数据寄存器、xxxx_BRR 波特率寄存器。
5.3 STM32F407 系统架构
5.3.1 Cortex M4 内核 & 芯片
Cortex-M4 由 ARM 设计,包含 NVIC(嵌套向量中断控制器)、SysTick、MPU、FPU、DSP 指令等。ST 在此基础上集成自家外设(USART/SPI/…)。
5.3.2 STM32 系统架构
F407 有 8 条主控总线:I-Code、D-Code、S-Bus、DMA、FSMC、AHB、APB1、APB2。
- I-Code:取指令,CPU → Flash
- D-Code:读常量,CPU → Flash
- S-Bus:CPU 访问外设寄存器
- DMA 总线:DMA 搬运数据不经过 CPU
原书图 5.3.2.1:STM32 系统架构框图
5.3.3 存储器映射
整个 4 GB 地址空间划分:
| 地址范围 | 用途 |
|---|---|
| 0x0000_0000~0x1FFF_FFFF | Code 区(Flash 映射) |
| 0x2000_0000~0x3FFF_FFFF | SRAM(192 KB) |
| 0x4000_0000~0x5FFF_FFFF | 外设寄存器 |
| 0x6000_0000~0x7FFF_FFFF | FSMC 扩展(外部 SRAM/NOR) |
| 0x8000_0000~0x9FFF_FFFF | FSMC 扩展(NAND) |
| 0xA000_0000~0xDFFF_FFFF | FSMC 扩展(其它) |
| 0xE000_0000~0xFFFF_FFFF | Cortex-M4 内核(NVIC/SysTick) |
原书图 5.3.3.1:STM32 存储器映射图
记住常用段: RAM 起始 0x20000000;外设起始 0x40000000;Flash 起始 0x08000000(启动后映射到 0x00000000)。
5.3.4 寄存器映射
寄存器描述解读:手册里每个寄存器都有「位定义表」,例如 USART_BRR 寄存器:
| 位 | 名称 | 读写 | 功能 |
|---|---|---|---|
| [3:0] | DIV_Fraction | RW | 小数部分 |
| [15:4] | DIV_Mantissa | RW | 整数部分 |
寄存器映射举例:把 USART1 的 BRR 地址算出:
| 外设基地址 | USART 基地址 | 偏移 | 总地址 |
|---|---|---|---|
| 0x40000000 | 0x40011000 | 0x08 | 0x40011008 |
寄存器地址计算:外设基址 + 偏移;外设基址 = 总线基址 + 总线相对偏移;总线基址 = 总线基址基址 + N×总线间距。
原书图 5.3.4.1:寄存器描述解读示例
stm32f407xx.h 寄存器映射说明:ST 已经把整个 F407 的寄存器映射成结构体,我们直接 USART1->BRR = 0x0683; 即可,不用自己算地址。
寄存器本质是什么?
特殊功能的内存单元,本质上是 32 位/16 位/8 位的内存地址。
STM32 的 I-Code 与 D-Code 总线各管什么?
I-Code 取指令(Flash→CPU),D-Code 读常量(常量数据)。
SRAM 起始地址与外设起始地址分别是?
SRAM 0x20000000,外设 0x40000000。
如何清零与置位某一位?
a &= ~BIT清零,
a |= BIT置位。