MK NOTES
电路板与PCBA

STM32F103 UART时钟树配置详解,从原理到代码实战指南

2025-10-08STM32主控芯片
STM32F103 UART时钟树配置详解,从原理到代码实战指南

STM32F103 UART时钟树配置详解,从原理到代码实战指南

系统时钟源与UART外设时钟路径解析

STM32F103微控制器采用高性能ARM Cortex-M3内核,其时钟系统由多个振荡器源构成,主要包括高速内部时钟(HSI)、高速外部时钟(HSE)以及锁相环(PLL)。UART外设的波特率发生器依赖于APB2或APB1总线时钟,具体取决于串口所在的通道位置。在STM32F103系列中,USART1挂载在APB2总线上,而USART2、USART3及UART4等则挂载在APB1总线上。这种物理连接决定了不同串口获取时钟源时的频率差异,APB2通常提供更高的时钟频率以支持高速通信需求,而APB1则服务于较低速的外设。

理解时钟树的核心在于明确RCC(复位和时钟控制)寄存器的作用机制。当系统初始化时,配置时钟树即是确定HSE或HSI经过预分频、倍频后,如何分配给AHB总线、APB1、APB2以及CPU主频。对于UART通信而言,波特率发生器需要一个稳定的时钟输入,该时钟直接来源于所在的APB总线频率。若APB总线频率发生变更,必须重新计算波特率寄存器的值,否则会导致通信数据错误。因此,在代码实现阶段,获取正确的总线时钟频率是配置串口的前提条件。

STM32F103 UART时钟树配置详解,从原理到代码实战指南

波特率发生器计算逻辑与误差分析

波特率发生器由USARTDIV(USART小数部分和整数部分)控制,其计算公式为:USARTDIV = fCLK / (16 Baud)。其中,fCLK为USART时钟频率,Baud为目标波特率。以常见的9600bps波特率为例,若USART1挂载在72MHz的APB2总线上,则USARTDIV值为72000000 / (16 9600) = 468.75。这意味着整数部分为468,小数部分为0.75。在STM32F103中,小数部分占用4位,因此需要将0.75转换为十六进制表示,即0.75 16 = 12(0xC)。这一计算过程必须精确,因为小数部分的精度直接影响波特率的误差范围。

实际应用中,时钟源的不稳定或总线频率配置错误会导致严重的通信失败。STM32F103的HSE频率通常为8MHz,若直接使用此频率作为系统时钟源而不经过PLL倍频,APB2的最大频率仅为24MHz。此时计算9600bps的USARTDIV为24000000 / (16 9600) = 156.25。若开发者误以为系统运行在72MHz却使用24MHz的时钟参数配置,波特率误差将高达50%,导致接收端完全无法识别数据。因此,在代码中动态获取实际总线时钟频率而非依赖宏定义的理论值,是确保通信稳定性的关键措施。

STM32F103 UART时钟树配置详解,从原理到代码实战指南

寄存器级配置流程与关键步骤

配置UART时钟及相关参数需依次操作RCC和USART寄存器。首先启用UART外设所在总线的时钟,对于USART1需设置RCC->APB2EN1寄存器中的USART1EN位,对于USART2则需设置RCC->APB1EN1中的USART2EN位。紧接着,配置GPIO引脚模式,TX引脚设为复用推挽输出,RX引脚设为浮空输入或上拉输入,并通过AFIO寄存器映射对应引脚。这一步骤确保了物理信号能够正确传输至UART模块内部。

随后进行UART外设初始化,核心是设置波特率和数据帧格式。通过USART->CR1寄存器使能发送和接收功能,通过USART->CR2配置停止位,通过USART->BRR寄存器写入计算得出的USARTDIV值。在写入BRR之前,必须确保波特率发生器处于禁用状态或按照特定顺序写入,以避免中间状态导致的数据错误。STM32标准库或HAL库中通常封装了这些寄存器操作,但理解底层寄存器映射有助于排查时钟配置错误,特别是在使用自定义启动文件或裸机开发环境中。

STM32F103 UART时钟树配置详解,从原理到代码实战指南

代码实战:动态时钟获取与初始化实现

在实际工程中,推荐使用标准外设库或HAL库进行开发,但必须包含动态获取时钟频率的逻辑。以下代码片段展示了如何获取USART1的时钟频率并初始化9600bps通信。通过调用SystemCoreClock或专门获取APB2时钟频率的函数,确保计算基准准确。若使用HAL库,HAL_RCC_GetHCLKFreq()可获取系统时钟,而HAL_RCC_GetPCLK2Freq()则直接提供APB2总线频率,这一细节常被初学者忽略,导致直接使用系统主频计算波特率而出错。

c

include "stm32f10x.h"

void UART1_Init(void) {

uint32_t pclk2;

USART_InitTypeDef USART_InitStructure;

// 获取APB2总线时钟频率,USART1挂载在APB2

pclk2 = HAL_RCC_GetPCLK2Freq();

// 使能USART1时钟和GPIOA时钟

__HAL_RCC_USART1_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();

// 配置GPIO: TX->PA9, RX->PA10

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_PULLUP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// 配置USART参数

USART_InitStructure.BaudRate = 9600;

USART_InitStructure.WordLength = USART_WORDLENGTH_8B;

USART_InitStructure.StopBits = USART_STOPBITS_1;

USART_InitStructure.Parity = USART_PARITY_NONE;

USART_InitStructure.Mode = USART_MODE_TX_RX;

USART_InitStructure.HWFlowControl = USART_HWCONTROL_NONE;

// 使用动态获取的pclk2进行初始化,库函数内部会自动处理分频

HAL_UART_Init(&USART1, &USART_InitStructure);

// 使能UART接收中断(可选)

__HAL_UART_ENABLE_IT(&UART1, UART_IT_RXNE);

}

上述代码中,`HAL_RCC_GetPCLK2Freq()`函数确保了无论系统时钟树如何配置(例如从8MHz HSE倍频至72MHz,或降低频率以节省功耗),UART初始化都能基于正确的总线频率工作。若手动计算BRR寄存器值,需使用`pclk2`而非`SystemCoreClock`。此外,GPIO配置中的`GPIO_MODE_AF_PP`模式明确指定了引脚复用功能,这是STM32F103系列中UART TX引脚输出的必要设置,若配置为普通输出模式,则无法产生串行数据信号。