stm32 PWM输出学习

STM32 的定时器除了 TIM6 和 7,其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。通用定时器也能同时产生多达 4路的 PWM 输出。

今天的实验,我们仅利用 TIM3的 CH2 通道产生一路 PWM 输出。

1.相关寄存器介绍

1)捕获/比较模式寄存器 (TIMx_CCMR1/2) 

捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器总共有 2 个,TIMx  _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制 CH3 和 CH4。


OCxx描述了通道在输出模式下的功能(上行),ICxx描述了通道在输出模式下的功能(下行)。因此必须注意,同一个位在输出模式和输入模式下的功能是不同的。 

这里我们需要说明的是模式设置位 OCxM,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种PWM 模式的区别就是输出电平的极性相反。 


这里的有效电平或者无效电平是可以配置的。

2) 捕获/比较使能寄存器(TIMx_CCER) 



3)捕获/比较寄存器(TIMx_CCR1~4)

该寄存器总共有 4 个,对应 4 个通道 CH1~CH4。因为这 4 个寄存器都差不多,我们仅以 TIMx_CCR1 为例介绍。


我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽。

2. TIM3_REMAP 重映射

我们要利用 TIM3 的 CH2 输出 PWM 来控制 LED的亮度,但是 TIM3_CH2 默认是接在 PA7上面的,而我们的 LED接在 PB5 上面,如果是普通 MCU,可能就只能用飞线把 PA7 飞到 PB5上;不过,我们用的是 STM32,可以通过重映射功能,把 TIM3_CH2映射到 PB5 上。 


3.代码难点分析

1)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能TIM3时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);  
//使能GPIOB时钟(PB5连接LED)和AFIO复用功能时钟

为什么要使能RCC_APB2Periph_AFIO呢?

参考手册上说:

对寄存器AFIO_EVCR(事件控制寄存器),AFIO_MAPR( 复用重映射和调试I/O配置寄存器),AFIO_EXTICRX(外部中断配置寄存器)进行读写操作前,应当首先打开AFIO的时钟。

因为我们在这个实验中用到了AFIO_MAPR,所以要打开AFIO时钟。

2)GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射,TIM3_CH2->PB5 

3)GPIO配置

//设置该引脚为复用输出功能,输出TIM3-CH2的PWM波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //推挽复用输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);


4)TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 

     可以参考捕获/比较使能寄存器(TIMx_CCER)位0, 输出使能。

5)TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 

     可以参考捕获/比较使能寄存器(TIMx_CCER)位1, 高/低 电平有效。

6)TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); 预装载使能

     可以参考捕获/比较模式寄存器 (TIMx_CCMR1/2) 位3.


7)TIM_SetCompare2(TIM3,300);

参考捕获/比较寄存器(TIMx_CCR1~4),这里设置比较值为300.

4. 代码参考

//PWM输出初始化函数
//arr:自动重装载值
//psc: 预分频系数
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能TIM3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);  
	//使能GPIOB时钟(PB5连接LED)和AFIO复用功能时钟
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射,TIM3_CH2->PB5    
 
    //设置该引脚为复用输出功能,输出TIM3-CH2的PWM波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
 
    //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; 
	TIM_TimeBaseStructure.TIM_Prescaler = psc; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 
	
	//TIM3 Channel2 PWM 初始化
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM2模式
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //捕获/比较使能寄存器(TIMx_CCER) 输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高电平有效
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //预装载使能
		
	TIM_Cmd(TIM3, ENABLE);  //开启TIM3

}

main函数如下

int main(void)
{	
	
	TIM3_PWM_Init(999,71); //72/(71+1)=1MHz, T=1us*(999+1) = 1ms
	TIM_SetCompare2(TIM3,300);	//脉宽= 300/1000 *1ms = 0.3ms
   	while(1)
	{
		;	   
	}	 
}

仿真说明:我们的代码设置为:

PWM2模式,向上计数,高电平有效。

所以,TIMx_CNT<TIMx_CCRx时通道x为低,否则为高。

5.仿真效果图如下


6. 疑问

如果调换main函数两句的顺序,则无法达到预期效果。

<span style="font-size:18px;">int main(void) //实验失败
{	
	TIM_SetCompare2(TIM3,300);	//脉宽= 300/1000 *1ms = 0.3ms
	TIM3_PWM_Init(999,71); //72/(71+1)=1MHz, T=1us*(999+1) = 1ms
	
   	while(1)
	{
		;	   
	}	 
}</span>

至于原因,这里不讨论,留待以后思考。

7. 用PWM控制LED的亮度

int main(void)
{		
 	u16 led0pwmval = 0; //比较值
	u8 dir = 1;	
	delay_init();	    	 
	
 	TIM3_PWM_Init(899,0);	 
	
   	while(1)
	{
 		delay_ms(10);	 
		
		if(dir)
			led0pwmval++;
		else 
			led0pwmval--;

 		if(led0pwmval>300)
			dir=0;
		if(led0pwmval==0)
			dir=1;										 
		
		TIM_SetCompare2(TIM3,led0pwmval);		   
	}	 
}

这里,我们将 led0pwmval 这个值设置为 PWM 比较值,也就是通过 led0pwmval 来控制 PWM 的占空比,进而控制LED的平均电流,达到控制LED亮度的目的。

 led0pwmval 的值从 0 变到 301,然后又从 301 变到 0,如此循环,LED的亮度也会跟着从暗变到亮,然后又从亮变到暗。至于这里的值,我们为什么取 300,是因为 PWM 的输出占空比达到并且超过这个值的时候, LED 亮度变化就不明显了。



原文地址:https://www.cnblogs.com/longintchar/p/5224422.html