1. STM32通用定时器基础入门
大家好,我是吹雪,今天我们来聊聊STM32的通用定时器。作为嵌入式开发中最常用的外设之一,定时器在项目中几乎无处不在。无论是精确延时、PWM生成,还是输入捕获测量,都离不开它。
STM32的通用定时器(如TIM2-TIM5)是一个16位可编程预分频器驱动的自动重装载计数器。简单来说,就像是一个可以精确计时的秒表,但功能强大得多。它不仅能计数,还能产生各种波形、测量信号频率和脉宽,甚至多个定时器之间还能协同工作。
我记得刚开始接触定时器时,最困惑的就是那一堆寄存器配置。后来发现,只要理解了几个核心概念,一切都变得简单了:
- 时钟源:定时器需要时钟信号才能工作,可以是内部时钟(72MHz)、外部引脚输入或其它定时器触发
- 预分频器(PSC):将时钟频率分频,获得更低的计数频率
- 自动重装载值(ARR):决定计数器的上限,即计多少个数产生一次溢出
- 计数模式:向上计数(从0到ARR)、向下计数(从ARR到0)或中央对齐
举个例子,如果我们使用72MHz的内部时钟,预分频设为71(实际分频系数72),那么计数频率就是1MHz,每个计数周期1微秒。如果ARR设置为999,那么每1000个计数(即1毫秒)就会产生一次更新事件。
2. 定时器工作模式详解
2.1 时钟源选择
通用定时器有四种时钟源可选,这个选择直接影响定时器的准确性和应用场景。
**内部时钟(CK_INT)**是最常用的选择,来自APB总线。这里有个容易混淆的点:当APB1预分频系数为1时,定时器时钟等于APB1时钟;否则等于APB1时钟的2倍。比如APB1时钟为36MHz时,定时器实际时钟是72MHz。
外部时钟模式1允许通过定时器输入引脚(TIx)提供时钟信号。我在做旋转编码器项目时就用过这个模式,每个编码器脉冲都触发计数器变化,非常适合位置测量。
外部时钟模式2使用专用外部触发输入(ETR)引脚,适合同步多个定时器。内部触发输入(ITRx)则让一个定时器作为另一个定时器的预分频器,可以实现更长的定时周期。
选择时钟源时需要考虑信号稳定性。内部时钟最稳定但精度有限;外部时钟精度高但需要稳定的信号源。我在电机控制项目中就吃过亏,外部信号有毛刺导致计数不准,后来加了滤波才解决。
2.2 计数模式对比
STM32支持三种基本计数模式,每种都有其适用场景。
向上计数模式是最直观的,计数器从0开始增加到ARR值,然后归零重新开始。这种模式简单明了,适合大多数定时应用。比如用定时器实现LED闪烁,设置ARR为49999(72MHz分频后1MHz计数,50ms中断),就能产生精确的时间基准。
向下计数模式从ARR值开始递减到0,然后重新装载ARR值。我在做音频处理时用过这种模式,配合DMA实现缓冲区填充,递减到0时触发DMA请求,正好完成一个缓冲区的数据传输。
中央对齐模式(又称向上/向下计数)先向上计数到ARR-1,产生一个计数器溢出事件,然后向下计数到1再次产生溢出。这种模式产生的PWM信号谐波特性更好,特别适合电机控制。我记得第一次用中心对齐模式时,发现PWM频率好像变了一半,后来才明白是因为计数周期增加了一倍。
实际选择时,向上计数最简单,向下计数适合特定外设配合,中心对齐则用于对波形质量要求高的场合。大多数情况下,向上计数模式就足够了。
3. PWM生成实战应用
3.1 PWM基础原理
PWM(脉冲宽度调制)是定时器最常用的功能之一。简单说,就是通过调节脉冲的占空比来等效模拟信号电平。比如占空比50%表示一半功率,100%表示全功率。
STM32的通用定时器支持两种PWM模式:模式1和模式2。它们的区别在于输出电平极性。模式1下,计数器小于比较值(CCR)时输出有效电平;模式2则相反。选择哪种模式取决于你的驱动电路设计。
PWM频率由ARR值决定:频率 = 定时器时钟 / (ARR + 1)。比如72MHz时钟,ARR设为7199,PWM频率就是10kHz。占空比由CCR值决定:占空比 = CCR / (ARR + 1)。设置CCR为3600,就得到50%占空比。
我在控制直流电机时,PWM频率选择很重要。太低会听到电机啸叫,太高则驱动芯片发热严重。经过测试,10-20kHz是比较理想的区间。LED调光则对频率不敏感,但低于100Hz会看到闪烁。
3.2 CubeMX配置步骤
使用STM32CubeMX配置PWM非常简单,大大减少了底层寄存器配置的工作量。
首先在Pinout界面中,将对应引脚设置为TIMx_CHy功能。比如要用TIM3的通道1,就找到PA6或PB4(根据具体型号),选择"TIM3_CH1"。
然后在Configuration标签中点击Timers,选择要配置的定时器。在Parameter Settings选项卡中:
- 设置Prescaler为71(72分频,得到1MHz计数)
- Counter Mode选择Up(向上计数)
- Period设为999(1000个计数,PWM频率1kHz)
- Internal Clock Division保持DIV1
接着切换到Channel1选项卡:
- Mode选择PWM Generation CHx
- Pulse设为初始占空比,比如500(50%)
- Fast Mode禁用(除非需要快速响应)
- PWM极性根据电路设计选择High或Low
生成代码后,在main函数中启动PWM:
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
运行时动态调整占空比:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 750); // 改为75%占空比
我建议在调试时先用示波器观察波形,确认频率和占空比是否正确。曾经有一次硬件设计是低电平有效,但我配置成高电平有效,导致设备一上电就全功率运行,差点烧坏电机。
4. 输入捕获测量技巧
4.1 输入捕获原理
输入捕获功能用于测量外部信号的脉宽或频率。当检测到引脚上的边沿跳变时,定时器会立即捕获当前的计数值,并产生中断。通过计算两次捕获的差值,就能得到信号的时间参数。
STM32的输入捕获支持多种配置:上升沿捕获、下降沿捕获或双边沿捕获。测量脉宽时通常需要同时使用两种边沿:上升沿记录开始时间,下降沿记录结束时间。
输入滤波是个实用功能,可以滤除信号中的毛刺。设置适当的滤波参数能提高测量稳定性,但过大的滤波值会延迟响应时间。我通常从较小的值开始测试,逐步增加直到噪声被有效抑制。
输入分频允许每N个事件才触发一次捕获,适合测量高频信号。比如测量1MHz信号时,设置4分频后只需处理250kHz的捕获中断,大大减轻CPU负担。
4.2 频率测量实现
测量频率有两种常用方法:直接测量法和周期测量法。
直接测量法在固定时间内计数脉冲个数,适合高频信号。比如设置1秒定时,计数这期间的脉冲数就是频率。但低频信号测量精度较差,1Hz信号可能有±1Hz误差。
周期测量法测量一个完整周期的时间,更适合低频信号。通过捕获连续两个上升沿的时间差,计算倒数得到频率。我在做转速测量时就用这种方法,即使转速很低也能准确测量。
下面是使用HAL库的实现代码:
// 在CubeMX中配置输入捕获
TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1);
// 启动输入捕获
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
// 在中断回调函数中处理
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
static uint32_t previous_value = 0;
uint32_t current_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if (previous_value != 0) {
uint32_t period = current_value - previous_value;
float frequency = 1000000.0f / period; // 1MHz计数
}
previous_value = current_value;
}
实测时遇到过一个坑:16位计数器溢出问题。测量低频信号时,可能两个上升沿间隔超过65535个计数。解决方法是用32位变量记录溢出次数,或者使用定时器的溢出中断。
5. 高级应用与优化
5.1 定时器同步技术
多个定时器协同工作能实现复杂功能。STM32支持多种同步方式:主从模式、触发同步和编码器接口。
主从模式让一个定时器作为主设备,控制其他从定时器。我在做三相电机控制时,就用TIM1作为主定时器,同步TIM8和TIM2,确保三相PWM完全同步,避免电机抖动。
触发同步使用内部连接线(ITRx)实现定时器级联。比如用TIM2触发TIM3启动,精确控制时序。这种硬件级同步比软件控制精确得多,jitter几乎为零。
编码器接口直接连接正交编码器,自动识别转向和计数。我在机器人项目中用它来读取电机位置,无需额外编程处理正交解码,大大简化了代码。
配置主从模式时,需要设置主定时器的触发输出(TRGO)和从定时器的触发源(TS)。例如,设置TIM2为主模式,更新事件时触发输出;TIM3为从模式,ITR1为触发源(连接到TIM2)。
5.2 性能优化建议
定时器配置不当可能导致性能问题。以下是一些优化经验:
中断优化:避免在中断服务程序中处理复杂任务。我通常只设置标志位,在主循环中处理数据。如果需要高频处理,使用DMA传输捕获数据。
时钟配置:选择适当的时钟源和分频比。过高时钟增加功耗,过低影响精度。72MHz时钟下,1MHz计数频率是个折中选择,兼顾精度和功耗。
影子寄存器:使能ARR预装载缓冲,确保参数更新在下一个周期生效,避免当前周期波形畸变。特别是在调整PWM参数时,这个功能很重要。
DMA应用:使用DMA自动传输PWM数据或捕获数据,减轻CPU负担。比如用DMA循环模式实现PWM序列输出,CPU只需初始化一次。
代码优化示例:
// 使用DMA自动更新PWM duty
uint16_t pwm_data[] = {100, 200, 300, 400};
HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, pwm_data, 4);
调试时建议先用简单测试验证基本功能。比如PWM输出,先固定占空比看波形;输入捕获先用方波信号测试。逐步复杂化,便于定位问题。
记得第一次做电机控制时,PWM频率设置太高导致MOS管发热严重。后来降到15kHz,问题就解决了。所以理论计算后一定要实际测试,根据结果调整参数。

3万+

被折叠的 条评论
为什么被折叠?



