既然我们要用定时器输出PWM波,那么我们不妨做一个小的PWM知识的介绍
(wiki中对pwm的介绍):
https://zh.wikipedia.org/wiki/%E8%84%88%E8%A1%9D%E5%AF%AC%E5%BA%A6%E8%AA%BF%E8%AE%8A
1.伺服:
脉冲宽度调制可以用于控制伺服机构。
2. 电信
时间信号不一定要被包含脉冲宽度调制的传递上,事实上资料的领先端可以被当成一个计时器使用,如果有一个小的偏移量被加在资料值之上,去避免资料值有一个长度为零的脉冲波。
3. 能量的传递
脉冲宽度调制可以被用来控制对于一个载流子能量传递的多寡,而不会产生由阻抗所造成的线性能量传递损失。此方法所需要付出的代价是,载流子所流失的能量并非一个常数且是不连续的(如降压式变换器),载流子上传递的能量也不是连续的。然而,由于载流子可能是具有高频电感性的,这时就必须要外加一个被动的电子滤波器,让这些脉冲波变为平滑且能复原平均的模拟波型,能量流入载流子才会是连续的。而从供应端流出的能量则不是连续的,因此大部分情况下需要额外的能量储存空间。(比方说在一个电路下,一个电容会吸收储存于供应端电感的能量。)
—————————————————————————————————————————————————————————————————————————————
1. PWM每个定时器有四个通道可选(除了基本定时器)
通道:定时器产生的PWM波的输出引脚
定时器种类 | 位数 | 计数器模式 | 产生DMA请求 | 捕获/比较通道 | 互补输出 | 应用场景 |
---|---|---|---|---|---|---|
高级定时器(TIM1,TIM8) | 16 | 向上/向下/向上向下 | 可以 | 4 | 有 | 带死区控制盒紧急刹车,可应用于PWM电机控制 |
通用定时器(TIM2~5) | 16 | 向上,向下,向上/下 | 可以 | 4 | 无 | 通用。定时计数,PWM输出,输入捕获,输出比较 |
基本定时器(TIM6,TIM7) | 16 | 向上/向下/向上向下 | 可以 | 0 | 无 | 主要应用于驱动DAC |
而且,有的通道还支持端口的重映射功能,参照中文参考手册可以很有效的看出这些通道的配置:
PS:TIM4,TIM5不存在部分重映射
· 看完了这些端口重映射我们可以大致总结一下,在PWM的输出中,每一个通用/高级定时器总是有着四个端口可以选择,在考虑的PCB布线的合理性的选择中,我们可以使用端口重映射让单片机产生的PWM波输出到其他的引脚去,然而,这写引脚是固定的,所以,要通过查找中文参考手册来查表选择
· 端口重映射的时候,作为GPIO输出应该选择 GPIO_Mode_AF_PP,复用推挽输出模式
· 端口复用所用到的函数:
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //其中PartialRemap表示部分重映射,我们可以通过查找相应的库函数来找出这些功能的定义
2. PWM的输出模式,占空比详解
我们这里选取的计数模式:TIM_CounterMode_Up; //TIM向上计数模式
CNT:表示计数的数量
ARR:寄存器设置的计数上界
CCRx:表示比较的值
1. 周期:还记得我们上一讲讲解的 Tout=(arr+1)(psc+1)/Tclk
也就是说,在psc确定的情况下,一个pwm的周期(Tout)是由arr来决定的,通常,我们要让变化显得更加平滑,就要把psc设置为0,以方便让频率达到36Mhz
2. 占空比: 由图看出,CRRx(x是PWM通道序号)
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
3. PWM的输出模式(TIM_OCMode_PWM2):
a. PWM模式1:CNTCCRx通道为有效,否则为无效
PWM的输出配置:
1. 使能时钟及IO口
GPIO : RCC_APB2PeriphClockCmd();
定时器:RCC_APB1PeirphClockCmd();
2. GPIO初始化:GPIO_Init()
3. *端口的重映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemap(GPIO_PartialRemap_TIM3,ENABLE);
4. 初始化定时器
TIM_TimeBaseInit();
5. 初始化比较定参数:
TIM_OC2Init();
6. 使能预装载值寄存器:
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
7. 使能定时器:
TIM_Cmd();
8. 改变占空比:
TIM_SetCompareX();//X是通道的序号
#include "led.h"
#include "delay.h"
#include "sys.h"
//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void PWM3_init(short arr,short psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
led_init(); //LED端口初始化
PWM3_init(899,0); //不分频。PWM频率=72000000/900=80Khz
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare2(TIM3,led0pwmval);
}
}