1. 程式人生 > >stm32 PWM輸出學習

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 亮度變化就不明顯了。