PWM实现呼吸灯

PWM实现呼吸灯

一、PWM简介
脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制, PWM 原理如图 14.1.1 所示:
PWM实现呼吸灯_第1张图片
图 14.1.1 就是一个简单的 PWM 原理示意图。图中,我们假定定时器工作在向上计数 PWM模式,且当 CNT=CCRx 时输出 1。那么就可以得到如上的 PWM示意图:当 CNT 值小于 CCRx 的时候, IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候,IO 输出高电平(1), 当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。
STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出!这里我们使用 TIM3的 CH2 产生一路 PWM 输出。
要使 STM32 的通用定时器 TIMx 产生 PWM 输出,我们会用到这 3 个寄存器 来控制 PWM 的 。这三个寄存器分别是 :捕获 /比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。接下来我们简单介绍一下这三个寄存器。
首先是捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器总共有 2 个, TIMx _CCMR1和 TIMx _CCMR2。 TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。该寄存器的各位描述如图 14.1.2 所示:
PWM实现呼吸灯_第2张图片
该寄存器的有些位在不同模式下,功能不一样,所以在图 14.1.2 中,我们把寄存器分了 2层,上面一层对应输出而下面的则对应输入。这里我们需要说明的是模式设置位 OCxM,总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种 PWM 模式的区别就是输出电平的极性相反。 另外 CCxS 用于设置通道的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。接下来,我们介绍捕获/比较使能寄存器(TIMx_CCER),该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如图 14.1.3 所示:
在这里插入图片描述
该寄存器比较简单, 我们这里只用到了 CC2E 位,该位是输入/捕获 2 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为1。
最后,我们介绍一下捕获/比较寄存器(TIMx_CCR1~4),该寄存器总共有 4 个,对应 4 个输通道 CH1~4。因为这 4 个寄存器都差不多,我们仅以 TIMx_CCR1 为例介绍,该寄存器的各位描述如图 14.1.4 所示:
PWM实现呼吸灯_第3张图片
在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。 本章,我们使用的是 TIM3的通道 2,所以我们需要修改 TIM3_CCR2 以实现脉宽控制 DS0 的亮度。
我们要使用 TIM3 的 CH2 输出 PWM 来控制 DS0 的亮度,但是 TIM3_CH2 默认是接在 PA7上面的,而我们的 DS0 接在 PB5 上面,如果普通 MCU,可能就只能用飞线把 PA7 飞到 PB5上来实现了,不过,我们用的是 STM32,它比较高级,可以通过重映射功能,把 TIM3_CH2映射到 PB5 上。
STM32 的重映射控制是由复用重映射和调试 IO 配置寄存器(AFIO_MAPR)控制的,该寄存器的各位描述如图 14.1.5 所示:
PWM实现呼吸灯_第4张图片
我们这里用到的是 TIM3 的重映射,从上图可以看出, TIM3_REMAP 是[11:10]这 2 个位控制的。 TIM3_REMAP[1:0]重映射控制表如表 14.1.1 所示:
PWM实现呼吸灯_第5张图片
默认条件下, TIM3_REMAP[1:0]为 00,是没有重映射的,所以 TIM3_CH1~TIM3_CH4 分别是接在 PA6、 PA7、 PB0 和 PB1 上的,而我们想让 TIM3_CH2 映射到 PB5 上,则需要设置TIM3_REMAP[1:0]=10,即部分重映射,这里需要注意,此时 TIM3_CH1 也被映射到 PB4 上了。
至此,我们把本章要用的几个相关寄存器都介绍完了, 本章要实现通过重映射 TIM3_CH2到 PB5 上,由 TIM3_CH2 输出 PWM 来控制 DS0 的亮度。 下面我们介绍配置步骤:
1)开启 TIM3 时钟,配置 PB5 为复用输出。
要使用 TIM3,我们必须先开启 TIM3 的时钟(通过 APB1ENR 设置),这点相信大家看了这么多代码,应该明白了。这里我们还要配置 PB5 为复用输出,这是因为 TIM3_CH2 通道将重映射到 PB5 上,此时, PB5 属于复用功能输出。
2) 设置 TIM3_CH2 重映射到 PB5 上。
因为 TIM3_CH2 默认是接在 PA7 上的,所以我们需要设置 TIM3_REMAP 为部分重映射(通过 AFIO_MAPR 配置),让 TIM3_CH2 重映射到 PB5 上面。
3)设置 TIM3 的 ARR 和 PSC。
在开启了 TIM3 的时钟之后,我们要设置 ARR 和 PSC 两个寄存器的值来控制输出 PWM 的周期。当 PWM 周期太慢(低于 50Hz)的时候,我们就会明显感觉到闪烁了。因此, PWM 周期在这里不宜设置的太小。
4)设置 TIM3_CH2 的 PWM 模式。
接下来,我们要设置 TIM3_CH2 为 PWM 模式(默认是冻结的),因为我们的 DS0 是低电平亮,而我们希望当 CCR2 的值小的时候, DS0 就暗, CCR2 值大的时候, DS0 就亮,所以我们要通过配置 TIM3_CCMR1 的相关位来控制 TIM3_CH2 的模式。
5)使能 TIM3 的 CH2 输出,使能 TIM3。
在完成以上设置了之后,我们需要开启 TIM3 的通道 2 输出以及 TIM3。前者通过
TIM3_CCER1 来设置,是单个通道的开关,而后者则通过 TIM3_CR1 来设置,是整个 TIM3 的总开关。只有设置了这两个寄存器,这样我们才能在 TIM3 的通道 2 上看到 PWM 波输出。
6)修改 TIM3_CCR2 来控制占空比。
最后,在经过以上设置之后, PWM 其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改 TIM3_CCR2 则可以控制 CH2 的输出占空比。继而控制 DS0 的亮度。
通过以上 6 个步骤,我们就可以控制 TIM3 的 CH2 输出 PWM 波了。

//TIM3 PWM 部分初始化
//PWM 输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
//此部分需手动修改 IO 口设置
	RCC->APB1ENR|=1<<1; //TIM3 时钟使能
	RCC->APB2ENR|=1<<3; //使能 PORTB 时钟
	GPIOB->CRL&=0XFF0FFFFF; //PB5 输出
	GPIOB->CRL|=0X00B00000; //复用功能输出
	RCC->APB2ENR|=1<<0; //开启辅助时钟
	AFIO->MAPR&=0XFFFFF3FF; //清除 MAPR 的[11:10]
	AFIO->MAPR|=1<<11; //部分重映像,TIM3_CH2->PB5
	TIM3->ARR=arr; //设定计数器自动重装值
	TIM3->PSC=psc; //预分频器不分频
	TIM3->CCMR1|=7<<12; //CH2 PWM2 模式
	TIM3->CCMR1|=1<<11; //CH2 预装载使能
	TIM3->CCER|=1<<4; //OC2 输出使能
	TIM3->CR1=0x0080; //ARPE 使能
	TIM3->CR1|=0x01; //使能定时器 3
}

此部分代码包含了上面介绍的 PWM 输出设置的前 5 个步骤。这里我们关于 TIM3 的设置就不再说了, 这里提醒下:在配置 AFIO 相关寄存器的时候,必须先开启辅助功能时钟。

void LED_Init(void)
{
	RCC->APB2ENR|=1<<3;    //使能PORTB时钟	   	 
	RCC->APB2ENR|=1<<6;    //使能PORTE时钟	
	   	 
	GPIOB->CRL&=0XFF0FFFFF; 
	GPIOB->CRL|=0X00300000;//PB.5 推挽输出   	 
    GPIOB->ODR|=1<<5;      //PB.5 输出高
											  
	GPIOE->CRL&=0XFF0FFFFF;
	GPIOE->CRL|=0X00300000;//PE.5推挽输出
	GPIOE->ODR|=1<<5;      //PE.5输出高 
}

初始化PB5和PE5为输出口.并使能这两个口的时钟 LED IO初始化

void TIM3_IRQHandler(void)
{ 		    		  			    
	if(TIM3->SR&0X0001)//溢出中断
	{
		LED1=!LED1;			    				   				     	    	
	}				   
	TIM3->SR&=~(1<<0);//清除中断标志位 	    
}

该部分代码主要是定时器3中断服务函数。

void TIM3_Int_Init(u16 arr,u16 psc)
{
	RCC->APB1ENR|=1<<1;	//TIM3时钟使能    
 	TIM3->ARR=arr;  	//设定计数器自动重装值//刚好1ms    
	TIM3->PSC=psc;  	//预分频器7200,得到10Khz的计数时钟		  
	TIM3->DIER|=1<<0;   //允许更新中断	  
	TIM3->CR1|=0x01;    //使能定时器3
  	MY_NVIC_Init(1,3,TIM3_IRQn,2);//抢占1,子优先级3,组2	
 }

通用定时器初始化。

#include "sys.h"
#include "delay.h"
#include "usart.h" 
#include "led.h" 
#include "timer.h" 
//ALIENTEK精英STM32F103开发板 实验9
//PWM输出 实验  
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司 
int main(void)
{					   
	u16 led0pwmval=0;    
	u8 dir=1;	
	Stm32_Clock_Init(9);	//系统时钟设置
	uart_init(72,115200);	//串口初始化为115200
	delay_init(72);	   	 	//延时初始化 
	LED_Init();		  		//初始化与LED连接的硬件接口
  	TIM3_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;	   					 
		LED0_PWM_VAL=led0pwmval;	   
	}
}

主函数代码。
通过以上的程序就可以通过PWM实现呼吸灯闪烁。接下来将通过STM32来完成一个4轮避障小车。实现一个定时器的4个通道来实现PWM电机调速。来加深对PWM的理解。

你可能感兴趣的:(32单片机)