STM32 通用定時器作為輸入捕獲 學習筆記
通用定時器作為輸入捕獲的使用。我們將用TIM5的通道1(PA0)來做輸入捕獲,捕獲PA0上高電平的脈寬(用 WK_UP 按鍵輸入高電平),通過串列埠列印高電平脈寬時間。
輸入捕獲簡介:
輸入捕獲模式可以用來測量脈衝寬度或者測量頻率。STM32的定時器,除了TIM6和TIM7,其他定時器都有輸入捕獲功能。
STM32 的輸入捕獲,簡單的說就是通過檢測 TIMx_CHx 上的邊沿訊號,在邊沿訊號發生跳變(比如上升沿/下降沿)的時候,
將當前定時器的值(TIMx_CNT)存放到對應的通道的捕獲/比較暫存器(TIMx_CCRx)裡面,完成一次捕獲。同時還可以配置捕獲時是否觸發中斷/DMA 等。
我們用到TIM5_CH1來捕獲高電平脈寬,也就是要先設定輸入捕獲為上升沿檢測,記錄發生上升沿的時候 TIM5_CNT 的值。
然後配置捕獲訊號為下降沿捕獲,當下降沿到來時,發生捕獲,並記錄此時的 TIM5_CNT 值。這樣,前後兩次 TIM5_CNT 之差,就是高電平的脈寬,同時 TIM5 的計數頻率我們是知道的,從而可以計算出高電平脈寬的準確時間。
輸入捕獲的配置步驟:
1)開啟 TIM5 時鐘和 GPIOA 時鐘,配置 PA0 為下拉輸入。
要使用 TIM5,我們必須先開啟 TIM5 的時鐘。這裡我們還要配置 PA0 為下拉輸入,因為我們要捕獲 TIM5_CH1 上面的高電平脈寬,而 TIM5_CH1 是連線在 PA0 上面的。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能 TIM5 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 時鐘
2)初始化 TIM5,設定 TIM5 的 ARR 和 PSC。
在開啟了 TIM5 的時鐘之後,我們要設定 ARR 和 PSC 兩個暫存器的值來設定輸入捕獲的自動重灌載值和與分頻數。 這在庫函式中是通過 TIM_TimeBaseInit 函式實現的
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
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);
3)設定 TIM5 的輸入比較引數,開啟輸入捕獲
輸入比較引數的設定包括對映關係,濾波,分頻以及捕獲方式等。這裡我們需要設定通道 1為輸入模式,且IC1對映到 TI1(通道 1)上面,並且不使用濾波(提高響應速度)器,上升沿捕獲。
庫函式是通過 TIM_ICInit 函式來初始化輸入比較引數的:
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
同樣,我們來看看引數設定結構體 TIM_ICInitTypeDef 的定義:
typedef struct
{
uint16_t TIM_Channel; //設定通道
uint16_t TIM_ICPolarity; //設 置 輸 入 信 號 的 有效 捕獲 極性
uint16_t TIM_ICSelection; //設定對映關係
uint16_t TIM_ICPrescaler; //設定 輸入捕獲分頻係數
uint16_t TIM_ICFilter; //設定濾波器長度
} TIM_ICInitTypeDef;
配置程式碼是:
TIM_ICInitTypeDef TIM5_ICInitStructure;
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //選擇輸入端 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);
4)使能捕獲和更新中斷(設定 TIM5 的 DIER 暫存器)
因為我們要捕獲的是高電平訊號的脈寬,所以,第一次捕獲是上升沿,第二次捕獲時下降沿,必須在捕獲上升沿之後,設定捕獲邊沿為下降沿,
同時,如果脈寬比較長,那麼定時器就會溢位,對溢位必須做處理,否則結果就不準了。這兩件事,我們都在中斷裡面做,所以必須開啟捕獲中斷和更新中斷。
這裡我們使用定時器的開中斷函式 TIM_ITConfig 即可使能捕獲和更新中斷:
TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允許更新中斷和捕獲中斷
5)設定中斷分組,編寫中斷服務函式
設定中斷分組主要是通過函式 NVIC_Init()來完成。分組完成後,我們還需要在中斷函式裡面完成資料處理和捕獲設定等關鍵操作,從而實現高電平脈寬統計。
在中斷服務函式裡面,跟以前的外部中斷和定時器中斷實驗中一樣,我們在中斷開始的時候要進行中斷型別判斷,在中斷結束的時候要清除中斷標誌位。
使用到的函式分別為 TIM_GetITStatus()函式和 TIM_ClearITPendingBit()函式。
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET){}//判斷是否為更新中斷
if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET){}//判斷是否發生捕獲事件
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);//清除中斷和捕獲標誌位
6)使能定時器(設定 TIM5的CR1暫存器)
最後,必須開啟定時器的計數器開關,啟動 TIM5 的計數器,開始輸入捕獲。
TIM_Cmd(TIM5,ENABLE ); //使能定時器 5
貼下正點原子的例程:
#include "timer.h"
#include "led.h"
#include "usart.h"
void TIM5_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM5_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM5_ICInitStructure.TIM_ICFilter = 0;
TIM_ICInit(TIM5,&TIM5_ICInitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5,ENABLE);
}
u8 TIM5CH1_CAPTURE_STA=0; //輸入捕獲狀態
u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
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)
{
if(TIM5CH1_CAPTURE_STA & 0x40)
{
TIM5CH1_CAPTURE_STA|=0X80; //標記成功捕獲到一次上升沿
TIM5CH1_CAPTURE_VAL = TIM_GetCounter(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);
}
extern u8 TIM5CH1_CAPTURE_STA; //輸入捕獲狀態
extern u16 TIM5CH1_CAPTURE_VAL; //輸入捕獲值
int main(void)
{
u32 temp=0;
delay_init(); //延時函式初始化
NVIC_Configuration(); //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序
uart_init(9600); //串列埠初始化為9600
LED_Init(); //LED埠初始化
TIM5_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計數
while(1)
{
delay_ms(10);
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;//開啟下一次捕獲
}
}
}