1. 从一个调光台灯说起PWM模式到底在解决什么问题很多刚接触STM32定时器的朋友第一次看到PWM模式1和PWM模式2这两个选项时心里都会犯嘀咕不就是输出一个方波吗为什么还要分两种模式我当初也是这么想的直到在一个调光台灯项目上栽了跟头才真正搞明白这两个模式的区别。先说说PWM本身。PWM全称是脉冲宽度调制说白了就是通过调节方波的高电平占空比来等效地控制输出功率。比如你要驱动一个LED灯如果直接给3.3V灯就是全亮如果给0V灯就是灭的。但如果你想让它半亮呢这时候PWM就派上用场了——让引脚快速地在高电平和低电平之间切换高电平占30%的时间低电平占70%的时间由于人眼的视觉暂留效应你看到的就是一个亮度约为30%的灯光。STM32的通用定时器和高级定时器都支持PWM输出功能而且每个定时器可以同时输出多路PWM。以STM32F103系列为例TIM1和TIM8是高级定时器每个可以输出4路互补PWMTIM2到TIM5是通用定时器每个也能输出4路PWM。这个数量对于大多数应用来说已经绰绰有余了。那PWM模式1和模式2到底差在哪里简单来说它们决定了“什么时候输出有效电平”这个逻辑。在PWM模式1下当计数器值小于比较值时通道输出有效电平在PWM模式2下当计数器值小于比较值时通道输出无效电平。你看就这一个字的差别实际效果却完全相反。我见过不少初学者在这里翻车明明代码里设置了50%的占空比结果用示波器一测发现波形完全反了。这种情况十有八九就是把模式1和模式2搞混了或者对“有效电平”的极性理解有偏差。所以这篇文章我就想把这个看似简单、实则容易踩坑的知识点彻底讲透从原理到配置从计算到实测把PWM模式1和模式2的区别以及各自适合的应用场景掰开揉碎说清楚。无论你是正在做毕业设计的学生还是在做电机控制、电源管理的工程师只要用到STM32的PWM输出这篇文章都能帮你少走弯路。我会用最直白的方式解释原理给出可以直接复现的代码配置还会分享几个我在实际项目中踩过的坑和总结的经验。2. 定时器PWM输出的底层逻辑计数器、比较值与有效电平2.1 定时器是怎么“数数”的要理解PWM模式得先搞清楚STM32定时器的基本工作流程。你可以把定时器想象成一个不停数数的计数器它从0开始每个时钟周期加1一直加到我们设定的自动重装载值ARR然后重新从0开始。这个循环往复的过程就形成了PWM的周期。假设系统时钟是72MHz我们给定时器预分频器PSC设置为71那么定时器的实际计数频率就是72MHz除以(711)等于1MHz也就是每1微秒计一个数。如果ARR设置为999那么计数器就从0数到999一共1000个数耗时1000微秒也就是1毫秒。这个1毫秒就是PWM的周期对应的PWM频率就是1kHz。在这个计数过程中还有一个关键角色捕获比较寄存器CCR。我们给CCR设定一个值比如500。定时器在数数的同时会不断把当前计数值CNT和CCR的值进行比较。当CNT小于CCR时通道输出一种电平当CNT大于等于CCR时通道输出另一种电平。这样就在引脚上产生了一个方波。占空比的计算就很直观了如果CCR等于500ARR等于999那么高电平的时间就是500个计数周期总周期是1000个计数周期占空比就是50%。改变CCR的值就能改变占空比改变ARR的值就能改变PWM频率。2.2 有效电平、极性寄存器与两种模式的真正含义这里要引入一个容易被忽略但非常关键的概念有效电平。在STM32的定时器里每个输出通道都有一个极性控制位CCxP用来设定什么电平算“有效”。你可以把有效电平理解为“我们想要的那个电平”。比如驱动一个共阳极LED低电平点亮那么低电平就是有效电平驱动一个共阴极LED高电平点亮那么高电平就是有效电平。PWM模式1和模式2的定义就是围绕“有效电平”来描述的PWM模式1当CNT CCR时通道输出有效电平当CNT CCR时通道输出无效电平。PWM模式2当CNT CCR时通道输出无效电平当CNT CCR时通道输出有效电平。注意这里说的是“有效电平”和“无效电平”而不是直接说“高电平”和“低电平”。有效电平到底是高还是低取决于CCxP极性位的设置。如果CCxP设置为0高电平有效那么有效电平就是高电平如果CCxP设置为1低电平有效那么有效电平就是低电平。这就解释了为什么很多人会觉得PWM模式1和模式2“看起来差不多”——因为如果你同时改了模式和极性最终输出的波形可能是一样的。但它们的内部逻辑不同适用的场景也不同。2.3 一个生活化的类比我用一个生活中的例子来类比。假设你在烧水水温从0度开始上升你设定了一个目标温度比如80度。在PWM模式1下只要水温低于80度你就打开加热器有效电平水温达到或超过80度你就关闭加热器无效电平。在PWM模式2下逻辑反过来水温低于80度时关闭加热器达到或超过80度时才打开加热器。显然对于烧水这个场景PWM模式1是合理的PWM模式2是反常识的。但在某些特殊场景下比如你需要一个“报警”信号——正常时输出低电平异常时输出高电平——PWM模式2配合适当的极性设置就能派上用场。2.4 中心对齐模式下的额外变量除了边沿对齐模式STM32定时器还支持中心对齐模式。在中心对齐模式下计数器先向上数到ARR再向下数回0形成对称的波形。这时候PWM模式1和模式2的行为会稍有不同因为计数方向在变化。在中心对齐模式下PWM模式1的输出逻辑是当CNT小于CCR时输出有效电平这个判断在向上计数和向下计数时都成立。但由于计数器先增后减实际输出的波形是关于中心对称的。这种模式在电机控制中特别有用因为对称的PWM波形可以减少谐波分量降低电机噪音。我实测过在中心对齐模式下如果ARR设置为8399PSC设置为0系统时钟72MHz那么PWM频率就是72MHz除以(83991)再除以2等于4.29kHz左右。这个频率对于大多数电机驱动来说是比较合适的。3. 模式1与模式2的配置差异从寄存器到代码3.1 寄存器层面的差异在STM32的标准外设库或HAL库中配置PWM模式最终都会落到几个关键寄存器上。以TIMx_CCMR1寄存器为例每个通道占用其中的OCxM位域用来设置输出比较模式。OCxM的值决定了是PWM模式1还是PWM模式2OCxM 110PWM模式1OCxM 111PWM模式2同时TIMx_CCER寄存器中的CCxP位决定极性CCxP 0高电平有效CCxP 1低电平有效还有一个容易被忽略的位TIMx_CCER中的CCxE位用来使能输出。如果这个位没置1不管你怎么配置模式和极性引脚都不会输出任何波形。我在早期调试时曾经因为忘记使能输出对着示波器折腾了半天最后才发现是CCxE没打开。3.2 HAL库配置实例下面是一段基于HAL库的PWM配置代码以TIM3的通道1为例输出1kHz、占空比50%的PWMTIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC {0}; // 定时器基础配置 htim3.Instance TIM3; htim3.Init.Prescaler 71; // 预分频72MHz/(711)1MHz htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; // ARR999周期1ms频率1kHz htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim3); // PWM通道配置 sConfigOC.OCMode TIM_OCMODE_PWM1; // 选择PWM模式1 sConfigOC.Pulse 500; // CCR500占空比50% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; // 高电平有效 sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); // 启动PWM输出 HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1);如果要改成PWM模式2只需要把TIM_OCMODE_PWM1改成TIM_OCMODE_PWM2。如果要改成低电平有效把TIM_OCPOLARITY_HIGH改成TIM_OCPOLARITY_LOW。3.3 四种组合的波形对比为了直观展示模式和极性的组合效果我整理了一个表格。假设ARR999CCR300那么模式极性CNTCCR时输出CNTCCR时输出实际高电平时间实际占空比PWM1高有效高电平低电平300个周期30%PWM1低有效低电平高电平700个周期70%PWM2高有效低电平高电平700个周期70%PWM2低有效高电平低电平300个周期30%从表中可以看出PWM1高有效和PWM2低有效的输出波形完全一样PWM1低有效和PWM2高有效的输出波形也完全一样。这就是为什么很多人觉得两种模式“可以互相替代”——在单纯追求波形的情况下确实如此。但它们的内部逻辑不同在某些高级应用场景下选择正确的模式可以简化代码逻辑避免额外的计算。3.4 为什么要有两种模式你可能会问既然效果可以互相替代为什么STM32要设计两种模式这其实是为了适应不同的应用需求。在某些电机控制算法中PWM模式2配合低有效极性可以实现更自然的控制逻辑。比如在BLDC电机的六步换相中某些扇区需要输出低电平有效的PWM这时候用PWM模式2就更符合控制逻辑的直觉。另外在互补输出和死区插入的场景下主通道和互补通道的模式选择会影响死区时间的实际效果。高级定时器的互补输出功能允许你同时输出一对反相的PWM信号用于驱动H桥的两个开关管。这时候如果模式选错了可能会导致上下桥臂直通烧毁功率管。这个坑我在做逆变器方案时亲眼见过代价是几个MOS管和一片驱动芯片。4. 实际应用场景什么时候用模式1什么时候用模式24.1 LED调光与呼吸灯这是最基础的应用场景。驱动一个共阴极LED高电平点亮我们希望占空比越大灯越亮。这时候用PWM模式1配合高电平有效是最自然的选择CCR值越大CNTCCR的时间越长高电平时间越长灯越亮。如果你用的是共阳极LED低电平点亮有两种方案一是保持PWM模式1把极性改成低有效二是保持高有效极性把模式改成PWM2。两种方案效果一样但我个人更倾向于第一种因为“模式1”这个名字本身就暗示了它是更常用的选择代码可读性更好。呼吸灯的实现就是在一个循环里不断改变CCR的值从0增加到ARR再从ARR减小到0。用PWM模式1的话CCR增加时亮度增加逻辑很直观。4.2 电机调速与H桥驱动直流电机的调速通常需要一个H桥驱动电路。H桥有四个开关管通过控制对角开关管的导通来实现电机的正反转。在PWM调速时我们通常只对其中一个开关管施加PWM信号另一个保持常通。假设我们用高电平导通的N沟道MOS管那么PWM模式1配合高有效就是合理的选择占空比越大电机转速越快。但如果你的驱动电路用的是低电平导通的P沟道MOS管就需要用低有效极性或者PWM模式2。在BLDC电机的控制中情况会更复杂一些。六步换相需要根据转子位置依次导通不同的相每个扇区中有一相需要PWM调制另外两相分别接高电平和低电平。这时候PWM模式的选择会影响换相逻辑的简洁性。我个人的经验是用PWM模式1配合动态改变极性可以比较优雅地处理正反转和不同扇区的需求。4.3 逆变器与SPWM生成在逆变器方案中我们需要生成正弦脉宽调制SPWM信号。基本思路是用一个正弦波作为调制波与一个三角载波进行比较根据比较结果输出PWM。STM32的高级定时器支持中心对齐模式可以方便地生成对称的三角载波。在SPWM生成中PWM模式1和模式2的选择会影响输出波形的相位。如果模式选反了输出正弦波的相位会偏移180度。对于单相逆变器来说这可能只是导致输出极性相反但对于三相逆变器来说相位错误会导致相序混乱电机无法正常运转。我建议在做SPWM时先用PWM模式1配合高有效极性生成波形后用示波器观察确认相位正确后再接入功率级。如果发现相位反了优先检查模式和极性设置而不是急着改算法。4.4 舵机控制舵机控制是一个对PWM参数要求比较严格的应用。标准舵机需要20ms周期的PWM信号高电平宽度在0.5ms到2.5ms之间对应0度到180度的角度。用STM32配置时通常设置PSC71ARR19999这样计数频率是1MHz周期是20ms。CCR值在500到2500之间变化对应0.5ms到2.5ms的高电平时间。舵机内部的控制电路通常期望高电平有效的信号所以PWM模式1配合高有效极性是标准做法。如果你发现舵机不动或者抖动先检查PWM频率和脉宽是否正确再检查模式和极性。4.5 互补输出与死区时间高级定时器TIM1、TIM8支持互补输出可以同时输出一对反相PWM用于驱动H桥的上下桥臂。在这种场景下主通道和互补通道的模式选择需要特别注意。假设主通道配置为PWM模式1、高有效那么当CNTCCR时主通道输出高电平互补通道输出低电平。当CNTCCR时主通道输出低电平互补通道输出高电平。这样上下桥臂就不会同时导通。但如果主通道配置为PWM模式2、高有效逻辑就反过来了CNTCCR时主通道输出低电平互补通道输出高电平。这时候如果你没有相应调整死区时间可能会导致上下桥臂在切换瞬间同时导通形成直通短路。我在做逆变器方案时曾经因为主通道和互补通道的模式配置不一致导致死区时间失效烧了两个MOS管。后来总结的经验是主通道和互补通道的模式必须匹配要么都用PWM模式1要么都用PWM模式2不能混用。5. 实操过程从零配置一个可调占空比的PWM输出5.1 硬件准备与开发环境我用的硬件是一块STM32F103C8T6最小系统板这是最经典的入门芯片72MHz主频定时器资源丰富。开发环境用的是Keil MDK5配合STM32CubeMX生成初始化代码。如果你用的是标准外设库配置逻辑类似只是函数名不同。首先在CubeMX里选择TIM3配置通道1为PWM Generation CH1。在参数设置里Prescaler填71Counter Period填999这样PWM频率是1kHz。Pulse填500初始占空比50%。Mode选择PWM mode 1CH Polarity选择High。生成代码后在main函数里调用HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1)启动PWM。然后用一个循环不断改变CCR值就能看到LED亮度变化。5.2 动态改变占空比的三种方法在实际项目中我们经常需要在运行时改变占空比。HAL库提供了几种方法第一种是直接修改寄存器TIM3-CCR1 newValue;。这种方法最快但可移植性差。第二种是用HAL库函数__HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, newValue);。这是推荐的做法代码清晰可读性好。第三种是重新配置通道HAL_TIM_PWM_ConfigChannel()。这种方法比较重一般只在初始化时用。我实测下来第二种方法最方便。但要注意如果定时器的自动重装载预装载功能ARPE没有使能修改CCR的值会立即生效可能导致波形出现毛刺。建议在初始化时使能ARPE这样CCR的修改会在下一个周期开始时生效波形更干净。5.3 参数计算从需求反推PSC和ARR假设你需要一个频率为5kHz、占空比可调范围0%到100%的PWM信号系统时钟72MHz。计算过程如下首先确定计数频率。PWM频率 计数频率 / (ARR1)。如果我们希望ARR有足够的调节精度比如1000级那么ARR设为999计数频率 5kHz × 1000 5MHz。预分频值PSC 72MHz / 5MHz - 1 13.4取整为13。实际计数频率 72MHz / 14 5.143MHz实际PWM频率 5.143MHz / 1000 5.143kHz误差在可接受范围内。如果对频率精度要求更高可以调整ARR的值。比如ARR设为899计数频率 5kHz × 900 4.5MHzPSC 72MHz / 4.5MHz - 1 15。实际计数频率 72MHz / 16 4.5MHz实际PWM频率 4.5MHz / 900 5kHz正好。这个计算过程看起来简单但在实际项目中我见过不少人因为PSC和ARR算错导致PWM频率偏差很大。特别是当系统时钟不是72MHz时比如用内部RC振荡器或者改了时钟树一定要重新计算。5.4 用示波器验证波形配置完成后用示波器探头接到对应的引脚上观察波形。重点看几个参数频率是否与设计值一致占空比是否准确上升沿和下降沿是否干净。如果波形频率不对检查PSC和ARR的计算。如果占空比不对检查CCR的值和模式/极性设置。如果波形有毛刺检查是否有其他外设干扰或者GPIO速度设置是否过高。我习惯在调试PWM时先把占空比设为50%这样高低电平时间相等用示波器的自动测量功能很容易看出频率和占空比是否正确。确认无误后再改到其他占空比。5.5 一个完整的呼吸灯代码下面是一个完整的呼吸灯实现用TIM3通道1驱动LED占空比从0到100%循环变化int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); uint16_t duty 0; int8_t direction 1; while (1) { __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, duty); if (direction 0) { duty 10; if (duty 1000) { duty 1000; direction -1; } } else { duty - 10; if (duty 0) { duty 0; direction 1; } } HAL_Delay(10); } }这段代码的逻辑很简单duty从0开始每次增加10到1000后开始减少循环往复。HAL_Delay(10)控制变化速度10ms一步的话一个完整的呼吸周期大约是2秒。6. 常见问题与排查技巧实录6.1 PWM没有输出引脚一直是低电平这是最常见的问题。排查思路如下首先检查GPIO是否配置为复用推挽输出模式。在STM32中定时器通道对应的引脚必须配置为AF_PP复用推挽不能配置为普通的输出模式。我见过有人把引脚配置成Out_PP结果PWM死活出不来。其次检查定时器时钟是否使能。在HAL库中HAL_TIM_PWM_Init()会自动使能定时器时钟但如果你用的是标准库需要手动调用RCC_APB1PeriphClockCmd()。然后检查HAL_TIM_PWM_Start()是否被调用。这个函数会设置CCxE位使能输出如果没调用引脚不会有任何波形。最后检查CCR的值是否大于ARR。如果CCR大于ARR在PWM模式1下CNT永远小于CCR输出一直是有效电平在PWM模式2下输出一直是无效电平。所以如果CCR设得太大可能看起来像没有输出。6.2 占空比与预期相反这个问题我在第一节就提到了原因通常是模式或极性设置反了。如果你想要的是“CCR越大高电平时间越长”那么应该用PWM模式1配合高有效极性。如果实际效果相反检查这两个设置。还有一种可能是CCR的值计算错了。比如你想要50%占空比ARR999那么CCR应该设为500。如果你设成了499占空比就是49.9%差别不大但如果你设成了999占空比就是99.9%看起来就像一直是高电平。6.3 改变CCR时波形出现毛刺这个问题通常是因为自动重装载预装载功能没有使能。在STM32中ARR和CCR都有预装载寄存器如果使能了预装载写入的新值会在下一个更新事件时才生效这样可以保证波形的完整性。如果没有使能写入新值会立即生效如果写入时刻正好在计数器接近CCR的位置就可能产生一个异常窄的脉冲。解决方法是在初始化时设置htim.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE;这样ARR和CCR的修改都会在周期边界生效。6.4 互补输出时上下桥臂直通这是一个危险的问题可能导致功率管烧毁。原因通常是死区时间设置不当或者主通道和互补通道的模式配置不一致。死区时间的计算假设系统时钟72MHz死区时间寄存器BDTR的DTG位设置为0x80那么死区时间大约是1微秒。具体计算公式参考参考手册不同系列的STM32可能略有不同。我的经验是死区时间至少要大于功率管的关断时间。比如IRF3205的关断时间大约是100纳秒那么死区时间设为500纳秒到1微秒比较安全。但也不能太大否则会影响输出波形质量。6.5 常见问题速查表现象可能原因排查方法引脚无输出GPIO未配为复用模式检查GPIO初始化代码引脚无输出定时器时钟未使能检查RCC配置引脚无输出PWM_Start未调用检查启动代码占空比相反模式或极性设置错误检查OCMode和OCPolarity波形有毛刺ARPE未使能使能AutoReloadPreload频率不对PSC或ARR计算错误重新计算并示波器验证互补输出直通死区时间不足或模式不匹配增大死区统一模式配置中心对齐波形不对称计数模式配置错误检查CounterMode设置6.6 几个我踩过的坑第一个坑在F4系列上定时器的时钟源和F1不同。F1的TIM1在APB2上TIM2-4在APB1上F4的定时器时钟可能来自APB1或APB2的倍频。如果不注意PSC的计算会完全错误。我建议在配置前先查一下参考手册的时钟树章节。第二个坑高级定时器的BDTR寄存器有一个MOE位主输出使能如果不置1即使配置了PWM模式引脚也不会有输出。这个位在HAL库中由HAL_TIM_PWM_Start()自动设置但如果你用寄存器直接操作千万别忘了。第三个坑在调试时用ST-Link连接目标板有时候会影响PWM输出。因为调试器可能会占用某些引脚或者暂停CPU导致定时器停止。如果发现PWM时有时无先断开调试器试试。第四个坑PWM频率太高时GPIO的驱动能力可能不足导致波形上升沿变缓。STM32的GPIO最高可以输出几十MHz的方波但如果你驱动的是大容性负载可能需要加缓冲器。我在驱动一个长线缆时遇到过这个问题后来加了一个74HC245缓冲器就解决了。7. 进阶话题中心对齐模式与PWM模式1/2的配合7.1 中心对齐模式的工作原理中心对齐模式和边沿对齐模式的区别在于计数器的计数方式。边沿对齐模式下计数器从0数到ARR然后立即回到0重新开始。中心对齐模式下计数器从0数到ARR然后从ARR数回0形成一个来回的过程。这种计数方式产生的PWM波形是对称的每个周期内高电平脉冲位于周期的中央。对于电机控制来说对称PWM可以减少谐波降低电磁噪音。对于逆变器来说对称PWM可以更方便地生成SPWM。在中心对齐模式下PWM模式1和模式2的行为需要重新理解。因为计数器有向上和向下两个方向CNTCCR这个条件在向上计数和向下计数时都会出现。在PWM模式1下向上计数时CNTCCR输出有效电平向下计数时CNTCCR也输出有效电平所以有效电平脉冲是关于ARR对称的。7.2 中心对齐模式下的频率计算中心对齐模式的PWM频率计算公式是PWM频率 系统时钟 / (PSC1) / (ARR1) / 2。注意最后多了一个除以2因为计数器要来回数两次才完成一个周期。假设系统时钟72MHzPSC0ARR8399那么PWM频率 72MHz / 1 / 8400 / 2 4.286kHz。这个频率在电机驱动中比较常用。如果你想要一个20kHz的超声波PWM用于无感BLDC的过零检测可以这样算72MHz / (PSC1) / (ARR1) / 2 20kHz。取PSC0则(ARR1) 72MHz / 20kHz / 2 1800ARR 1799。7.3 中心对齐模式下的占空比计算在中心对齐模式下占空比的计算和边沿对齐模式类似但要注意CCR的值对应的是有效电平的宽度。在PWM模式1下有效电平的宽度是2倍的CCR值因为向上和向下各有一段CNTCCR的时间。所以占空比 2 × CCR / (2 × (ARR1)) CCR / (ARR1)。这个公式和边沿对齐模式一样所以占空比的计算不需要额外调整。但实际波形中有效电平脉冲是居中的这对于某些应用来说很重要。7.4 什么时候该用中心对齐模式中心对齐模式适合以下场景电机控制对称PWM减少谐波和噪音逆变器方便生成SPWM需要精确控制脉冲位置的场景边沿对齐模式适合以下场景LED调光对波形对称性要求不高舵机控制标准舵机只关心脉宽不关心位置简单的DAC滤波边沿对齐模式实现更简单我个人的经验是如果应用对波形质量要求不高优先用边沿对齐模式因为配置简单频率计算直观。如果做电机控制或逆变器再考虑中心对齐模式。8. 从PWM模式延伸到定时器的其他用法8.1 输入捕获测频率定时器的输入捕获功能和PWM输出是相辅相成的。在很多项目中我们需要同时输出PWM和测量外部信号的频率。STM32的同一个定时器可以同时配置多个通道有的做输出有的做输入捕获。输入捕获测频率的基本原理是当引脚上出现上升沿或下降沿时定时器自动把当前CNT的值锁存到CCR中并触发中断。在中断里读取CCR的值两次捕获的差值就是信号的周期取倒数就是频率。我实测过用72MHz的定时器PSC71计数频率1MHz测量1kHz到100kHz的信号频率精度可以做到0.1%以内。如果被测信号频率更高可以减小PSC的值。8.2 定时器中断与PWM的配合在很多应用中我们需要在PWM的特定时刻执行一些操作比如在PWM周期结束时更新CCR的值或者在特定占空比时触发ADC采样。STM32的定时器支持多种中断源更新中断UEV、捕获比较中断CCxIF、触发中断TIF等。更新中断在计数器溢出时触发可以用来做周期性的任务调度。捕获比较中断在CNT等于CCR时触发可以用来在PWM的特定相位执行操作。比如在电机控制中我们可以在PWM周期的中点触发ADC采样获取电流反馈。配置捕获比较中断的方法是在初始化时使能CCxIE位然后在中断服务函数里处理。注意中断服务函数要尽量简短避免影响PWM波形的实时性。8.3 定时器级联与同步STM32的定时器支持级联和同步功能。一个定时器可以作为另一个定时器的预分频器或者多个定时器可以同步启动生成多路相位一致的PWM。在需要多路同步PWM的应用中比如三相逆变器我们可以用TIM1作为主定时器TIM2和TIM3作为从定时器通过内部触发线实现同步。这样三路PWM的频率和相位都严格一致不会出现漂移。配置同步的方法是通过TIMx_SMCR寄存器的TS位选择触发源通过SMS位选择从模式。具体配置比较复杂建议参考参考手册的“定时器同步”章节。9. 我个人的实操心得与建议9.1 先仿真再上硬件在配置PWM之前我习惯先用STM32CubeMX的仿真功能或者在线计算器算好PSC和ARR的值。这样上硬件后一次成功的概率高很多。特别是对于不熟悉的芯片系列时钟树可能和F1不同提前算好可以避免很多返工。9.2 保留一个“黄金配置”我在做项目时会保留一个经过验证的PWM配置作为模板。这个模板里PSC、ARR、模式、极性都是调好的新项目直接复制过来改改引脚和频率就行。这样可以避免每次都从头调试节省大量时间。我的黄金配置是PSC71ARR999PWM模式1高有效极性使能ARPE。这个配置输出1kHz、占空比可调的PWM适用于大多数LED调光和低速电机驱动场景。9.3 用LED做快速验证在没有示波器的情况下可以用一个LED串联1kΩ电阻接到PWM输出引脚上。如果LED亮度可以随CCR值变化说明PWM基本正常。虽然看不到频率和占空比的精确值但至少能确认有输出。如果LED一直亮或者一直灭说明PWM有问题。一直亮可能是占空比接近100%一直灭可能是占空比接近0%或者没有输出。9.4 注意GPIO的复用功能重映射STM32的定时器通道引脚通常有多个可选位置通过AFIO的重映射功能切换。比如TIM3的通道1默认在PA6但可以重映射到PB4或PC6。如果你发现引脚没有输出检查一下重映射配置是否正确。在HAL库中重映射通过__HAL_AFIO_REMAP_TIM3_ENABLE()之类的宏来配置。不同系列的宏名称可能不同建议查一下对应的头文件。9.5 关于PWM模式选择的最终建议回到文章的主题PWM模式1和模式2到底怎么选我的建议是默认用PWM模式1配合高有效极性这是最符合直觉的组合如果驱动低电平有效的负载优先改极性而不是改模式只有在互补输出或特殊控制逻辑中才考虑用PWM模式2无论用哪种模式都要用示波器验证实际波形这个建议看起来简单但背后是我多次调试和烧管子的教训。PWM模式的选择没有绝对的对错关键是要理解它的逻辑并根据实际需求做出合理的选择。最后分享一个小技巧如果你不确定模式和极性该怎么配可以先把所有组合都试一遍用示波器观察波形找到符合预期的那一组。虽然听起来有点笨但在紧急调试时非常有效。我到现在还保留着这个习惯特别是在用不熟悉的芯片时。