PWM波的理解
PWM波我们可以简单的理解为输出一个普通的矩形波
在C51单片机中通过I/O口直接输出,我们可以很简单的直接得到一个PWM波;输出一个高电平然后延迟一段时间,再输出一个低电平延迟一段时间,将其进行循环便可得到一个简易的PWM波,占空比可通过高电平与低电平延时时间的对应比例改变,代码如下:
//输出一个周期为50ms的PWM波
whlie(1)
{
size = 0.6; //占空比为50%
P0^0 = 0; //输出低电平
//delay(1)为10ms
delay(5*(1-size));
P0^0 = 1; //输出高电平
delay(5*size);
}
在C51单片机中通过定时器产生一个PWM波(其实就是将delay延迟改为计数器延时,频率可更快,也更加精准)
//输出一个周期为5ms的PWM波
void tim1init()//定时器1初始化
{
TMOD=0X10; //t1 方式1
TH1=0XFC;
TL1=0X18;//定时1ms
TR1=1; //打开t1定时器
}
void main()
{
size = 0.5; //占空比为60%
int i = 0;
while(1)
{
if(TF == 1)
{
TF = 0;//重装载计数器
i = i+1;
if(i == 5)
i=0;
}
if(i < size*5)
P0^0 = 1; //输出高电平
else
P0^0 = 0; //输出低电平
}
}
相对于以上C51单片机的代码来讲,stm32单片机有一全套关于PWM波输出的库函数,相对比较可视化,但也比较复杂。(也是我们本次主要讲的重点)
首先我们先了解一下stm32单片机是怎样形成PWM波输出的
其实跟我们上面C51单片机的原理相差无几,通过设定一个CCRX值与计数值进行比较;通过设定CCER来控制有效电平为高电平还是低电平,通过设定CCMRX寄存器来控制计数值比CCRX值大为有效电平还是比CCRX值小为有效电平。(从下面的图可以很明显看出,设置溢出值ARR可以调整输出频率,设置CCRX值可以调整占空比)也可以很清晰看出该种PWM波频率更加快
控制PWM波的所有寄存器以及相对应的作用
由于stm32单片机有一全套的库函数可供调用,故关于PWM的库函数配置过程如下:(以下用定时器14为例)
-
使能定时器14和相关I/O口时钟
使能定时器14时钟:RCC_APB1PeriphClockCmd();
使能GPIOF时钟:RCC_AHB1PeriphClockCmd(); -
初始化I/O口为复用功能输出
…
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
GPIO_Init(); -
GPIOF复用映射到定时器14
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); -
初始化定时器:ARR//溢出数;PSC//分频系数
TIM_TimeBaseInit(); -
初始化PWM输出比较参数
TIM_OC1Init(); -
使能TIM14在CCR1上的预装载寄存器
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); -
ARPE使能
TIM_Cmd(TIM14, ENABLE); -
使能TIM14
TIM_Cmd(TIM14, ENABLE); -
改变CCRX值达到不同的占空比效果
TIM_SetCompare1();
以下为输出PWM波于LED灯实现亮度不断变化的效果的代码
void TIM14_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //TIM14时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能PORTF时钟
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化PF9
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
//初始化TIM14 Channel1 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能
TIM_Cmd(TIM14, ENABLE); //使能TIM14
}
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200);//初始化串口波特率为115200
TIM14_PWM_Init(500-1,84-1); //84M/84=1Mhz的计数频率,重装载值500,所以PWM频率为 1M/500=2Khz.
while(1) //实现比较值从0-300递增,到300后从300-0递减,循环
{
delay_ms(10);
if(dir)led0pwmval++;//dir==1 led0pwmval递增
else led0pwmval--; //dir==0 led0pwmval递减
if(led0pwmval>300)dir=0;//led0pwmval到达300后,方向为递减
if(led0pwmval==0)dir=1; //led0pwmval递减到0后,方向改为递增
TIM_SetCompare1(TIM14,led0pwmval); //修改比较值,修改占空比
}
}
通过比较很清晰知道,stm32单片机库函数实现的PWM波的周期为一个溢出周期,而以上两种方法为多个周期,可知stm32单片机库函数实现的PWM波的频率可远高于以上两种方法(其实C51单片机也可以在计数器中断函数进行设置值进行比较从而输出频率高的PWM波,但是书写起来极其麻烦)