3. 寄存器映射与时钟树
在裸机串口驱动开发中,寄存器映射是软件与硬件交互的桥梁,时钟树是外设工作的能量来源。本章将深入剖析STM32F1系列(以F103为例)的GPIO复用功能配置、USART时钟使能路径以及波特率计算公式,帮助读者建立从寄存器位操作到硬件时序的完整认知。
3.1 寄存器映射基础与GPIO复用功能配置
STM32的寄存器被映射到固定的内存地址空间,C语言通过指针或结构体直接访问这些地址。GPIO的复用功能(Alternate Function, AF)配置是串口通信的第一步,需要将USART的TX/RX引脚连接到对应的GPIO端口。
3.1.1 GPIO复用功能配置步骤
- 使能GPIO时钟:通过RCC_APB2ENR寄存器使能对应GPIO端口时钟。
- 配置引脚模式:设置GPIOx_CRL(低8位)或GPIOx_CRH(高8位)寄存器,将引脚配置为复用推挽输出(TX)和浮空输入(RX)。
- 选择复用功能:对于STM32F1,复用功能由GPIOx_AFIO寄存器或直接由外设映射决定(F1的USART1默认映射到PA9/PA10)。
关键寄存器:GPIOx_CRL/CRH
- MODE位:设置引脚速度(10MHz、50MHz等)
- CNF位:设置引脚模式(输入、输出、复用功能、模拟)
- 对于USART TX:CNF=10(复用推挽输出),MODE=11(50MHz)
- 对于USART RX:CNF=01(浮空输入),MODE=00(输入模式)
- MODE位:设置引脚速度(10MHz、50MHz等)
- CNF位:设置引脚模式(输入、输出、复用功能、模拟)
- 对于USART TX:CNF=10(复用推挽输出),MODE=11(50MHz)
- 对于USART RX:CNF=01(浮空输入),MODE=00(输入模式)
// 示例:配置USART1的TX(PA9)和RX(PA10)
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟
// 配置PA9为复用推挽输出(50MHz)
GPIOA->CRH &= ~(0xF << 4); // 清除CNF9和MODE9位
GPIOA->CRH |= (0xB << 4); // CNF=10, MODE=11
// 配置PA10为浮空输入
GPIOA->CRH &= ~(0xF << 8); // 清除CNF10和MODE10位
GPIOA->CRH |= (0x4 << 8); // CNF=01, MODE=00
避坑提示:STM32F1的USART1默认映射到PA9(USART1_TX)和PA10(USART1_RX),无需额外配置AFIO重映射。若使用其他USART(如USART2/3),需检查数据手册确认引脚映射,并可能需配置AFIO寄存器。
3.1.2 GPIO复用功能配置流程图
3.2 USART时钟使能:APB1与APB2总线
STM32的时钟树通过RCC(Reset and Clock Control)模块管理。USART外设挂载在不同的APB总线上,需要正确使能对应的时钟位。USART1挂载在APB2总线(最高72MHz),USART2/3挂载在APB1总线(最高36MHz)。
3.2.1 时钟使能寄存器对比
| 外设 | 挂载总线 | 时钟使能寄存器 | 使能位 | 最大时钟频率 |
|---|---|---|---|---|
| USART1 | APB2 | RCC_APB2ENR | 位14 (USART1EN) | 72 MHz |
| USART2 | APB1 | RCC_APB1ENR | 位17 (USART2EN) | 36 MHz |
| USART3 | APB1 | RCC_APB1ENR | 位18 (USART3EN) | 36 MHz |
// 使能USART1时钟(APB2总线)
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// 使能USART2时钟(APB1总线)
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
时钟配置陷阱:APB1总线的最大频率为36MHz,若系统时钟(HCLK)为72MHz,则APB1预分频器必须设置为2分频。USART2/3的波特率计算需使用36MHz作为输入时钟,而非72MHz。务必检查RCC_CFGR寄存器的PPRE1位。
3.2.2 USART时钟树结构图
3.3 波特率计算公式与USART_BRR寄存器详解
波特率(Baud Rate)决定了串口通信的速率。STM32的USART波特率通过USART_BRR寄存器配置,其值由系统时钟和过采样模式决定。
3.3.1 波特率计算公式
USART的波特率计算公式如下:
波特率公式:
BaudRate = f_CK / (16 * USARTDIV)
其中:
- f_CK:USART输入时钟频率(APB1或APB2时钟)
- USARTDIV:USART_BRR寄存器的值(定点数,整数部分+小数部分)
- 16:过采样率(默认模式,也可配置为8倍过采样)
BaudRate = f_CK / (16 * USARTDIV)
其中:
- f_CK:USART输入时钟频率(APB1或APB2时钟)
- USARTDIV:USART_BRR寄存器的值(定点数,整数部分+小数部分)
- 16:过采样率(默认模式,也可配置为8倍过采样)
USARTDIV的计算公式:
USARTDIV = f_CK / (16 * BaudRate)
例如:f_CK = 72MHz, BaudRate = 115200
USARTDIV = 72000000 / (16 * 115200) = 39.0625
3.3.2 USART_BRR寄存器结构
| 位域 | 名称 | 描述 |
|---|---|---|
| 15:4 | DIV_Mantissa | USARTDIV的整数部分(12位) |
| 3:0 | DIV_Fraction | USARTDIV的小数部分(4位,精度1/16) |
USART_BRR寄存器的值计算:
USARTDIV = 39.0625
DIV_Mantissa = 39 (0x27)
DIV_Fraction = 0.0625 * 16 = 1 (0x1)
USART_BRR = (39 << 4) | 1 = 0x271
小数部分计算技巧:小数部分 = round(小数 * 16)。例如0.0625 * 16 = 1.0,取整为1。若计算结果为0.5,则四舍五入。误差过大会导致通信失败,建议使用整数运算避免浮点。
3.3.3 波特率配置代码示例
// 计算并设置USART1波特率为115200(f_CK=72MHz)
uint32_t f_ck = 72000000; // USART1时钟频率
uint32_t baud = 115200; // 目标波特率
// 计算USARTDIV = f_ck / (16 * baud)
uint32_t usartdiv = (f_ck + (8 * baud)) / (16 * baud); // 四舍五入
// 提取整数和小数部分
uint16_t mantissa = usartdiv / 16;
uint16_t fraction = usartdiv % 16;
// 写入USART_BRR寄存器
USART1->BRR = (mantissa << 4) | fraction;
3.3.4 波特率误差分析表
| 目标波特率 | f_CK=72MHz | USARTDIV | 实际波特率 | 误差 |
|---|---|---|---|---|
| 9600 | 72MHz | 468.75 | 9600 | 0% |
| 115200 | 72MHz | 39.0625 | 115200 | 0% |
| 250000 | 72MHz | 18.0 | 250000 | 0% |
| 921600 | 72MHz | 4.8828 | 921600 | 0% |
误差警告:当f_CK不是16*BaudRate的整数倍时,小数部分舍入会产生误差。误差超过2%可能导致通信不稳定。建议使用整数运算计算USART_BRR,并验证实际波特率误差。
3.3.5 波特率计算流程图
3.4 本章小结
- GPIO复用功能配置:通过GPIOx_CRL/CRH寄存器设置引脚模式,TX为复用推挽输出,RX为浮空输入。
- 时钟使能:USART1使用APB2时钟(RCC_APB2ENR),USART2/3使用APB1时钟(RCC_APB1ENR),注意APB1最大频率限制。
- 波特率计算:使用USARTDIV = f_CK / (16 * BaudRate)计算,通过USART_BRR寄存器的整数和小数部分配置。
- 误差控制:使用整数运算四舍五入,确保误差在2%以内。