2. PWM生成原理:定时器与比较器的工作原理、PWM信号的硬件生成方式、软件模拟PWM的局限性

各位好,我是老周。今天咱们聊聊PWM到底是怎么从芯片里“变”出来的。很多初学者把PWM想得很玄乎,其实说白了,它就是定时器和比较器配合演的一出双簧。我当年刚入行时,也以为PWM是软件算出来的,后来被 mentor 一句话点醒:“你去看数据手册的定时器章节。”嗯,从那以后我才真正理解了硬件生成PWM的门道。

2.1 定时器:PWM的“心跳”

定时器是PWM生成的核心。你可以把它想象成一个不停从0数到某个最大值、然后又跳回0重新数的计数器。这个“数数”的过程,就是PWM的时基。

常见的定时器有两种工作模式:

  • 向上计数模式:从0开始,一直加到自动重装载值(ARR),然后归零重新开始。
  • 中央对齐模式:先向上加到ARR,再向下减到0,如此反复。这种模式生成的PWM对称性更好,谐波含量更低。

我个人习惯在电机控制中优先使用中央对齐模式。为什么?因为它的PWM脉冲在周期中间对齐,电流纹波更小,电机运行更平稳。当然,向上计数模式也有它的用武之地,比如在一些简单的开关电源控制中。

关键参数:

  • ARR(Auto-Reload Register):决定PWM周期。ARR越大,周期越长,频率越低。
  • PSC(Prescaler):预分频器,用来降低定时器的输入时钟频率。说白了就是让定时器“数慢一点”。

PWM频率计算公式:Fpwm = Fclk / ( (ARR+1) * (PSC+1) )

2.2 比较器:PWM的“雕刻刀”

定时器负责“数数”,比较器则负责“判决”。比较器内部有一个比较寄存器(CCR),它时刻拿CCR的值和定时器的当前计数值做比较。

比较结果决定了输出电平:

  • 当计数值 < CCR 时,输出高电平(或低电平,取决于极性设置)。
  • 当计数值 >= CCR 时,输出电平翻转。

你想想看,这不就是占空比的来源吗?CCR的值决定了高电平持续的时间。CCR越大,高电平时间越长,占空比越大。

避坑指南:我曾经在一个项目中,发现PWM输出波形偶尔会“跳变”。排查了半天,最后发现是CCR值在更新时没有和定时器溢出事件同步。硬件上有个“预装载”机制,一定要使能。否则,你在计数值运行中直接写CCR,它可能立即生效,导致一个不完整的脉冲。记住:更新CCR要在定时器溢出中断里做,或者使能自动预装载功能。

2.3 PWM信号的硬件生成方式

现代微控制器(比如STM32、TI的C2000系列)都内置了专用的PWM硬件模块。硬件生成PWM,说白了就是让定时器和比较器自动工作,CPU只需要在需要改变占空比时更新一下CCR值就行。

硬件PWM的典型流程:

  1. 配置定时器的时钟源、预分频器、计数模式。
  2. 设置ARR值,确定PWM频率。
  3. 设置CCR值,确定初始占空比。
  4. 配置输出通道的极性、空闲状态。
  5. 启动定时器,硬件自动输出PWM波形。

下面是一个典型的STM32硬件PWM配置代码片段:

// 以STM32 TIM1 通道1为例
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

// 1. 时基配置
TIM_TimeBaseStructure.TIM_Prescaler = 71;      // 72MHz / (71+1) = 1MHz
TIM_TimeBaseStructure.TIM_Period = 999;        // 1MHz / (999+1) = 1kHz PWM
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

// 2. 输出比较配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500;           // 初始占空比 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);

// 3. 使能预装载
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

// 4. 启动定时器
TIM_Cmd(TIM1, ENABLE);

硬件生成PWM的优势很明显:

  • 零CPU开销:一旦配置好,PWM波形自动输出,CPU可以干别的事。
  • 高精度:定时器时钟通常能达到几十甚至几百MHz,占空比分辨率可以做到很高。
  • 低抖动:硬件逻辑的响应是确定性的,不会受中断延迟影响。

2.4 软件模拟PWM的局限性

有些场合,比如成本敏感的8位单片机,可能没有硬件PWM模块。这时候就得用软件来模拟PWM。说白了,就是用定时器中断,在中断服务程序里手动翻转IO口。

软件模拟PWM的典型做法:

  1. 设置一个定时器,产生固定频率的中断。
  2. 在中断里维护一个计数器。
  3. 当计数器小于某个阈值时,输出高电平;否则输出低电平。
  4. 计数器溢出时归零,并重置输出。

听起来很简单,对吧?但实际用起来,坑不少。

软件PWM的三大局限:

局限 说明 实际影响
CPU占用率高 每个PWM周期都需要多次中断处理 电机控制中,CPU被PWM中断占满,无法执行其他控制算法
分辨率受限 受限于中断响应速度和定时器时钟 通常只能做到8位分辨率,电机低速时抖动明显
抖动大 中断响应时间不确定,受其他中断影响 PWM脉冲宽度不稳定,电机产生额外噪声和振动

我曾经在一个项目中,被迫用8位单片机软件模拟3路PWM来控制一个直流电机。结果呢?电机在低速时“咔咔”响,高速时效率也上不去。后来换了带硬件PWM的芯片,问题迎刃而解。所以我的建议是:只要预算允许,尽量用硬件PWM。软件PWM只适合对控制精度要求不高的场合,比如LED调光、风扇调速等。

2.5 硬件PWM vs 软件PWM:一张图看懂

下面这张图展示了硬件PWM和软件PWM在系统架构上的本质区别。你可以看到,硬件PWM的定时器和比较器是独立工作的,而软件PWM则完全依赖CPU的中断服务。

硬件PWM vs 软件PWM 架构对比 硬件PWM 定时器(自动计数) 比较器(自动比较) PWM输出(自动翻转) CPU:仅更新CCR值 CPU占用率:< 1% 软件PWM 定时器中断(频繁触发) CPU中断服务:手动翻转IO PWM输出(依赖CPU响应) CPU:被中断占满 CPU占用率:30%~80% 硬件PWM:定时器和比较器独立工作,CPU只负责“下指令”。软件PWM:CPU亲自“搬砖”,效率低、抖动大。

2.6 小结

这一章我们聊了PWM生成的硬件基础。定时器提供时间基准,比较器雕刻出占空比,两者配合就能输出精准的PWM波形。硬件PWM是电机控制的首选,它把CPU解放出来去跑更重要的控制算法。软件PWM虽然在某些低成本场景下能用,但它的局限性——高CPU占用、低分辨率、大抖动——在电机控制中往往是致命的。