STM32F103R6T中HAL_GetTick精度异常问题求助
问题现象
在STM32F103R6T上通过TIM2周期中断实现自定义毫秒计数器milliseconds,每次中断加1。系统时钟配置为64MHz(APB1=32MHz、APB2=64MHz),TIM2预分频器设为127,周期值设为500。示波器测试中断触发的引脚翻转得到预期1ms半周期,但与HAL_GetTick()对比输出后,发现HAL_GetTick()计时速度更快,二者差值随时间持续增大,实测HAL_GetTick()周期为998us而非预期1ms。
原因分析
1. TIM2中断周期计算误差
STM32F1系列中,挂载在APB1的定时器(如TIM2),当APB1预分频系数不为1时,定时器时钟为APB1时钟×2。此处APB1分频为HCLK/2(HCLK=64MHz),因此TIM2时钟为64MHz。
定时器中断周期公式为:中断周期 = (预分频器值 + 1) × (自动重装载值 + 1) / 定时器时钟频率
当前配置代入计算:(127+1)×(500+1)/64MHz = 128×501/64000000 = 1.002ms
即自定义计数器每1.002ms才加1,而HAL_GetTick()实际周期为998us,因此HAL_GetTick()的数值增长更快,差值逐渐扩大。
2. HAL_GetTick()的HSI时钟精度问题
HAL_GetTick()基于SysTick定时器,其时钟来自系统核心时钟(64MHz),而系统时钟由HSI(内部RC振荡器)经PLL倍频得到。HSI本身精度有限(典型误差±1%),实际频率可能偏离8MHz,导致PLL输出与理论64MHz存在偏差,最终使SysTick周期偏离1ms。
解决办法
1. 修正TIM2周期值,确保中断周期为1ms
根据公式反推正确的自动重装载值:
总计数周期 = 定时器时钟频率 × 目标中断周期 = 64MHz × 1ms = 64000
自动重装载值 = (总计数周期 / (预分频器值 + 1)) - 1 = (64000/128) -1 = 499
修改MX_TIM2_Init()中的周期配置:
htim2.Init.Period = 499;
2. 优化HAL_GetTick()计时精度(可选)
- 使用外部晶振(HSE)替代HSI:外部晶振精度远高于内部RC,能大幅降低时钟误差。
- 校准HSI:通过调整
RCC_OscInitStruct.HSICalibrationValue校准HSI频率,减小偏差。
3. 变量类型统一(可选)
将自定义计数器milliseconds改为uint32_t类型,与HAL_GetTick()返回值类型保持一致,避免潜在的符号问题:
uint32_t milliseconds = 0;
关键修改代码片段
中断回调函数
uint32_t milliseconds = 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9); milliseconds++; }
TIM2初始化函数
static void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 127; // 分频系数=127+1=128 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 499; // 修正后的值,确保1ms中断 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } }
内容的提问来源于stack exchange,提问作者Spitfire1.2

