PWM,即脉宽调制,也可称为占空比,在一个高低电平周期内,高电平所占总周期时长的比例,即为占空比。
在单片机当中,通常使用定时器来产生所需的PWM波形。这个东西用途还是很广的,举个例子,不知道大家知不知道呼吸灯是什么,LED灯由灭慢慢增加亮度直到最亮,再慢慢降低亮度到完全熄灭,反复这一过程,即为呼吸灯效果。这里LED灯亮度的变化一般使用的就是PWM波控制,一般来讲,如果让一个LED灯以一个很快的速度进行亮灭亮灭循环,我们看到的LED就一直是亮的状态,具体有多亮,那就要看输出到LED的高电平占一个高低电平总时间的比例,这个比例就是占空比,通过调整这一比例,就可以控制LED的亮度,进而达到呼吸灯的效果。
此外,PWM还有许多功能,例如PWM可以控制舵机的旋转角度,可以控制一些直流电机的旋转速度,以及通过PWM控制器,可以实现高效的电源转换等等功能。
然后我们大概介绍一下定时器是怎么输出PWM波形的,定时器大家应该有所了解,定时器的主要功能就是计数,我们给定时器输入一个时钟,这个时钟每来一次,定时器里的值就加一,直到达到我们提前设置好的数值时,触发中断。当然,这只是一个最简单的描述,具体配置起来,还需要设置一些预分频值,重装值等等内容。PWM输出是定时器的一个功能,输出比较,简单来说,将定时器设置为输出比较功能后,我们需要给输出比较寄存器设置一个固定的值,这个输出比较寄存器每个定时器有四个,我们需要几个设置几个,定时器经过时钟分配后的值最终会保存到一个叫CNT的计数器中,我们通过CNT中的值与设置好的输出比较寄存器中的值进行比对,当CNT的值大于输出比较寄存器的值时,我们输出高/低电平,反之,我们输出相反的电平。这样,我们就实现了PWM输出功能。上面所写的每一步,在STM32的库函数中都有相应的函数对应。
今天,基于此,我们简单做一个呼吸灯的案例。
先看一下开发板,这里用的芯片是STM32F103C8T6,使用的LED引脚为PB0,通过手册,我们可以查到,PB0是TIM3的输出比较通道3引脚,所以我们配置TIM3的PWM输出功能,输出比较寄存器设置第三个寄存器,最后通过在循环中,不断改变输出比较寄存器的值,来实现呼吸灯效果。
具体代码如下:
首先是PWM初始化的.c文件。
#include "stm32f10x.h"
void PWM_Init(void)
{
//第一步,开启定时器时钟,定时器挂接在APB1总线上,所以调用下述函数。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//第二步,选择定时器的时钟源,这里选择内部时钟
TIM_InternalClockConfig(TIM3);
//第三步,设置定时器的相关配置
TIM_TimeBaseInitTypeDef TIM_InitStructure;//初始化定时器时基结构体
TIM_InitStructure.TIM_ClockDivision=TIM_CKD_DIV2;//这里是设置分配系数,设置为2,即2分频,则定时器输入频率为36MHz
TIM_InitStructure.TIM_Period=100-1;//这里设置ARR的值,即CNT的极值
TIM_InitStructure.TIM_Prescaler=360-1;//这里设置预分频系数,通过该处分频,输入CNT的频率降低为0.1MHz
TIM_InitStructure.TIM_RepetitionCounter=0;//这个是重复计数设置,这里用不到,设为0即可
TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//这里将计数器设置为从开始递增,一直到预设值为止,也可设置为从预设值开始往下减
TIM_TimeBaseInit(TIM3,&TIM_InitStructure);//调用初始化定时器函数
//接下来初始化输出比较内容
TIM_OCInitTypeDef TIM_OCStructure;//先初始化输出比较结构体
TIM_OCStructInit(&TIM_OCStructure);//这个函数是给上面的结构体赋一个初值,后续需要改动就直接修改对应内容即可。
TIM_OCStructure.TIM_OCPolarity=TIM_OCPolarity_High;//极性设置,若设置为低,则输出的高低电平需要取反,这里设置为高
TIM_OCStructure.TIM_OutputState=TIM_OutputState_Enable;//使能
TIM_OCStructure.TIM_OCMode=TIM_OCMode_PWM1;//这个是设置输出比较模式的,PWM1模式即CNT的值小于对应输出比较寄存器的值时,输出高电平
TIM_OCStructure.TIM_Pulse=0;//设置CCR初值
TIM_OC3Init(TIM3,&TIM_OCStructure);//初始化输出比较单元,选择输出比较通道3
//使能TIM3
TIM_Cmd(TIM3,ENABLE);
//TIM3使能后,还要使能相应的引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//打开时钟,因为GPIO均挂接在APB2总线上,所以调用该函数
//配置GPIO相关内容
GPIO_InitTypeDef GPIO_InitSturetuct;//初始化结构体
GPIO_InitSturetuct.GPIO_Mode=GPIO_Mode_AF_PP;//设置为复用推挽输出模式,因为用的是定时器输出
GPIO_InitSturetuct.GPIO_Pin=GPIO_Pin_0;//选择引脚
GPIO_InitSturetuct.GPIO_Speed=GPIO_Speed_50MHz;//选择输出频率
GPIO_Init(GPIOB,&GPIO_InitSturetuct);//初始化
}
PWM的.h文件:
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
#endif
主函数:
#include "stm32f10x.h"
#include "Delay.h"
#include "PWM.h"
uint16_t num;
int main(void)
{
PWM_Init();
while (1)
{
for(num=0;num<100;num++)
{
TIM_SetCompare3(TIM3,num);//给输出比较通道3寄存器赋值
Delay_ms(10);
}
for(num=0;num<100;num++)
{
TIM_SetCompare3(TIM3,99-num);
Delay_ms(10);
}
}
}
上面的函数都有注释,可以自己看看,最终结果显示正确。