3. PWM输出模式详解:模式1与模式2的区别、通道引脚映射与极性配置

各位同学,咱们接着往下聊。上一节我们把PWM的基本原理和定时器配置框架搭起来了,这一节我打算深入进去,把PWM输出模式里最容易让人迷糊的几个点彻底讲透。

说白了,PWM输出就是定时器在背后不停地比较、翻转、更新。但STM32给了你两种比较模式——模式1和模式2,很多人一开始搞不清它俩到底差在哪。再加上引脚映射和极性配置,新手很容易在这里栽跟头。我当年第一次调舵机,就是因为极性搞反了,舵机死活不转,还以为是代码写错了……嗯,咱们今天就把这些坑一个个填平。

3.1 PWM模式1与模式2:核心区别

先看一张对比表,把两个模式的核心差异摆出来:

对比项 PWM模式1 PWM模式2
计数方向 向上计数 向上计数
CNT < CCR时 输出有效电平 输出无效电平
CNT ≥ CCR时 输出无效电平 输出有效电平
有效电平位置 占空比起始段 占空比结束段
典型应用 舵机控制、LED调光 互补输出、死区补偿

你仔细看这个表,其实模式1和模式2的差别就一句话:有效电平出现在计数值小于CCR的那一段,还是大于等于CCR的那一段。

我个人习惯用模式1做舵机控制。为什么?因为模式1下,CCR值越大,有效电平占的时间越长,占空比越大。这个逻辑很直观——你给CCR写500,占空比就是50%;写900,占空比就是90%。调起来不用动脑子。

模式2刚好反过来。CCR越大,有效电平反而越短。我刚开始用的时候就被这个反逻辑坑过一次——想加大占空比,结果把CCR往大了写,舵机反而往回缩。你想想看,这要是没发现,排查起来得多头疼。

核心记忆法:模式1是“小值有效”,模式2是“大值有效”。但注意,这个“有效”指的是有效电平,不是占空比大小。

3.2 PWM输出通道与引脚映射关系

讲完模式,咱们看看通道和引脚。STM32的定时器通常有4个通道(CH1~CH4),每个通道对应一个GPIO引脚。但问题是——同一个引脚可能被多个外设复用,你得选对才行。

以我常用的STM32F103C8T6为例,TIM2的通道映射是这样的:

定时器 通道 默认引脚 重映射引脚
TIM2 CH1 PA0 PA15
TIM2 CH2 PA1 PB3
TIM2 CH3 PA2 PB10
TIM2 CH4 PA3 PB11

这里有个关键点:重映射不是你想用就能用的。你得在GPIO初始化之前,调用GPIO_PinRemapConfig()函数,把引脚功能切过去。我在项目中遇到过一块板子,PA0被别的外设占用了,只能把PWM挪到PA15。结果忘了开重映射时钟,折腾了半小时才找到原因。

我的习惯:画原理图之前,先把定时器通道和引脚对应表列出来,避免后期打架。如果板子已经做好了,优先用默认引脚,实在不行再考虑重映射。

3.3 PWM输出极性配置

极性配置,说白了就是决定有效电平是高电平还是低电平。在STM32的库函数里,通过TIM_OCInitTypeDef结构体的OCPolarity成员来设置:

// 配置PWM输出极性
TIM_OCInitTypeDef TIM_OCInitStructure;

// 有效电平为高电平(常用)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

// 有效电平为低电平
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;

你可能会问:极性有什么用?我直接默认高电平不行吗?

行,但有时候不行。举个例子:你用一个NPN三极管驱动LED,基极高电平导通,那PWM极性就得设成高有效。但如果你用的是PNP管,基极低电平导通,极性就得反过来。我刚开始做电机驱动板的时候,没注意这个细节,结果电机一上电就全速转——因为极性反了,PWM输出一直处于无效电平,但无效电平对电机来说反而是导通状态。

避坑指南:我曾经在调试一个舵机项目时,舵机始终只能转到0°和180°两个位置,中间角度全跳过去了。查了半天,发现是极性设反了——有效电平变成了无效电平,导致CCR的比较逻辑完全颠倒。所以,配置极性之前,先确认你的负载电路是“高有效”还是“低有效”。

3.4 知识结构总览

为了让你更直观地理解这三部分的关系,我画了一张结构图:

PWM输出模式知识结构 PWM输出配置 模式1 vs 模式2 通道与引脚映射 输出极性配置 CNT < CCR有效 CNT ≥ CCR无效 CNT < CCR无效 CNT ≥ CCR有效 默认引脚 vs 重映射引脚 AFIO时钟与Remap函数 TIM_OCPolarity_High TIM_OCPolarity_Low 核心要点:模式决定比较逻辑,映射决定物理引脚,极性决定有效电平 三者独立配置,但必须协同工作才能输出正确的PWM波形

从这张图你可以看到,PWM输出配置其实就三个维度:模式、引脚、极性。它们各自独立,但最终输出的波形是三者共同作用的结果。我建议你在写代码之前,先在纸上把这三个参数定下来,再动手。

3.5 实战配置示例

最后,给你一个完整的PWM输出配置代码片段,把今天讲的知识点串起来:

// 以TIM2_CH1为例,PA0输出PWM,模式1,高有效
void PWM_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    // 1. 使能时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 2. 配置GPIO为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 3. 配置定时器时基
    TIM_TimeBaseStructure.TIM_Period = 999;        // 自动重装值
    TIM_TimeBaseStructure.TIM_Prescaler = 71;      // 预分频
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    // 4. 配置PWM通道
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;      // 模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 500;                    // 初始占空比50%
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高有效
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    
    // 5. 使能定时器
    TIM_Cmd(TIM2, ENABLE);
}

这段代码里,模式1、高有效、PA0引脚,三个配置一目了然。你以后写PWM代码,就按这个模板来,把通道号和引脚改一改就行。

一个小技巧:调试的时候,先用示波器看波形,确认频率和占空比对不对,再连负载。我吃过亏——直接连舵机,结果波形不对,舵机嗡嗡响,差点烧了。