一,PWM简介
PWM是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。本实验中我们利用PWM对电平极性输出占比进行调控。下图为PWM原理图
图中的ARR即是我们上一个实验提到的自动重装载周期值,达到该周期值后清零重新向上计数。而CCRx为本次实验中由我们设定的比较值,我们通过相应的函数控制该值变化,达到控制输出占比的目的。我们可以看到低于该值的输出电平和高于该值的电平不同,图中高于该值输出高电平,低于该值输出低电平。
下面再来简单说两句寄存器,首先是捕获/比较模式寄存器(TIMx_CCMR1/2),其中TIMx_CCMR1 控制 通道CH1 和 CH2,而 TIMx_CCMR2 控制 通道CH3 和 CH4。我们用到的是模式设置位 OCxM,有三位,而我们使用的是 PWM 模式,这 3 位必须设置为 110/111。110对应模式1,此模式无论如何计数,只有计数值小于比较值时才为有效电平,否则为无效电平。111则是计数值大于比较值为有效电平,否则为无效电平。
之后是捕获/比较使能寄存器(TIMx_CCER), CC1E 位位是输入/捕获 1 输出使能位,要想 PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。而CC1P位为输入/捕获 1 输出极性位,设置为0为高电平有效,1为低电平有效。看我们的需求设置。
最后是捕获/比较寄存器(TIMx_CCR1~4),四个通道各对应一个寄存器。我们通过该寄存器设置比较值。
二,库函数设置
1)使能TIM1 时钟和GPIOA时钟,配置 PA8 为复用输出。
我们要使用定时器,就必须使能其时钟,同时因为我们使用PA8作为输出引脚,也必须使能GPIO时钟并将PA8设置为端口复用。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器 3 时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
2)设置 TIM1 的 ARR 和 PSC。
配置自动重装载值和预分频值,控制PWM周期。这两个值在上个实验讲解过,不多赘述。
3)设置 TIM1_CH1 的 PWM 模式及通道方向, 使能 TIM1 的 CH1 输出。
在库函数中,PWM 通道设 置是通过函数 TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样,这里我 们使用的是通道 1,所以使用的函数是 TIM_OC1Init()。
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
其中结构体的定义为
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState; */
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;
本实验只用到其中几个参数。
参数 TIM_OCMode 设置模式是PWM模式1或PWM模式2.
TIM_OutputState设置输出使能或失能。
Pulse是比较值,写CCRx。TIM_OCPolarity 用来设置极性是高还是低。
例如
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //初始化 TIM1 OC1
4)使能 TIM1。
TIM_Cmd(TIM1, ENABLE); //使能 TIM1
5)修改 TIM1_CCR1 来控制占空比。
修改 TIM1_CCR1 占空比的函数是:
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
对于其他通道,分别有一个函数名字,函数格式为 TIM_SetComparex(x=1,2,3,4)。
三,硬件设计
本实验用到的硬件资源有: 1) 指示灯 DS0 2) 定时器 TIM3
这两个前面实验都介绍过,虽然本实验用到定时器CH1通道的输出。但该通道与PA8相连,故电路无变化。
四,软件设计
本实验我们再工程中添加了pwm.c及其头文件。
pwm.c代码如下
#include "pwm.h"
#include "led.h"
//PWM 输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// ①使能 tim1 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
//①使能 GPIO 外设时钟使能
//设置该引脚为复用输出功能,输出 TIM1 CH1 的 PWM 脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr;
//设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =psc;
//设置用来作为 TIMx 时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //②初始化 TIMx
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //脉宽调制模式 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //③初始化外设 TIMx
TIM_CtrlPWMOutputs(TIM1,ENABLE); //⑤MOE 主输出使能
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1 预装载使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能 TIMx 在 ARR 上的预装载寄存器
TIM_Cmd(TIM1, ENABLE); //④使能 TIM1
}
梳理一下思路,这段代码只有PWM的初始化函数。首先是三个结构体,之后是GPIO外设时钟和定时器时钟使能。下面是GPIO配置参数,引脚为PA8(对应TIM_ CH1),模式为复用推挽输出,端口速率为50MHz.完成GPIO初始化。之后设置arr为自动重装载值,psc为预分频值,与上个实验一样。之后设置时钟分割值为0,代表TDTS=Tck_tim。设置计数模式为向上计数模式。成功初始化TIMx。之后初始化外设TIMx,模式设置为PWM模式2,使能比较输出。之后设置初始比较值为0,设置高电平为有效电平。然后使能CH1预装载和TIMx的预装载寄存器。最后使能定时器。
主函数main.c代码如下
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init(); //延时函数初始化
LED_Init(); //初始化与 LED 连接的硬件接口
TIM1_PWM_Init(899,0);//不分频。PWM 频率=72000/(899+1)=80Khz
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare1(TIM1,led0pwmval);
}
}
先定义比较值和方向值。之后初始化LED和延时函数,以及PWM,PWM的自动重装载值设为899,预分频值设为0。则PWM频率为80khz。然后一个死循环。延时10毫秒。方向值为1则比较值增加,方向值为0则比较值减小。比较值加到300或减到0都会使方向值反转。最后是一个比较值改变的函数。得到的结果就是LED灯的亮度随时间由暗变亮在变暗,如此往复。
检查无误后就可以下载验证了。