PWM寄存器详解:CR1、CR2、ARR与CCR
各位同学,今天我们来啃PWM最核心的四个寄存器。说实话,我刚接触STM32的定时器时,看到那几十个寄存器也头大。但后来我发现,做PWM输出,你真正需要搞明白的,其实就这四个:CR1、CR2、ARR和CCR。
为什么是这四个?我打个比方你就懂了。ARR决定了PWM的周期,就像你定了个闹钟每1秒响一次;CCR决定了高电平占多少时间,好比闹钟响0.5秒就停;而CR1和CR2,就是控制这个闹钟怎么启动、怎么对齐、怎么计数的开关。你想想看,没有这四个寄存器,PWM根本转不起来。
3.1 控制寄存器CR1:PWM的启动与计数模式
CR1,全称Control Register 1。它控制着定时器最基本的行为——启动、停止、计数方向。我习惯把它叫做“总闸寄存器”。
| 位段 | 名称 | 功能描述 |
|---|---|---|
| 位0 | CEN | 计数器使能。写1启动,写0停止。这是PWM输出的总开关。 |
| 位4 | DIR | 计数方向。0向上计数,1向下计数。PWM输出一般用向上计数。 |
| 位5:6 | CMS | 中央对齐模式选择。00边沿对齐,01/10/11中央对齐。 |
| 位7 | ARPE | 自动重装载预装载使能。建议置1,防止ARR更新时产生毛刺。 |
这里我要特别强调一下ARPE位。我在项目中遇到过一个问题:PWM输出时,频率突然跳变了一下,导致电机抖了一抖。查了半天,发现就是ARPE没置1。ARR的值在定时器运行时被直接写入了影子寄存器,导致计数周期瞬间改变。所以我的建议是——ARPE永远置1,配合软件更新事件来更新ARR,这样波形才干净。
关键配置示例: 初始化PWM时,CR1的典型值通常是0x01(CEN=1,ARPE=1,其他默认)。如果你用中央对齐模式,CMS位要设为01或10。
3.2 控制寄存器CR2:输出极性、刹车与主从模式
CR2管的是PWM输出的“后处理”。说白了,就是信号从定时器出来之前,要不要反相、要不要刹车、要不要同步给别的定时器。
| 位段 | 名称 | 功能描述 |
|---|---|---|
| 位0 | CCPC | 捕获/比较预装载控制。建议置1,保证CCR更新同步。 |
| 位4:6 | MMS | 主模式选择。可输出更新事件、比较脉冲等给其他定时器。 |
| 位8 | TI1S | 输入选择,PWM输出时一般保持默认0。 |
嗯,这里有个坑。CR2的CCPC位,很多人会忽略。我曾经调试一个多通道PWM输出,发现通道1和通道2的波形更新不同步,一个变了另一个还保持旧值。原因就是CCPC没置1,CCR的预装载功能没打开。记住:做多通道PWM时,CCPC必须置1。
个人经验: 如果你需要多个定时器同步输出PWM,可以用CR2的MMS位把主定时器的更新事件映射到TRGO引脚,从定时器通过从模式接收这个信号。这样所有PWM的起始相位就完全一致了。
3.3 自动重装载寄存器ARR:决定PWM频率
ARR,Auto-Reload Register。它决定了定时器计数的上限。PWM的频率计算公式很简单:
PWM频率 = 定时器时钟频率 / (PSC + 1) / (ARR + 1)
举个例子:定时器时钟是72MHz,预分频器PSC设为71(即72分频),ARR设为999,那么PWM频率就是:
72,000,000 / (71 + 1) / (999 + 1) = 72,000,000 / 72 / 1000 = 1000 Hz
也就是1kHz的PWM。你想想看,要改变频率,要么调PSC,要么调ARR。但PSC一般初始化后就固定了,所以运行时调频率,其实就是改ARR。
注意: ARR的值不能小于CCR的值。如果ARR比CCR小,PWM输出会一直保持高电平或低电平,失去调制效果。我在调试舵机时犯过这个错,舵机直接转到极限位置卡住了。
3.4 捕获/比较寄存器CCR:决定PWM占空比
CCR,Capture/Compare Register。它存的是一个比较值。定时器计数器CNT从0开始向上计数,每计一个数就和CCR比较一次:
- 当CNT < CCR时,输出高电平(或低电平,取决于极性设置)
- 当CNT >= CCR时,输出翻转
占空比的计算公式:
占空比 = CCR / (ARR + 1) × 100%
比如ARR=999,CCR=500,占空比就是50%。CCR=750,占空比就是75%。很简单对吧?
但这里有个细节——CCR也有预装载功能。如果你在运行时直接写CCR,而没有使能预装载,那么新值会立即生效,可能导致当前周期出现不完整的脉冲。我建议的做法是:使能CCR预装载(通过CR1的ARPE和CR2的CCPC配合),然后在更新事件发生时再让新值生效。
核心要点: ARR控制频率,CCR控制占空比。两者配合,就能输出任意频率和占空比的PWM波。实际项目中,我通常把ARR固定,只调整CCR来实现调速或调光。
3.5 寄存器关系全景图
下面这张图,我把四个寄存器的关系画了出来。你看一遍就能明白PWM的完整数据流。
从这张图你能看到:时钟经过PSC分频后驱动CNT计数,CNT与ARR比较决定是否重装载,CNT与CCR比较决定输出电平。而CR1和CR2,就像两个“总管”,控制着整个流程的启停和模式。
3.6 寄存器配置实战要点
最后,我总结几个实际配置时的要点,都是我在项目里踩过的坑:
- 初始化顺序: 先配置CR1和CR2的模式位,再设置ARR和CCR的值,最后才置位CEN启动定时器。顺序反了可能导致第一个周期输出异常。
- 更新事件: 修改ARR或CCR后,需要产生一个更新事件(软件置位UG位)才能让新值生效。我习惯在修改后加一条TIM_GenerateEvent(TIMx, TIM_EventSource_Update)。
- 极性设置: CR2的CCxP位控制输出极性。如果发现PWM波形反了,检查一下是不是极性位配置反了。我遇到过好几次,示波器一看,高电平变低电平,就是极性位搞反了。
- 刹车功能: 如果用到电机控制,CR2的刹车位(BKE)一定要配置。万一出故障,刹车信号能立即强制PWM输出到安全状态。
我的习惯: 每次配置完寄存器,我都会用调试器读回寄存器值确认一遍。特别是CR1的CEN位和ARPE位,经常有人忘了置1。读回来确认一下,比事后查bug省时间多了。