PWM的全称是脉冲宽度调制(Pulse Width Modulation),是一种控制模拟信号的方法。它通过改变脉冲的宽度来控制模拟信号的平均值。
PWM的工作原理是将一个周期性的脉冲信号与一个控制信号进行比较。当控制信号大于脉冲信号时,输出高电平;当控制信号小于脉冲信号时,输出低电平。通过改变脉冲信号的宽度,可以控制输出信号的平均值。
输出信号的平均值连在一起,可以达到模拟信号的效果,如下图所示:
PWM波形在单位时间内重复出现的次数。
PWM波形中高电平信号所占的比例。
STM32F1除了基本定时器TIM6和TIM7,其它定时器都可以产生PWM输出。其中:
在STM32微控制器中,生成PWM信号通常涉及到自动重装载寄存器(ARR)和比较寄存器(CCR)两个重要的寄存器。
PWM输出模式一共8种,常用的是PWM1和PWM2,其用法差不多,区别如下:
下表是PWM1和PWM2的区别:
在该模式下,定时器的计数器从0开始递增,
这种模式下,PWM信号的周期由ARR决定,占空比由CCR决定。
与PWM模式1相比,PWM模式2输出有效性正好是相反的。
下表是PWM1和PWM2的比较:
| 模式 | CNT 计算方式 | CNT| CNT>CCR | |
|---|---|---|---|
| PWM1 | 递增 | 通道CH有效 | 通道CH无效 |
| PWM1 | 递减 | 通道CH无效 | 通道CH有效 |
| PWM2 | 递增 | 通道CH无效 | 通道CH有效 |
| PWM2 | 递减 | 通道CH有效 | 通道CH无效 |

0CXREF表示定时器的比较器

以上图为例,设ARR=8,当CCRx=4时,
PWM 的配置在库文件 time.c 中。
下面是使能设置代码:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 后面示例的 PWM 需要配置引脚的复用功能(重映像),定时器的重映像可在《STM32F10x参考手册》查询,摘录如下:



以使用 TIM3 的通道1为例,它默认是在PA6引脚上,它完全重映像是在PC6,后面使用的开发板上原理图示:
示例代码将使用PC6输出TIM3的通道1 PWM波。
代码示例:
// 设置 TIM3 完全重映像 GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); 在输出PWM信号时,通常需要考虑信号的稳定性、噪声抑制以及输出电流的能力等因素。复用推挽输出是一种常见的配置方式。
// 复用推挽输出 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; 包括 : 自动重载值、分频系数、计数方式等。
void TIM_TimeBaseInit(TIM_TypeDef*TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStructure) 包括 :PWM 模式、输出极性、使能等。
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStructure); // 结构体定义 typedef struct { uint16_t TIM_OCMode; // 比较输出模式 uint16_t TIM_OutputState; // 比较输出使能 uint16_t TIME_OutputNState: // 比较互补输出使能 uint32_t TIM_Pulse; // 脉冲宽度 0~65535 /** * 输出极性 * * TIM_OCPolarity_High: 高电平有效 * * TIM_OCPolarity_Low: 低电平有效 */ uint16_t TIM_OCPolarity; /** * 互补比较输出极性 * * TIM_OCNPolarity_High: 高电平有效 * * TIM_OCNPolarity_Low: 低电平有效 */ uint16_t TIM_OCNPolarity; /** * 空闲状态下比较输出状态 * * TIM_OCIdleState_Set: 置位 * * TIM_OCIdleState_Reset: 复位 */ uint16_t TIM_OCIdleState; /** * 空闲状态下比较输出状态 * * TIM_OCNIdleState_Set: 置位 * * TIM_OCNIdleState_Reset: 复位 */ uint16_t TIM_OCNIdleState; } TIM_OCInitTypeDef; // NewState: 新的状态,可以是 ENABLE 或 DISABLE。 void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState) void TIM_SetCompare1(TIM_TypeDef* TIMx, uint32_t Compare1); // 参数 TIM_OCPreload 可为 TIM_OCPreload_Enable、TIM_OCPreload_Disable void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); // NewState: 新的状态,可以是 ENABLE 或 DISABLE。 void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); 对于高级定时器,需要设置MOE位。
MOE 位,全称 Master Output Enable,是定时器控制寄存器 1 (TIMx->CR1) 中的一个控制位(15位),用于使能或禁用定时器主输出。
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState); 本实验对TIM3控制,使用通道1, 对TIM3_CH1重映像到PC6引脚,控制PC6上接的LED亮度。
示例程序控制LED呼吸灯效果,渐渐变亮,再渐渐变暗。
#ifndef __PWM_UTILS_H__ #define __PWM_UTILS_H__ #include "stm32f10x.h" void tim3_ch1_pwm_init(u16 preriod, u16 prescaler); void tim3_ch1_pwm_set_duty(u16 duty); #endif #include "pwm_utils.h" #include "led_utils.h" /** * @brief 定时器3初始化 */ void tim3_ch1_pwm_init(u16 preriod, u16 prescaler){ // 使能TIM3时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能LED所在端口的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 使能AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO初始化结构体 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置输出速度为50MHz GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置为推挽输出模式 GPIO_Init(LED_PORT, &GPIO_InitStructure); //初始化 LED_PORT // 管脚重映像 GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); // 定时器初始化 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = preriod; //设置自动重装载寄存器周期值 TIM_TimeBaseStructure.TIM_Prescaler = prescaler; //设置时钟预分频数 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分频因子 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 初始化 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // PWM模式1 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC1 // 使能TIM3的CCR1寄存器预装载 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能TIM3的ARR寄存器预装载 TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能TIM3 TIM_Cmd(TIM3, ENABLE); } void tim3_ch1_pwm_set_duty(u16 duty){ // 设置定时器3的PWM占空比 TIM_SetCompare1(TIM3, duty); } #include "gpio_utils.h" #include "stm32f10x.h" #include "sys_tick_utils.h" #include "led_utils.h" #include "pwm_utils.h" // 主函数 int main(void) { // led 初始化 custom_led_init(); // tick 初始化 sys_tick_init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // PWM 初始化,2K tim3_ch1_pwm_init(500, 72-1); led_all_off(); int i = 0; u8 direction=0; while (1) //无限循环 { tim3_ch1_pwm_set_duty(i); if(direction==0){ i++; }else{ i--; } if(i>300){ direction = 1; }else if(i<1){ direction = 0; } delay_ms(10); } } 实测PC6的波形是一直变化中:

本文代码开源地址:
https://gitee.com/xundh/stm32_arm_learn