STM32定时器与PWM实战:从原理到RoboMaster电机精准控制
2026/7/29 9:33:27 网站建设 项目流程

1. 项目概述:从定时器到精准控制

玩RoboMaster的朋友都知道,想让机器人动起来、打得准,光有主控芯片和电机驱动是远远不够的。你得让芯片能“掐着表”干活,精确地控制每一个动作的时机和力度。这就是STM32的Timer(定时器)和PWM(脉冲宽度调制)出场的时候了。在进阶篇里,我们把这两个看似基础,实则决定上层建筑稳定性的核心模块掰开揉碎了讲。

很多新手在调车时遇到过电机抖动、云台响应迟缓或者发射机构卡弹的问题,追根溯源,十有八九是定时器配置没吃透,或者PWM输出不稳。这篇文章,我就结合自己从校赛打到国赛,在RoboMaster赛场上踩过的各种坑,来聊聊STM32的Timer和PWM到底该怎么玩。我会从最根本的定时器工作原理讲起,一步步带你配置出稳定可靠的PWM信号,去驱动电机、舵机,甚至实现编码器测速和输入捕获测频率。目标很简单:让你写的代码,能精准地控制你的战车。

2. 定时器(Timer)核心原理与工作模式解析

STM32的定时器是个功能强大的外设,远不止“计时”那么简单。你可以把它理解为一个自带时钟、多种齿轮(分频器)、多种模式(向上、向下、中央对齐)的精密计时仪器,还能对外部信号进行计数、比较和捕获。

2.1 定时器的基本构成与时钟源

任何一个STM32的定时器,都离不开几个核心部分:时钟源、预分频器(PSC)、自动重装载寄存器(ARR)和计数器(CNT)。

时钟源是定时器的心脏跳动节奏。对于通用定时器(TIM2-TIM5等),时钟通常来自APB总线。这里有个关键点:当APB的预分频系数为1时,定时器时钟等于APB时钟;否则,定时器时钟是APB时钟的2倍。例如,在常见的72MHz系统时钟下,如果APB1预分频系数是2(36MHz),那么挂载在APB1上的TIM2的时钟实际是72MHz。这个细节直接影响你计算定时时间的准确性,很多人在此栽跟头。

预分频器PSC是一个16位的寄存器,用来对时钟源进行分频。假设时钟源频率是CK_PSC,经过PSC分频后,驱动计数器CNT的实际时钟频率CK_CNT = CK_PSC / (PSC + 1)。PSC的值决定了计数的“快慢”。

自动重装载寄存器ARR决定了计数器的周期。计数器CNT从0开始,每个CK_CNT周期加1或减1,当CNT的值达到ARR时,就会产生一个更新事件(溢出),并且CNT复位重新开始计数。这样,一个定时周期T = (ARR + 1) * (PSC + 1) / CK_PSC。通过调整PSC和ARR,我们可以得到从微秒到数秒不等的精确定时。

注意:在计算定时周期时,务必注意ARRPSC都是16位寄存器,最大值65535。如果需要更长的定时,需要合理分配两者的值,或者使用定时器的溢出中断进行软件累计。

2.2 关键工作模式:向上、向下与中央对齐

定时器主要有三种计数模式,选择哪种模式对PWM生成和电机控制影响很大。

向上计数模式:这是最直观的模式。计数器CNT从0开始递增,计到ARR值后产生更新事件,并清零重新开始。这种模式生成的PWM波形,其占空比从0%到100%是线性增长的,非常适合控制普通直流电机或舵机。

向下计数模式:与向上相反,CNT从ARR值开始递减,减到0后产生更新事件,并重新加载ARR值。这种模式在某些特定的电机控制算法(如某些类型的步进电机细分驱动)中有其用武之地。

中央对齐模式(又称向上/向下计数):这是RoboMaster中电机控制(特别是直流无刷电机的FOC控制)的常用模式。计数器从0开始递增到ARR,然后立即从ARR递减到0,如此循环。这种模式有两个关键优势:第一,它在一个周期内会产生两次更新事件(递增到顶和递减到底),方便更高频率的控制循环;第二,它生成的PWM波形是关于中心对称的,能有效减少谐波分量,对于电机驱动来说,可以降低噪音和转矩脉动。在配置云台舵机或底盘电机驱动时,如果追求极致的平稳性和响应速度,中央对齐模式是首选。

3. PWM生成机制与配置实战

PWM本质上是一种通过调节脉冲“占空比”(高电平时间占整个周期的比例)来模拟不同电压或控制信号的技术。在STM32中,PWM功能是定时器比较输出通道的一部分。

3.1 PWM输出原理与通道配置

定时器的每个通道都关联着一个捕获/比较寄存器(CCRx)和一个输出比较模块。当计数器CNT的值与CCRx的值相等时,输出比较模块会根据配置的模式,改变对应输出引脚的电平。

生成PWM的关键在于设置“PWM模式1”“PWM模式2”。两者的区别在于有效电平的定义。

  • PWM模式1:在向上计数时,当CNT < CCRx,通道输出有效电平(可设置为高或低);当CNT ≥ CCRx,输出无效电平。在向下计数时逻辑相反。
  • PWM模式2:与模式1的逻辑完全相反。

对于RoboMaster常见的电机驱动(如TB6612、DRV系列)和舵机(标准舵机),我们通常使用PWM模式1,并且将极性设置为有效电平为高。这样,CCRx的值就直接决定了高电平的宽度。

配置步骤与代码示例(以TIM3的通道1为例,驱动舵机): 舵机通常要求PWM频率为50Hz(周期20ms),控制脉宽在0.5ms到2.5ms之间对应0到180度。假设系统时钟72MHz,使用TIM3(挂载在APB1,时钟为72MHz)。

  1. 计算ARR和PSC:目标是20ms周期。

    • 先确定计数器分辨率。我们希望CCRx有足够精度(比如0.1°对应一个步进),0.5ms到2.5ms是2ms的变化范围,如果希望有2000个步进,则计数器每步对应1us。
    • 驱动计数器的时钟CK_CNT应为 1MHz(1us计数一次)。
    • 因此,PSC = CK_PSC / CK_CNT - 1 = 72MHz / 1MHz - 1 = 71
    • 周期20ms对应20000个计数步,所以ARR = 20000 - 1 = 19999
    • 此时,0.5ms脉宽对应CCR=500,2.5ms对应CCR=2500。
  2. GPIO与定时器初始化

    // 1. 开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置GPIOA.6为复用推挽输出(TIM3_CH1) GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 初始化定时器时基 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 19999; // ARR TIM_TimeBaseStructure.TIM_Prescaler = 71; // PSC TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 舵机用向上计数即可 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 4. 初始化PWM输出通道 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 有效电平为高 TIM_OCInitStructure.TIM_Pulse = 1500; // 初始占空比,对应1.5ms中位 TIM_OC1Init(TIM3, &TIM_OCInitStructure); // 初始化通道1 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能预装载 // 5. 使能定时器的预装载寄存器(ARR)和计数器 TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE);

3.2 多通道PWM与同步输出

在RoboMaster机器人上,我们经常需要同时控制多个部件,比如底盘的两个或四个轮子电机、云台的两个舵机(Yaw和Pitch)。这就需要配置同一个定时器的多个通道,或者多个定时器。

使用同一个定时器的多通道:这是最理想的情况,因为所有通道共享同一个时基(ARR和PSC),它们的PWM频率绝对同步,相位关系固定。例如,用TIM1的通道1、2、3、4同时驱动四个底盘电机,可以确保四个轮子的控制信号在时间上完全对齐,避免因微小的时间差导致车辆跑偏。配置方法与单通道类似,依次初始化TIM_OCxInit(x=1,2,3,4)即可。

使用不同定时器:如果IO口限制或定时器通道不够用,就需要用到多个定时器。这时要特别注意它们的时钟源是否一致。如果两个定时器来自不同的APB总线(如TIM2在APB1,TIM1在APB2),且APB预分频系数不同,即使设置相同的PSC和ARR,产生的频率也可能有细微差别。虽然对于电机控制来说这点差别影响不大,但对于需要严格同步的应用(如全向轮底盘的特殊运动算法),最好通过主从定时器模式进行同步。

实操心得:在画PCB或设计硬件时,尽量将需要同步控制的PWM信号规划到同一个定时器的不同通道上。这能从根本上避免软件同步的复杂性和潜在误差。例如,云台的两个舵机控制信号,强烈建议使用同一个定时器的两个通道。

4. 高级应用:输入捕获与编码器接口

定时器不仅能输出,还能输入。这在RoboMaster中主要用于两类场景:一是测量舵机反馈电位器的信号或测速模块的脉冲频率(输入捕获);二是直接读取电机编码器的正交信号(编码器接口模式)。

4.1 输入捕获测量频率与占空比

输入捕获功能可以测量外部脉冲信号的周期和占空比。例如,你可以用它来测量裁判系统测速模块输出的频率(换算成射速),或者测量一个未知PWM信号的参数。

其原理是:当捕获通道的输入引脚检测到指定的边沿(上升沿或下降沿)时,定时器当前的计数值CNT会被瞬间锁存到对应的捕获/比较寄存器CCRx中,并可以产生中断。通过记录连续两次上升沿捕获到的CNT值之差,再乘以计数周期,就能算出信号的周期。

测量频率和占空比的思路

  1. 配置通道为上升沿和下降沿都捕获。
  2. 第一次上升沿捕获,记录CNT值t1
  3. 随后到来的下降沿捕获,记录CNT值t2t2 - t1即为高电平时间。
  4. 下一个上升沿捕获,记录CNT值t3t3 - t1即为整个信号周期。
  5. 频率f = 1 / ((t3 - t1) * T_cnt),占空比duty = (t2 - t1) / (t3 - t1)

这里的关键是处理计数器溢出。如果测量低频信号,t3 - t1的值可能超过ARR,导致计数器在中间溢出了一次或多次。必须在中断服务函数中,用一个软件变量(如capture_overflow_count)来记录定时器更新中断(溢出中断)的次数,并在计算时考虑进去。

4.2 编码器接口模式读取电机转速

这是RoboMaster底盘电机控制的核心。直流无刷电机通常自带一个增量式正交编码器,输出A、B两路相位差90度的方波。STM32的定时器编码器接口模式,可以硬件解码这两路信号,直接读出代表电机位置和方向的计数值。

配置编码器模式的关键点

  1. 模式选择:通常配置为TIM_EncoderMode_TI12,即在TI1和TI2两个边沿都计数。这样可以得到4倍频的分辨率。例如,如果编码器线数是13线(电机一转,编码器产生13个脉冲),经过4倍频后,计数器一转的计数值变化为13 * 4 = 52个。
  2. 极性设置:根据编码器A、B相与定时器通道的硬件连接,可能需要设置TIM_ICPolarity_RisingFalling来确保计数方向与电机实际转向一致。
  3. 滤波器:电机运行环境可能有毛刺,可以适当配置输入滤波器的参数(TIM_ICFilter),但不宜过大,以免滤掉正常脉冲。

读取速度:速度计算通常采用M法测速(即固定时间间隔读取位置差)。在定时中断(比如1ms中断)里,读取编码器计数器CNT的当前值cnt_now,与上一次的值cnt_last相减,得到固定时间Δt内的脉冲数Δcnt

  • 速度(转/秒) =(Δcnt / (4 * 编码器线数)) / Δt
  • 方向:可以通过检查计数器的计数方向(向上还是向下),或者根据Δcnt的正负来判断。

踩坑记录:编码器计数器的溢出。通用定时器是16位的,最大值65535。电机高速正反转时,CNT值很容易溢出。处理不当会导致位置积分错误。务必使用有符号的32位整型变量来累加位置。在中断中:

int16_t cnt_diff = (int16_t)(cnt_now - cnt_last); // 注意强制转换为有符号16位,硬件上CNT是16位无符号数 total_position += cnt_diff; // total_position 是 int32_t 类型 cnt_last = cnt_now;

这样即使CNT从65535跳回0,cnt_diff也能正确计算出-65535,保证total_position连续。

5. 定时器中断与高级定时器应用

5.1 定时器中断的精准调度

除了生成PWM,定时器中断是实现周期性任务调度的利器。比如,你需要一个1kHz的控制循环来运行底盘电机的PID算法。

配置更新中断

  1. 在时基初始化后,配置更新中断。
    TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 根据任务紧急程度设置优先级 NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
  2. 在中断服务函数中,清除标志位并执行任务。
    void TIMx_IRQHandler(void) { if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIMx, TIM_IT_Update); // 执行你的1kHz控制任务,例如: Motor_PID_Calculate(); } }

高级技巧——使用多个定时器实现多速率控制:一个复杂的机器人系统可能有不同周期的控制任务。例如,云台姿态解算需要500Hz,底盘控制需要1kHz,而一些状态监控只需要100Hz。你可以分配不同的定时器(如TIM6, TIM7这种基本定时器)来产生不同频率的中断,实现分层、多速率控制,提高系统效率。

5.2 高级定时器(TIM1, TIM8)的特殊功能

在RoboMaster中,驱动大功率直流无刷电机(通常使用三相逆变桥)时,高级定时器是必不可少的。相比通用定时器,它们多了几个关键功能:

  1. 互补输出与死区插入:高级定时器每个通道可以输出一对互补的PWM信号(CHx和CHxN),用于驱动桥式电路的上臂和下臂。为了防止上下臂直通短路,必须在互补信号之间插入一个“死区时间”,这段时间内上下臂都关闭。高级定时器硬件支持可编程的死区插入,你只需要配置TIM_BDTRInitStructure.TIM_DeadTime参数,硬件会自动在互补信号中加入死区,这是软件无法精确实现的。

  2. 刹车功能:当紧急情况(如堵转、过流)发生时,需要立即关闭所有PWM输出,使电机刹车。高级定时器的刹车输入引脚(BKIN)可以响应外部故障信号,硬件级地快速关闭输出,响应速度远快于软件中断。

  3. 更灵活的PWM模式:支持更复杂的波形生成,如带插入的PWM模式,适用于更高级的电机控制算法。

配置带死区的互补PWM输出(以TIM1通道1为例)

// ... 时基初始化与通用定时器类似 ... // 输出通道初始化,注意模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; // 使能互补输出 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_Pulse = ccr_value; TIM_OC1Init(TIM1, &TIM_OCInitStructure); // 关键:配置刹车和死区寄存器 TIM_BDTRInitTypeDef TIM_BDTRInitStructure; TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime = 0x5A; // 死区时间,具体值需根据驱动芯片和开关频率计算 TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

死区时间的计算需要根据你使用的MOSFET或驱动芯片的开启/关断延迟来确定,通常会在几十到几百纳秒之间。设置过小有直通风险,设置过大会降低输出电压利用率。

6. 常见问题排查与调试技巧

即使理解了原理,实际调试中依然会遇到各种问题。下面是一些典型问题的排查思路。

6.1 无PWM波形输出

这是最常见的问题。请按以下顺序排查:

  1. 时钟与引脚检查

    • 确认定时器和对应GPIO的时钟是否已使能(RCC_APBxPeriphClockCmd)。
    • 确认GPIO是否被正确初始化为复用推挽输出(GPIO_Mode_AF_PP)。特别注意:部分引脚(如PA15, PB3, PB4)默认是JTAG/SWD功能,需要先禁用JTAG才能用作普通IO或外设功能。
    // 禁用JTAG, 启用SWD(常用调试配置) GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
  2. 定时器使能顺序

    • 确保在配置完所有参数(时基、输出比较、预装载)后,最后才使能定时器(TIM_Cmd)。
    • 对于高级定时器,使能主输出(TIM_CtrlPWMOutputs(TIM1, ENABLE))是必须的。
  3. 重装载与预装载

    • 检查TIM_ARRPreloadConfigTIM_OCxPreloadConfig是否已使能。如果不使能预装载,修改ARR或CCR的值会立即生效,可能导致当前周期波形异常。使能后,修改的值会在下一个更新事件时才生效,波形更平滑。
  4. 使用调试器查看寄存器:在Keil或IAR的调试模式下,直接查看TIMx的寄存器。

    • 查看CR1寄存器的CEN位是否为1(定时器使能)。
    • 查看CCER寄存器对应通道的CCxE位是否为1(输出使能)。
    • 查看CCRx寄存器的值是否在0和ARR之间。

6.2 PWM波形频率或占空比不对

  1. 计算错误:反复核对系统时钟CK_PSC、预分频器PSC、自动重载值ARR的计算公式。特别注意APB总线预分频系数对定时器时钟的影响。
  2. ARR或CCR值为0:ARR不能为0,否则计数器不工作。CCR为0则占空比0%,CCR大于等于ARR则占空比100%或0%(取决于极性),可能看不到脉冲。
  3. 中央对齐模式的特殊性:在中央对齐模式下,PWM频率f = CK_PSC / ( (ARR+1) * (PSC+1) )。因为计数器要上下跑一遍才是一个完整周期,所以实际周期是向上计数模式的两倍,频率减半。这是很多人配置中央对齐模式后频率对不上的主要原因。

6.3 编码器计数方向反了或读数跳动

  1. 方向反了:交换编码器A、B相的信号线,或者通过软件交换定时器通道的输入极性(修改TIM_ICInitStructure.TIM_ICPolarity)。
  2. 读数跳动或丢脉冲
    • 硬件问题:检查编码器接线是否松动,电源是否稳定。编码器信号线最好使用双绞线或屏蔽线,并远离电机电源线,防止干扰。
    • 软件滤波不足:适当增加输入捕获滤波器的值(TIM_ICFilter),但要以不滤掉正常脉冲为限。
    • 中断优先级与处理时间:确保编码器定时器的中断优先级足够高,并且中断服务函数执行时间尽可能短。如果被其他长时间中断打断,可能会丢失脉冲。可以考虑使用DMA来传输编码器计数器的值,彻底解放CPU。

6.4 高级定时器互补输出异常

  1. 无互补信号:检查TIM_OCInitStructure.TIM_OutputNState是否使能,以及TIM_BDTRConfig是否成功执行。
  2. 死区时间效果不明显或导致波形畸变:用示波器同时测量CHx和CHxN的波形,观察死区是否插入。如果死区时间设置过大,会导致有效PWM占空比损失,电机最大电压上不去。需要根据驱动芯片手册精确计算。
  3. 刹车功能误触发:检查刹车输入引脚(BKIN)的电平。如果配置为高电平刹车,则该引脚被拉高时会关闭输出。确保该引脚在正常工作时处于正确的电平状态。

调试PWM和编码器,一个示波器是必不可少的。它能直观地告诉你波形频率、占空比、死区、信号质量(是否有振铃或过冲)等信息。对于编码器,可以用逻辑分析仪同时抓取A、B相信号和定时器计数器的值,观察其对应关系,这是排查复杂问题的终极手段。

定时器和PWM是连接STM32代码与RoboMaster机器人物理世界的桥梁。理解它们的每一个细节,意味着你能更精准地掌控你的机器人。从让轮子平稳转动,到云台快速精准指向,再到发射机构稳定连发,都离不开底层这些稳定可靠的定时脉冲。多动手配置,多观察波形,多思考背后的硬件逻辑,这些经验会让你在调试机器人时更加得心应手。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询