目录
学习目标
本节内容我们来介绍关于STM32中有关PWM的使用,其实这部分在51中我们已经接触过,再次在32中接触,第一感觉就是繁琐,特别繁琐,在51中几行代码就能解决的东西,这里还要各种模式配来配去。不过好在原理都是一样的,也没有复杂多少,而且32的PWM配置给我一种很踏实的感觉,并且精度也更高,所以繁琐一点也没事。
运行结果
呼吸灯32
舵机
内容
这章要介绍的东西相对而言比较少,因为PWM本身也不是特别难以理解的东西,而且在51中也已经介绍过了,感兴趣的同学可以看看这篇文章:51单片机的PWM 。
介绍
如果要我来简单概括一下PWM到底是什么东西的话,我觉得化直为曲可以用来简单概括一下他。PWM相当于改变每一点的电压值,做到将恒定的电压变成非恒定的电压。
PWM
- PWM(Pulse Width Modulation)即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速、开关电源等领域
- PWM重要参数:
- 频率 = 1 / TS
- 占空比 = TON / TS
- 精度 = 占空比变化步距
产生PWM
模型结构
波形
我们假定定时器工作在向上计数 PWM 模式,且当 CNT=CCRx 时输出 1。那么就可以得到如上的 PWM 示意图:当 CNT 值小于 CCRx 的时候,IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候, IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。 改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。
模式选择
值得注意的地方就是,与51单片机不一样,32还有许多的寄存器控制,其中还有PWM模式选择这一说法。
总而言之,这一步就是设置有效状态,但是有效状态对应是高电平还是低电平,还是需要我们自己去设置的。
寄存器
还是一样的,寄存器部分我们不做详细介绍。
- CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。
- CCMR1: OC1M[2:0]位:对于PWM方式下,用于设置PWM模式1(110)或者PWM模式2(111)
- CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
- CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。
自动重载
简单描述一下,就是当ARPE为1时,自动重载立马生效,而当ARPE为0时,自动重载下一个周期生效。
配置
- 开启 TIM14 和 GPIO 时钟,配置 PF9 选择复用功能 AF9(TIM14)输出。
- 初始化 TIM14,设置 TIM14 的 ARR 和 PSC 等参数。
- 设置 TIM14_CH1 的 PWM 模式,使能 TIM14 的 CH1 输出。
- 使能 TIM14。
- 使能预装载寄存器。
- 修改 TIM14_CCR1 来控制占空比。
具体流程在代码中给出。
代码
#include "pwm.h"
//TIM14 PWM输出初始化
void TIM14_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE ); // TIM14时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); // GPIO使能
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);
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; //定时器分频,psc:时钟预分频数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值,arr:自动重装值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //分频1
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
//初始化TIM14_CH1 PWM模式
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//选择定时器模式
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM14, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM14, ENABLE); //ARPE使能
TIM_Cmd(TIM14, ENABLE); //使能TIM14
}
// 呼吸灯
#include "sys.h"
#include "delay.h"
#include "pwm.h"
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init(168); //初始化延时函数
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); //修改比较值,修改占空比
}
}
// 舵机
#include "sys.h"
#include "delay.h"
#include "pwm.h"
#include "key.h"
int main(void)
{
u16 pwmval=7200;
delay_init(168); //初始化延时函数
TIM14_PWM_Init(10000-1,84-1); //84M/84=1Mhz的计数频率,重装载值10000,所以PWM周期为2ms.
KEY_Init();
while(1)
{
if(KEY_Scan(1) == KEY0_PRES){ // 支持连续按下,按键为0时,PWM加10
pwmval += 10;
}
if(KEY_Scan(1) == KEY1_PRES){ // 支持连续按下,按键为0时,PWM减10
pwmval -= 10;
}
TIM_SetCompare1(TIM14,pwmval); //修改比较值,修改占空比
}
}