1. 程式人生 > 其它 >STM32學習筆記-定時器、PWM與輸入捕獲

STM32學習筆記-定時器、PWM與輸入捕獲

STM32學習筆記-定時器、PWM與輸入捕獲

1. 定時器的時鐘

APB1和APB2上掛的外設如圖所示:

定時器不是直接來自於APB1和APB2,而是來自於輸入為APB1或APB2的一個倍頻器。

​ 當APB1的預分頻係數為1時,這個倍頻器不起作用,定時器的時鐘頻率等於APB1的頻率;當APB1的預分頻係數為其它數值(即預分頻係數為2、4、8或16)時,這個倍頻器起作用,定時器的時鐘頻率等於APB1的頻率兩倍。

​ 舉個例子:當AHB=72MHz時,APB1的預分頻係數必須大於2,因為APB1的最大頻率只能為36MHz。如果APB1的預分頻係數=2,則因為這個倍頻器,TIM2~7仍然能夠得到72MHz的時鐘頻率。能夠使用更高的時鐘頻率,無疑提高了定時器的解析度,這也正是設計這個倍頻器的初衷。

2. 計數時間和頻率

定時器的計數頻率有個公式:

​ TIMx_CLK = CK_INT / (TIM_Prescaler + 1)

​ 其中:TIMx_CLK 定時器的計數頻率

​ CK_INT 內部時鐘源頻率(APB1的倍頻器送出時鐘)

​ TIM_Prescaler 使用者設定的預分頻係數,取值範圍0~65535。

例如:RCC中AHB=72MHZ、APB1=36MHZ、APB2=72MHZ,則CK_INT=72MKZ。

計時時間設定:

​ 上述公式中TIM_Prescaler涉及到暫存器TIMx_PSC(16位暫存器)

​ 如果TIM_Prescaler設為36000,由上面公式可知:

​ 定時器的計數頻率 TIMx_CLK = 72MKZ / 36000 = 2000HZ,則定時器的計數週期=1/2000HZ=0.5ms.

​ 如果要定時1秒,則需要計數2000次,這也是自動重灌載的值。又涉及到TIMx_ARR(16位暫存器)

3. 定時器中斷實驗

//timer.c
#include "timer.h"
#include "led.h"
   	 

//通用定時器3中斷初始化
//這裡時鐘選擇為APB1的2倍,而APB1為36M
//arr:自動重灌值。
//psc:時鐘預分頻數
//這裡使用的是定時器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //時鐘使能
	
	//定時器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //設定在下一個更新事件裝入活動的自動重灌載暫存器週期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //設定用來作為TIMx時鐘頻率除數的預分頻值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據指定的引數初始化TIMx的時間基數單位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中斷,允許更新中斷

	//中斷優先順序NVIC設定
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先佔優先順序0級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //從優先順序3級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC暫存器


	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}
//定時器3中斷服務程式
void TIM3_IRQHandler(void)   //TIM3中斷
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //檢查TIM3更新中斷髮生與否
		{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中斷標誌 
		LED1=!LED1;
		}
}

//timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

void TIM3_Int_Init(u16 arr,u16 psc);
 
#endif


//main函式
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
 
 int main(void)
 {		
 
	delay_init();	    	 //延時函式初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序
	uart_init(115200);	 //串列埠初始化為115200
 	LED_Init();			     //LED埠初始化
	TIM3_Int_Init(4999,7199);//10Khz的計數頻率,計數到5000為500ms  
   	while(1)
	{
		LED0=!LED0;
		delay_ms(200);		   
	}	 

 
}	 
 


4. PWM

1. PWM輸出配置步驟

①:使能定時器3和相關IO口時鐘

//使能定時器3
RCC_APB1PeriphClockCmd();
//使能GPIOB(實驗為控制PB5連線的小燈輸出不同的亮度)
PCC_APB2PeriphClockCmd();

②:初始化IO口為複用功能輸出

//函式:
GPIO_Init();
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;		//複用推輓輸出

③:使用PB5作為PWM輸出引腳,所以要重對映配置,開啟AFIO時鐘同時設定重對映

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);

④: 初始化定時器:ARR,PSC等

TIM_TimeBaseInit();

⑤: 初始化輸出比較引數

TIM_OC2Init();

⑥: 使能預裝載暫存器

TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);

⑦: 使能定時器

TIM_Cmd();

⑧: 不斷改變比較值CCRx,達到不同的佔空比效果

TIM_SetCompare2();

2. 程式

//TIM3 PWM部分初始化 
//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);	//使能定時器3時鐘
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外設和AFIO複用功能模組時鐘
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重對映  TIM3_CH2->PB5    
 
   //設定該引腳為複用輸出功能,輸出TIM3 CH2的PWM脈衝波形	GPIOB.5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; //設定在下一個更新事件裝入活動的自動重灌載暫存器週期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //設定用來作為TIMx時鐘頻率除數的預分頻值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設定時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位
	
	//初始化TIM3 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈衝寬度調製模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根據T指定的引數初始化外設TIM3 OC2

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的預裝載暫存器
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	

}

//main函式
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"

int main(void)
 {		
 	u16 led0pwmval=0;
	u8 dir=1;	
	delay_init();	    	 //延時函式初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序
	uart_init(115200);	 //串列埠初始化為115200
 	LED_Init();			     //LED埠初始化
 	TIM3_PWM_Init(899,0);	 //不分頻。PWM頻率=72000000/900=80Khz
   	while(1)
	{
 		delay_ms(10);	 
		if(dir)led0pwmval++;
		else led0pwmval--;

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


5. 輸入捕獲

//定時器5通道1輸入捕獲配置

TIM_ICInitTypeDef  TIM5_ICInitStructure;

void TIM5_Cap_Init(u16 arr,u16 psc)
{	 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
   	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//使能TIM5時鐘
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA時鐘
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前設定  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 輸入  
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 //PA0 下拉
	
	//初始化定時器5 TIM5	 
	TIM_TimeBaseStructure.TIM_Period = arr; //設定計數器自動重灌值 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//預分頻器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的引數初始化TIMx的時間基數單位
  
	//初始化TIM5輸入捕獲引數
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	選擇輸入端 IC1對映到TI1上
  	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕獲
  	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //對映到TI1上
  	TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置輸入分頻,不分頻 
  	TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置輸入濾波器 不濾波
  	TIM_ICInit(TIM5, &TIM5_ICInitStructure);
	
	//中斷分組初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM3中斷
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先佔優先順序2級
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //從優先順序0級
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器 
	
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允許更新中斷 ,允許CC1IE捕獲中斷	
	
   	TIM_Cmd(TIM5,ENABLE ); 	//使能定時器5
   


}

u8  TIM5CH1_CAPTURE_STA=0;	//輸入捕獲狀態		    				
u16	TIM5CH1_CAPTURE_VAL;	//輸入捕獲值
 
//定時器5中斷服務程式	 
void TIM5_IRQHandler(void)
{ 

 	if((TIM5CH1_CAPTURE_STA&0X80)==0)//還未成功捕獲	
	{	  
		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
		 
		{	    
			if(TIM5CH1_CAPTURE_STA&0X40)//已經捕獲到高電平了
			{
				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高電平太長了
				{
					TIM5CH1_CAPTURE_STA|=0X80;//標記成功捕獲了一次
					TIM5CH1_CAPTURE_VAL=0XFFFF;
				}else TIM5CH1_CAPTURE_STA++;
			}	 
		}
	if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕獲1發生捕獲事件
		{	
			if(TIM5CH1_CAPTURE_STA&0X40)		//捕獲到一個下降沿 		
			{	  			
				TIM5CH1_CAPTURE_STA|=0X80;		//標記成功捕獲到一次高電平脈寬
				TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 設定為上升沿捕獲
			}else  								//還未開始,第一次捕獲上升沿
			{
				TIM5CH1_CAPTURE_STA=0;			//清空
				TIM5CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM5,0);
				TIM5CH1_CAPTURE_STA|=0X40;		//標記捕獲到了上升沿
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC1P=1 設定為下降沿捕獲
			}		    
		}			     	    					   
 	}
 
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中斷標誌位
 
}

//main函式
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"


extern u8  TIM5CH1_CAPTURE_STA;		//輸入捕獲狀態		    				
extern u16	TIM5CH1_CAPTURE_VAL;	//輸入捕獲值	
 int main(void)
 {		
 	u32 temp=0; 
	delay_init();	    	 //延時函式初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序
	uart_init(115200);	 //串列埠初始化為115200
 	LED_Init();			     //LED埠初始化
 
 	TIM3_PWM_Init(899,0); 		//不分頻。PWM頻率=72000/(899+1)=80Khz
 	TIM5_Cap_Init(0XFFFF,72-1);	//以1Mhz的頻率計數 
   	while(1)
	{
 		delay_ms(10);
		TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);

		if(TIM_GetCapture2(TIM3)==300)TIM_SetCompare2(TIM3,0);	
		 		 
 		if(TIM5CH1_CAPTURE_STA&0X80)//成功捕獲到了一次上升沿
		{
			temp=TIM5CH1_CAPTURE_STA&0X3F;
			temp*=65536;//溢位時間總和
			temp+=TIM5CH1_CAPTURE_VAL;//得到總的高電平時間
			printf("HIGH:%d us\r\n",temp);//列印總的高點平時間
			TIM5CH1_CAPTURE_STA=0;//開啟下一次捕獲
		}
	}
 }