1.輸入捕獲介紹
? 在定時器中斷實驗章節中我們介紹了通用定時器具有多種功能,輸入捕獲就是其中一種。STM32F1除了基本定時器TIM6和TIM7,其他定時器都具有輸入捕獲功能。輸入捕獲可以對輸入的信號的上升沿,下降沿或者雙邊沿進行捕獲,通常用于測量輸入信號的脈寬、測量 PWM 輸入信號的頻率及占空比。
? 輸入捕獲的工作原理比較簡單,在輸入捕獲模式下,當相應的 ICx 信號檢測到跳變沿后,將使用捕獲/比較寄存器(TIMx_CCRx)來鎖存計數器的值。簡單的說就是通過檢測TIMx_CHx上的邊沿信號,在邊沿信號發生跳變(比如上升沿/下降沿)的時候,將當前定時器的值(TIMx_CNT)存放到對應的通道的捕獲/比較寄存(TIMx_CCRx)里面,完成一次捕獲。同時還可以配置捕獲時是否觸發中斷/DMA 等。下面我們以輸入捕獲測量脈寬為例,通過一個簡圖來介紹輸入捕獲的原理。?
? CNT計數的次數等于:有了這個計數次數,再乘以 CNT 的計數周期,即可得到 t2-t1 的時間長度,即高電平持續時間。
上圖以及公式N*ARR+CCRx2,簡單解釋如下:
首先,定時器在以一個固定次數不斷重載并且計數,比如,重載值為1000,那么它就一直不斷地從0計數到1000,然后再次從0計數到1000。與此同時,輸入引腳在監測上升沿跳變,假定某一次計數到500的時候,監測到了一個上升沿,那么,這個時候就產生一個內部觸發,定時器就立即重載,于是定時器馬上從0開始再次計數,(所以,上圖中T1的位置其實是錯誤的!)同時監測改為下降沿觸發,開始監測下降沿!定時器從0開始計數后,有可能1000以前,比如,計到800,就監測到了下降沿,那么很簡單,高電平的持續時間,就是800*一個計數的計數周期。如果計到1000后,仍然沒有監測到下降沿,那么就從0開始計數,一直這樣循環,直到發現下降沿為止。
2.輸入捕獲配置步驟
? 接下來我們介紹下如何使用庫函數對通用定時器的輸入捕獲進行配置。這個也是在編寫程序中必須要了解的。其實輸入捕獲和前面定時器中斷一樣也是通用定時器的一個功能,因此還是要用到定時器的相關配置函數,具體步驟如下:(定時器相關庫函數在stm32f10x_tim.c和
stm32f10x_tim.h文件中)。
(1)使能定時器及端口時鐘,并設置引腳復用器映射和引腳模式等
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
(2)初始化定時器參數,包含自動重裝值,分頻系數,計數方式等
voidTIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef*
TIM_TimeBaseInitStruct);
(3)設置通用定時器的輸入捕獲參數,開啟輸入捕獲功能??????????????????????????????
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef*
TIM_ICInitStruct);
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_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
如果我們需要配置TIM5的通道1為輸入捕獲功能,并且為上升沿捕獲、
不分頻、直接映射到TI,可以如下配置:
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1; //通道1
TIM_ICInitStructure.TIM_ICFilter=0x00;? //濾波
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//捕獲極性
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //分頻系數
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//直接映射到TI1
TIM_ICInit(TIM5,&TIM_ICInitStructure);
(4)開啟捕獲和定時器溢出(更新)中斷???????????????????????????????
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
(5)設置定時器中斷優先級,使能定時器中斷通道
NVIC初始化庫函數是NVIC_Init()
(6)編寫定時器中斷服務函數
TIM5_IRQHandler
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
TIM_SetCounter(TIM5,0); //定時器初值為0
(7)使能定時器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
3.硬件電路
? 本實驗使用到硬件資源如下:
(1)D1指示燈
(2)K_UP按鍵
(3)串口1
(4)TIM5的通道1
D1指示燈用來提示系統正常運行,K_UP按鍵是接在PA0管腳上的,所以可以通過此按鍵輸入一個高電平,通過串口1的printf函數打印捕獲到的高電平時間。
4.編寫輸入捕獲控制程序
? 本實驗所要實現的功能是:按下key_up鍵,產生一個高電平脈沖,使用TIM5的CH1檢測輸入信號高電平脈寬,將檢測的高電平脈寬時間通過printf函數打印出來,同時讓D1指示燈不斷閃爍表示系統正常運行。程序框架如下:
(1)初始化PA0管腳為TIM5通道1輸入捕獲功能,開啟捕獲和溢出中斷等
(2)編寫TIM5中斷服務函數
(3)編寫主函數
main.c:
#include "system.h"
#include "led.h"
#include "SysTick.h"
#include "usart.h"
#include "input.h"int main()
{u32 indata=0;u8 i=0;SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中斷優先級分組LED_Init();USART1_Init(9600);TIM5_CH1_Input_Init(0xffff,71);//自動重載值設為最大,且計數頻率為1M,每次計數為1us,這樣計數的總數就是高電平的持續時間while(1){if(TIM5_CH1_CAPTURE_STAUS&0x80){indata=TIM5_CH1_CAPTURE_STAUS&0x3f;indata*=0xffff;indata+=TIM5_CH1_CAPTURE_VALUE;printf("高電平持續時間:%d us\r\n",indata);TIM5_CH1_CAPTURE_STAUS=0;//清空以便再次捕獲}i++;if(i%20 ==0){led1=!led1;//LED1閃,用來指示主程序循環是否運行}delay_ms(50);}
}
???????????????
input.c
#include "input.h"
#include "SysTick.h"u8 TIM5_CH1_CAPTURE_STAUS;//這個標志不僅用來判斷是否產生了中斷,同時也用來記錄溢出中斷的次數
u16 TIM5_CH1_CAPTURE_VALUE;//保存捕獲到下降沿中斷時,計數器的數值void TIM5_CH1_Input_Init(u16 period,u16 prescaler)
{GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//結構體變量聲明TIM_ICInitTypeDef TIM_ICInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5時鐘GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;//GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //GPIO_Init(GPIOA,&GPIO_InitStructure);TIM_TimeBaseInitStructure.TIM_Period=period; //裝入函數傳過來的自動裝載值TIM_TimeBaseInitStructure.TIM_Prescaler=prescaler; //裝入函數傳過來的分頻系數TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//1分頻(沒有分頻)TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //設置向上計數模式:從0開始計數到自動重載值后溢出產生中斷TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5各參數:自動重裝值、分頻系統、計數方式等TIM_ICInitStructure.TIM_Channel=TIM_Channel_1; //通道1TIM_ICInitStructure.TIM_ICFilter=0x00; //濾波TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//捕獲極性設為上升沿TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //分頻系數TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//直接映射到TI1TIM_ICInit(TIM5,&TIM_ICInitStructure);TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//開啟捕獲和定時器溢出(更新)中斷//設置定時器中斷優先級,使能定時器中斷通道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_Cmd(TIM5,ENABLE );//使能定時器}void TIM5_IRQHandler(void)
{if((TIM5_CH1_CAPTURE_STAUS&0x80)==0)//在進入打印輸出時,不檢測、不捕獲{if(TIM_GetITStatus(TIM5,TIM_IT_Update))//判斷是否產生溢出中斷 {if(TIM5_CH1_CAPTURE_STAUS&0x40){if((TIM5_CH1_CAPTURE_STAUS&0x3f)==0x3f)//處理高電平時間過長的情況{TIM5_CH1_CAPTURE_STAUS |= 0x80;TIM5_CH1_CAPTURE_VALUE=0xffff;} else{TIM5_CH1_CAPTURE_STAUS++;//保存定時溢出中斷的次數}}}if(TIM_GetITStatus(TIM5, TIM_IT_CC1))//判斷是否產生上升捕獲中斷{if(TIM5_CH1_CAPTURE_STAUS&0x40) //0x40 = 0100 0000,判斷是否捕獲到下降沿{TIM5_CH1_CAPTURE_STAUS |= 0x80;TIM5_CH1_CAPTURE_VALUE=TIM_GetCapture1(TIM5);TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//重新改為上降沿觸發}else //產生上升捕獲后先進else,把定時器重載,以從0開始重新計數{TIM5_CH1_CAPTURE_STAUS =0;TIM5_CH1_CAPTURE_VALUE =0;TIM5_CH1_CAPTURE_STAUS|= 0x40;//這就是為在第二次捕獲(即捕獲到下降沿)時能進入上面的if語句里TIM_Cmd(TIM5,DISABLE );TIM_SetCounter(TIM5,0); //設定定時器初值為0以重新計數TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//改為下降沿觸發TIM_Cmd(TIM5,ENABLE );//使能定時器}}}TIM_ClearITPendingBit(TIM5, TIM_IT_Update|TIM_IT_CC1);//清除溢出標志}
input.h
#ifndef _input_H
#define _input_H#include "system.h"
extern u8 TIM5_CH1_CAPTURE_STAUS;
extern u16 TIM5_CH1_CAPTURE_VALUE;void TIM5_CH1_Input_Init(u16 period,u16 prescaler);
void TIM5_IRQHandler(void);#endif
程序燒寫到開發板運行,實驗結果如下:實驗是成功的!
? ? ? ? ? ??