PWM实验

1.PWM简介

脉冲宽度调制(PWM),是利用微处理器的数字输出来对模拟电路 进行控制的 一种非常有效的技术,即就是对脉冲宽度的控制。

STM32的定时器除了TIM6和7,其他的定时器都可以用来产生PWM输出。其中高级定时器TIM1和TIM8可以同时 产生多达7路的PWM输出。而通用定时器也能同时产生多达4路的PWM输出,这样,STM32最多可以同时产生30路PWM输出。这个实验我们仅使用TIM1的CH1产生一路PWM输出。

要使用STM32的高级定时器TIMI产生PWM输出,我们会用到4个寄存器来控制PWM的输出。这四个寄存器分别是:捕获/比较模式寄存器、捕获/比较使能寄存器、捕获/比较寄存器以及刹车和死区寄存器。

捕获/比较模式寄存器,该寄存器总共有2个,TIMxCCMR1和TIMx_CCMR1 控制CH1和2,而TIMx_CCMR2控制CH3和CH4。该寄存器的各位描述

该寄存器的有些位在不同的模式下,功能不一样,把寄存器分了2层,上面一层对应输出时的设置而下面的则对应输入时的设置。

这里需要说明的是模式设置位OCxM,此部分由3位组成。总共可以配置成7个模式,我们使用的是PWM模式,这3位必须设置为110/111。这两种PWM模式的区别就是输出电平的极性相反。另外CCxs用于设置通道的方向(输入/输出)默认设置为0,就是设置通道作为输出使用。

捕获/比较使能寄存器(TIMx_CCER)该寄存器控制着各个输入输出通道的开关。

该寄存器比较简单,只用到了CC1E位,该位是输入/捕获1输出使能位,要想PWM从IO输出,这个位必须设置为1,所以我们需要设置该位为1.

我们介绍一下捕获/比较寄存器(TIMx_CCR1-4),该寄存器总共有4个,对应4个茶孙虎通道CH1-4。因为这4个寄存器都差不多,我们仅以TIMx_CCR1为例介绍,该寄存器的各位描述如图所示:

在输出模式下,该寄存器的值与CNT的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制PWM的输出脉宽了。本章,我们使用的是TIM1的通道1,所以我们需要修改TIM1_CCR1以实现脉冲控制DS0的亮度。

如果是通用定时器,则配置以下三个寄存器就够了,但是如果是高级定时器,则还需要配置:刹车和死区寄存器(TIMx_BDTR),

该寄存器我们只关注最高位:MOE位,要想高级定时器的PWM正常输出,则必须设置MOE位为1,否则不会有输出。注意:通用定时器不需要配置这个。

本次试验实现通过TIM1_CH1输出PWM来控制DS0的亮度。下面介绍配置步骤:

1)开启TIM1时钟,配置PA8为复用输出。

要使用TIM1,我们必须先开启TIM1的时钟,还需要配置PA8为复用输出。库函数使能TIM3时钟的方法是:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器 3 时钟
设置PA8为复用功能输出的方法在前面的几个实验都有类似的讲解,这里简单的列出GPIO初始化的一行代码即可

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
2)设置TIM1的ARR和PSC。

在开启了TIM1的时钟之后,我们要设置ARR和PSC两个寄存器的值 来控制输出PWM的周期。当PWM周期太慢(低于50HZ)的时候,我们就会明显感觉到闪烁了。因此,PWM周期在这里不宜设置太小。 这在库函数是通过TIM_TimeBaseInit函数实现的

调用的格式为:

TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx 的
3)设置TIM1_CH1的PWM模式及通道方向,使能TIM1的CH1输出。

接下来,我们要设置TIM1_CH1为PWM模式(默认是冻结的),因为我们的DS0是低电平亮,而我们希望当CCR1的值小的时候,DS0就暗,CCR1值大的时候,DS0就亮,所以我们通过配置TIM1_CCMR1的相关位来控制TIM1_CH1的模式。在库函数中,PWM通道设置是通过函数TIM_OC1Init()~TIM_oc4Init()来设置的,不同的通道的设置函数不一样,这里我们使用的是通道1,所以使用的函数是TIM_OC1Init().

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
直接来看看结构体TIM_OCIitTypeDef的定义:

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还是输出比较,这里我们是PWM 模式。

参数TIM_OutputState用来设置比较输出使能,也就是使能PWM输出到端口。

参数TIM_OCPolarity用来 设置极性是高还是低。

其他的参数其他的参数 TIM_OutputNState, TIM_OCNPolarity, TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器 TIM1 和 TIM8 才用到的。

要实现这个功能,方法是:

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。

在完成以上设置了之后,我们需要使能TIM1。

TIM_Cmd(TIM1, ENABLE); //使能 TIM1
5)设置MOE输出,使能PWM输出。

普通定时器在完成以上设置了之后,就可以输出PWM了,但是高级定时器,我们还需要使能刹车和死区寄存器(TIM1_BDTR)的MOE位,以使能整个OCx(即PWM)输出。库函数的设置函数为:

TIM_CtrlPWMOutputs(TIM1,ENABLE);// MOE 主输出使能
6)修改TIM1_CCR1来控制占空比。

最后,在经过以上设置之后,PWM其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改TIM_CCR则可以控制CH的输出占空比。继而控制DS的亮度。在库函数中,修改TIM_CCR占空比的函数是:

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
对于其他通道,分别有一个函数名字,函数格式为 TIM_SetComparex(x=1,2,3,4)。
通过以上 6 个步骤,我们就可以控制 TIM1 的 CH1 输出 PWM 波了。


2.硬件设计

1)指示灯DS0

2)定时器TIM3

我们这里用到了TIM1_CH1通道的输出,TIM1_CH1是和PA8相连的,所以电路上并没有任何变化。

3.软件设计

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 输出设置的前 5 个步骤。这里我们关于 TIM1 的设置
就不再说了。
头文件 pwm.h 主要是函数申明,这里就不做过多讲解。
接下来,我们看看主函数 main 内容如下:
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);
}
}
这里,我们从死循环函数可以看出,我们控制 LED0_PWM_VAL 的值从 0 变到 300,然后
又从 300 变到 0,如此循环,因此 DS0 的亮度也会跟着从暗变到亮,然后又从亮变到暗。至于
这里的值,我们为什么取 300,是因为 PWM 的输出占空比达到这个值的时候,我们的 LED 亮
度变化就不大了(虽然最大值可以设置到 899),因此设计过大的值在这里是没必要的。至此,
我们的软件设计就完成了。
 

 

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值