STM32F103C8T6片上外设笔记3——PWM
首先,PWM波的输出,是靠定时器来完成的。在手册中,定时器的功能描述,我们也能看到包含了PWM的生成。对于生成PWM,通过手册所给的框图,我们可以看到使用的是这一部分CNT计数器,计的数会与捕获比较寄存器进行比较,也就是会CNT与CCR进行比较。在STM32给的库函数中,有以下几种输出比较模式其他模式用处较少,常用的是PWM模式1。也就是说,若是向上计数,当定时器在CNT的数小于CRR时,会输出低
一、PWM的原理简单介绍
1、占空比
PWM通常用来驱动舵机、电机,或是让LED灯呈现呼吸效果。基本原理,就是在一定时间内,通过控制高低电平持续时间的占比,来控制输出强度。
比如以10毫秒秒为周期,低电平点亮LED。那么低电平的时间为7毫秒,高电平的时间为3毫秒,那么在视觉上,LED灯发出的光,肯定比低电平的时间为3毫秒时更亮。
(实际上LED的发光时的亮度是一定的,只是让它总体被点亮的时间变长,LED更多的时间让自己完全亮起,因为视觉滞留效应,就会感觉更亮)
而高电平的持续时间,与一个周期的时间之比,就被称为占空比。由此也可以很轻松的想到,占空比,就是控制PWM输出强度的关键。

此时我们就可以说占空比为40%。
而由这无数个高低电平切换,所形成的波形,我们就称为PWM波形。
这就算是对PWM有了简单的理解,其本质就是通过控制高低电平的变化,来控制模拟电压的大小。
2、频率
PWM还有一个重要的属性,就是频率。
比如,1秒为一个周期,也就是PWM的频率为1HZ,此时我们高电平维持0.5秒,低电平维持0.5秒,我们就会发现,LED灯,是在闪烁的。
而当我们以10毫秒为一个周期,也就是PWM的频率为100HZ时,在这个频率下,我们的肉眼是感知不到LED的闪烁的。并且因为在10毫秒内,LED快速转换了亮灭状态,LED还未完全亮起就熄灭,所以我们会感觉LED比起上者,亮度会减小一半。
在此基础上,我们在不停改变占空比,就能实现LED呼吸灯的效果。
并且,频率越大,就会让PWM输出的越平滑,但是相对应的开销也会越大,所以选择合适的频率至关重要。
所以可以看出,频率是让所驱动对象能否正常实现我们想要的效果的关键。
3、分辨率
确定电平变化的步距,越高越好,但是与频率一样,对于硬件的要求也会越高,一般情况下,1%就足够使用。
二、PWM的代码介绍
1、定时器
首先,PWM波的输出,是靠定时器来完成的。

在手册中,定时器的功能描述,我们也能看到包含了PWM的生成。
对于生成PWM,通过手册所给的框图,我们可以看到使用的是这一部分


CNT计数器,计的数会与捕获比较寄存器进行比较,也就是会CNT与CCR进行比较。
在STM32给的库函数中,有以下几种输出比较模式

其他模式用处较少,常用的是PWM模式1。
也就是说,若是向上计数,当定时器在CNT的数小于CRR时,会输出低电平,大于时,会输出高电平。

在手册中,我们能看到,在输出比较部分,还有一个选择极性的非门

通过这个非门,我们可以改变极性,也就是可以互换高低电平的变化。
当置0的时候,则没有变化。当置1的时候,则会使输出的电平颠倒。
综上所述,我们便知道了大致如何配置定时器,以实现PWM波形了。
以下是确定PWM各项数值的计算公式

2、代码部分
前半段正常开启定时器。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启定时器时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启IO口时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//配置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);//设置内部时钟为定时器时钟
以我们驱动电机为例,电机要求PWM的频率在1KHZ~20KHZ之间,我们设置PWM的分辨率为1%,PWM的频率为20KHZ
根据上面的公式,我们可以通过分辨率确定ARR的值。也就是 1 / (ARR+1)= 1%,所以ARR=100-1;由此也可以确定了PSC的值,也就是72MHZ / (PSC + 1)/(ARR + 1),PSC = 36 + 1;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100-1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
接下来,便可以设置关于PWM的结构体了。
注意,我们设置的输出比较通道,示例代码用的是通道三。
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM模式1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//向上计数模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能输出比较
TIM_OCInitStructure.TIM_Pulse = 50;//CRR
TIM_OC3Init(TIM2,&TIM_OCInitStructure);
最后使能定时器。
TIM_Cmd(TIM2,ENABLE);
在ARR确定的情况下,我们只需要改变CRR的值,便能很轻松的改变PWM的占空比,从而改变PWM的输出强度。
为了方便,我们可以单独写一个函数,再使用库函数中的一个函数,对于结构体中的CRR的值,进行单独的修改。
void PWM_SetCompare1(uint16_t dat)
{
TIM_SetCompare3(TIM2,dat);
}
这样,对于PWM的设置全部完成。
三、结语
贴上完整的代码
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启定时器时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启IO口时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//配置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);//设置内部时钟为定时器时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100-1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM模式1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//向上计数模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能输出比较
TIM_OCInitStructure.TIM_Pulse = 50;//CRR
TIM_OC3Init(TIM2,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t dat)
{
TIM_SetCompare3(TIM2,dat);
}
更多推荐



所有评论(0)