1. PWM信号基础与定时器原理
脉冲宽度调制(PWM)是数字系统控制模拟设备的桥梁技术,其核心在于通过调节方波的占空比来等效模拟电压值。现代微控制器中,定时器模块是生成PWM的硬件基础。以STM32的通用定时器为例,其内部包含16位自动重装载寄存器(ARR)、比较寄存器(CCRx)和计数器(CNT),这三个核心寄存器协同工作形成PWM生成机制。
当计数器从0开始递增时,会持续与比较寄存器的值进行对比。在计数器值小于CCRx时,输出高电平;超过CCRx但未达到ARR值时保持低电平;当计数器溢出时重新开始循环。这种工作模式被称为PWM模式1,其数学关系可表示为:
占空比 = (CCRx + 1) / (ARR + 1) × 100%
频率 = 定时器时钟 / [(ARR + 1) × (PSC + 1)]
关键提示:ARR和CCRx寄存器都是0-based计数,实际周期值需要+1计算。例如ARR设为999时,实际脉冲周期包含1000个时钟周期。
2. 硬件定时器配置实战
以STM32F407的TIM3_CH2通道(PA7引脚)为例,完整配置流程包含时钟使能、GPIO初始化、定时器参数设置三大步骤。首先需要开启APB1总线上的TIM3时钟和GPIOA时钟:
c复制RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
接着配置PA7为复用功能模式,特别注意不同芯片的引脚复用映射可能不同:
c复制GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM3);
定时器基础配置需要计算预分频器(PSC)和自动重装载值(ARR)。假设系统时钟84MHz,目标生成1kHz PWM:
c复制TIM_TimeBaseInitTypeDef TIM_InitStruct;
TIM_InitStruct.TIM_Prescaler = 84 - 1; // 分频后1MHz
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_InitStruct.TIM_Period = 1000 - 1; // 1kHz频率
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &TIM_InitStruct);
最后配置PWM输出通道,设置初始占空比为50%:
c复制TIM_OCInitTypeDef OC_InitStruct;
OC_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
OC_InitStruct.TIM_OutputState = TIM_OutputState_Enable;
OC_InitStruct.TIM_Pulse = 500 - 1; // 50%占空比
OC_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &OC_InitStruct);
TIM_Cmd(TIM3, ENABLE);
3. 动态调节技巧与异常处理
实际应用中常需动态调整PWM参数。直接修改CCRx寄存器可实现占空比实时调节,但频率调整需要谨慎处理:
c复制// 安全更新占空比
TIM3->CCR2 = new_duty_cycle - 1;
// 频率修改需禁用定时器
TIM_Cmd(TIM3, DISABLE);
TIM3->ARR = new_period - 1;
TIM_Cmd(TIM3, ENABLE);
常见问题排查清单:
- 无信号输出:检查时钟使能、GPIO复用映射、定时器使能位
- 频率偏差:确认时钟树配置,检查PSC和ARR计算
- 占空比异常:验证CCRx值是否超过ARR,检查极性设置
- 信号抖动:降低GPIO速度等级,增加硬件滤波电容
经验之谈:使用示波器测量时,建议开启定时器的预装载功能(TIM_OCPreloadConfig使能),可避免寄存器写入时的信号毛刺。
4. 高级应用:互补PWM与死区控制
在电机控制等场景中,需要互补的PWM信号加死区时间。以高级定时器TIM1为例,死区时间计算公式为:
死区时间 = (DTR + 1) / 定时器时钟频率
其中DTR是死区寄存器值,其配置示例:
c复制TIM_BDTRInitTypeDef BDTR_InitStruct;
BDTR_InitStruct.TIM_OSSRState = TIM_OSSRState_Enable;
BDTR_InitStruct.TIM_OSSIState = TIM_OSSIState_Enable;
BDTR_InitStruct.TIM_LOCKLevel = TIM_LOCKLevel_1;
BDTR_InitStruct.TIM_DeadTime = 0x54; // 约1us死区
BDTR_InitStruct.TIM_Break = TIM_Break_Disable;
BDTR_InitStruct.TIM_BreakPolarity = TIM_BreakPolarity_Low;
BDTR_InitStruct.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &BDTR_InitStruct);
互补PWM需要配置主从输出模式,并注意Break功能的安全保护机制。实际调试时建议逐步增加死区时间,用示波器观察高低侧MOSFET的栅极信号,确保没有重叠导通风险。
5. 软件模拟PWM方案对比
当硬件定时器资源紧张时,可用GPIO配合软件定时器模拟PWM。以RT-Thread的软件PWM实现为例:
c复制struct rt_device_pwm *pwm_dev;
pwm_dev = (struct rt_device_pwm *)rt_device_find("pwm0");
rt_pwm_set(pwm_dev, 0, 1000000, 500000); // 1MHz, 50%占空比
软件PWM与硬件方案的关键差异:
- 精度:受系统调度影响,通常只能达到ms级
- 资源占用:需要持续CPU干预
- 适用场景:低频控制(如LED调光)、应急方案
- 稳定性:需注意中断嵌套导致的周期抖动
实测数据显示,在Cortex-M4内核上,软件PWM在10kHz以下时占空比误差<2%,但当频率超过50kHz后误差可能骤增至15%以上。因此关键控制系统建议优先使用硬件定时器。