1. STM32定时器基础与项目场景解析第一次接触STM32定时器时我被手册里那些专业术语搞得晕头转向。直到用TIM2做了个LED呼吸灯才真正理解定时器的强大。STM32F103C8T6这颗芯片虽然属于入门级但它的4个定时器TIM1-TIM4足以应对大多数嵌入式场景。通用定时器TIM2就像个多功能瑞士军刀内部包含16位向上/向下计数器CNT可编程预分频器PSC自动重装载寄存器ARR触发控制器在工业自动化项目中我常用它做两种工作内部时钟模式用72MHz主频做精确计时比如每500ms采集一次传感器数据ETR外部时钟模式通过PA0引脚接收光电编码器脉冲统计电机转速最近做的包装机项目就同时用到了这两种模式先用内部时钟生成1ms系统心跳再用ETR模式统计流水线上的产品数量。下面我就分享具体实现方法包含踩过的坑和优化技巧。2. 内部时钟模式精确定时实战2.1 时钟树配置要点刚开始调TIM2时我误以为APB1总线的36MHz就是时钟上限。后来发现STM32有个精妙设计当APB1预分频系数≠1时定时器时钟会自动×2。所以TIM2实际得到的时钟是72MHz这也是计算时间基准的关键。配置1ms定时中断的步骤如下// 开启TIM2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 选择内部时钟源 TIM_InternalClockConfig(TIM2); // 时基单元配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_TimeBaseInitStruct.TIM_ClockDivision TIM_CKD_DIV1; // 不分频 TIM_TimeBaseInitStruct.TIM_CounterMode TIM_CounterMode_Up; // 向上计数 TIM_TimeBaseInitStruct.TIM_Period 1000 - 1; // ARR值 TIM_TimeBaseInitStruct.TIM_Prescaler 72 - 1; // PSC值 TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStruct);这里有个易错点Period和Prescaler写入的值实际是ARR-1和PSC-1。计算公式如下定时时间 (ARR 1) × (PSC 1) / 72MHz所以上述配置得到(1000)×(72)/72MHz 1ms2.2 中断配置技巧在调试时发现上电瞬间总会误触发一次中断。解决方法是在初始化后手动清除标志位TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除更新标志 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断 // NVIC配置 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel TIM2_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 1; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStruct); TIM_Cmd(TIM2, ENABLE); // 启动定时器中断服务函数里要注意及时清除标志位void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { // 用户代码区 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }3. ETR外部触发模式深入解析3.1 硬件连接方案在智能仓储项目中我用ETR模式统计传送带货物数量。光电传感器输出接PA0TIM2_ETR引脚电路设计要注意信号线加10K上拉电阻并联100pF电容滤波必要时加光耦隔离GPIO配置为浮空输入模式RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStruct);3.2 模式2配置详解ETR模式2的特点是时钟直接驱动计数器适合高频脉冲计数。下面配置每10个脉冲触发中断TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, // 无预分频 TIM_ExtTRGPolarity_NonInverted, // 上升沿有效 0x0F); // 最大滤波 TIM_TimeBaseInitStruct.TIM_Period 10 - 1; // 计数到9 TIM_TimeBaseInitStruct.TIM_Prescaler 1 - 1; // 每个脉冲计1次 TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStruct);实际测试发现当传感器输出信号有抖动时计数值会异常增加。解决方法有两种调整滤波参数示例中设为0x0F在TIM_ETRClockMode2Config中启用预分频4. 两种模式切换的工程实践4.1 动态重配置方案在自动灌装生产线上需要根据工况切换定时模式。通过以下函数可动态修改时钟源// 切换到内部时钟 void SwitchToInternalClock(void) { TIM_ETRConfig(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0); TIM_InternalClockConfig(TIM2); TIM_SetCounter(TIM2, 0); } // 切换到ETR模式 void SwitchToETRMode(void) { TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); TIM_SetCounter(TIM2, 0); }注意切换时要先停止计数器配置完成后再重新使能。4.2 性能优化技巧最小化中断处理时间在中断服务函数中只置标志位具体处理放在主循环使用DMA传输对于高频脉冲计数可配置TIM_DMACmd启用DMA灵活运用从模式TIM2可作为其他定时器的触发源在最近的水表项目中结合输入捕获和ETR模式实现了流量计信号的双重校验。具体做法是用ETR统计总脉冲数同时用输入捕获测量瞬时频率两者数据互相校验提高可靠性。
STM32F103C8T6定时器实战:从内部时钟到ETR外部触发的精准定时设计
发布时间:2026/6/18 14:25:39
1. STM32定时器基础与项目场景解析第一次接触STM32定时器时我被手册里那些专业术语搞得晕头转向。直到用TIM2做了个LED呼吸灯才真正理解定时器的强大。STM32F103C8T6这颗芯片虽然属于入门级但它的4个定时器TIM1-TIM4足以应对大多数嵌入式场景。通用定时器TIM2就像个多功能瑞士军刀内部包含16位向上/向下计数器CNT可编程预分频器PSC自动重装载寄存器ARR触发控制器在工业自动化项目中我常用它做两种工作内部时钟模式用72MHz主频做精确计时比如每500ms采集一次传感器数据ETR外部时钟模式通过PA0引脚接收光电编码器脉冲统计电机转速最近做的包装机项目就同时用到了这两种模式先用内部时钟生成1ms系统心跳再用ETR模式统计流水线上的产品数量。下面我就分享具体实现方法包含踩过的坑和优化技巧。2. 内部时钟模式精确定时实战2.1 时钟树配置要点刚开始调TIM2时我误以为APB1总线的36MHz就是时钟上限。后来发现STM32有个精妙设计当APB1预分频系数≠1时定时器时钟会自动×2。所以TIM2实际得到的时钟是72MHz这也是计算时间基准的关键。配置1ms定时中断的步骤如下// 开启TIM2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 选择内部时钟源 TIM_InternalClockConfig(TIM2); // 时基单元配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_TimeBaseInitStruct.TIM_ClockDivision TIM_CKD_DIV1; // 不分频 TIM_TimeBaseInitStruct.TIM_CounterMode TIM_CounterMode_Up; // 向上计数 TIM_TimeBaseInitStruct.TIM_Period 1000 - 1; // ARR值 TIM_TimeBaseInitStruct.TIM_Prescaler 72 - 1; // PSC值 TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStruct);这里有个易错点Period和Prescaler写入的值实际是ARR-1和PSC-1。计算公式如下定时时间 (ARR 1) × (PSC 1) / 72MHz所以上述配置得到(1000)×(72)/72MHz 1ms2.2 中断配置技巧在调试时发现上电瞬间总会误触发一次中断。解决方法是在初始化后手动清除标志位TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除更新标志 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断 // NVIC配置 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel TIM2_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority 1; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStruct); TIM_Cmd(TIM2, ENABLE); // 启动定时器中断服务函数里要注意及时清除标志位void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { // 用户代码区 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }3. ETR外部触发模式深入解析3.1 硬件连接方案在智能仓储项目中我用ETR模式统计传送带货物数量。光电传感器输出接PA0TIM2_ETR引脚电路设计要注意信号线加10K上拉电阻并联100pF电容滤波必要时加光耦隔离GPIO配置为浮空输入模式RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStruct);3.2 模式2配置详解ETR模式2的特点是时钟直接驱动计数器适合高频脉冲计数。下面配置每10个脉冲触发中断TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, // 无预分频 TIM_ExtTRGPolarity_NonInverted, // 上升沿有效 0x0F); // 最大滤波 TIM_TimeBaseInitStruct.TIM_Period 10 - 1; // 计数到9 TIM_TimeBaseInitStruct.TIM_Prescaler 1 - 1; // 每个脉冲计1次 TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStruct);实际测试发现当传感器输出信号有抖动时计数值会异常增加。解决方法有两种调整滤波参数示例中设为0x0F在TIM_ETRClockMode2Config中启用预分频4. 两种模式切换的工程实践4.1 动态重配置方案在自动灌装生产线上需要根据工况切换定时模式。通过以下函数可动态修改时钟源// 切换到内部时钟 void SwitchToInternalClock(void) { TIM_ETRConfig(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0); TIM_InternalClockConfig(TIM2); TIM_SetCounter(TIM2, 0); } // 切换到ETR模式 void SwitchToETRMode(void) { TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); TIM_SetCounter(TIM2, 0); }注意切换时要先停止计数器配置完成后再重新使能。4.2 性能优化技巧最小化中断处理时间在中断服务函数中只置标志位具体处理放在主循环使用DMA传输对于高频脉冲计数可配置TIM_DMACmd启用DMA灵活运用从模式TIM2可作为其他定时器的触发源在最近的水表项目中结合输入捕获和ETR模式实现了流量计信号的双重校验。具体做法是用ETR统计总脉冲数同时用输入捕获测量瞬时频率两者数据互相校验提高可靠性。