【STM32】通用定時器的輸入捕獲(例項:輸入捕獲)
《STM32中文參考手冊V10》-第14章 通用定時器
通用定時器輸入捕獲概述
輸入捕獲的工作原理
在通用定時器框圖中,主要涉及到最頂上的一部分(計數時鐘的選擇)、中間部分(時基單元)、左下部分(輸入捕獲
輸入捕獲模式可以用來測量脈衝寬度或者測量頻率。STM32的定時器,除了TIM6、TIM7,其他的定時器都有輸入捕獲的功能。下面以一個簡單的脈衝輸入為例,簡單地講述一下輸入捕獲用於測量脈衝寬度的工作原理:
先設定輸入捕獲為上升沿檢測,記錄發生上升沿時TIMx_CNT的值。然後配置捕獲訊號為下降沿捕獲,當下降沿到來的時候發生捕獲,並記錄此時的TIMx_CNT的值。這樣,前後兩次TIMx_CNT的值之差就是高電平的脈寬。同時根據TIM的計數頻率,我們就能知道高電平脈寬的準確時間。
輸入捕獲的通道概覽
每一個捕獲/比較通道都是圍繞著一個捕獲/比較暫存器(包含影子暫存器),包括捕獲的輸入部分(數字濾波、多路複用和預分頻器),和輸出部分(比較器和輸出控制)。
捕獲/比較模組由一個預裝載暫存器和一個影子暫存器組成。讀寫過程僅操作預裝載暫存器。
- 在捕獲模式下,捕獲發生在影子暫存器上,然後再複製到預裝載暫存器中。
- 在比較模式下,預裝載暫存器的內容被複制到影子暫存器中,然後影子暫存器的內容和計數器進行比較。
輸入部分對相應的TIx輸入訊號取樣,併產生一個濾波後的訊號TIxF。然後,一個帶極性選擇的邊緣檢測器產生一個訊號(TIxFPx),它可以作為從模式控制器的輸入觸發或者作為捕獲控制。該訊號通過預分頻進入捕獲暫存器(ICxPS)。
一句話總結工作過程:通過檢測TIMx_CHx通道上的邊沿訊號,在邊沿訊號發生跳變(比如上升沿/下降沿)的時候,將當前定時器的值(TIMx_CNT)存放到對應的捕獲/比較暫存器(TIMx_CCRx)裡面,完成一次捕獲。同時,還可以配置捕獲時是否觸發中斷/DMA等。
輸入捕獲的工作過程
將輸入捕獲的通道圖進行分解,分解成四個部分,下面對這四個部分進行分析來了解輸入捕獲的工作過程:
設定輸入捕獲濾波器
輸入捕獲濾波器IC1F[3:0],這個用於設定取樣頻率和數字濾波器長度。其中:fCK_INT是定時器的輸入頻率,fDTS是根據TIMx_CR1的CKD[1:0]的設定來確定的。
這裡濾波器的作用是什麼意思呢?數字濾波器由一個事件計數器組成,它記錄到N個事件後會產生一個輸出的跳變。也就是說連續N次取樣,如果都是高電平,則說明這是一個有效的觸發,就會進入輸入捕捉中斷(如果設定了的話)。這樣就可以濾除那些高電平脈寬低於8個取樣週期的脈衝訊號,從而達到濾波的作用。
設定輸入捕捉極性
這裡是設定捕捉事件是發生在上升沿還是下降沿。
設定輸入捕獲對映關係
由於我們只顯示了一路通道的通道圖,如果在幾條通路的情況下:
在TIMx_CH1和TIMx_CH2兩條通道的情況下,我們可以看出除了TIMx_CH1捕捉到的訊號可以連線到IC1,TIMx_CH2捕捉到的訊號可以連線到IC2之外,TIMx_CH1捕捉到的訊號也可以連線到IC2,TIMx_CH2捕捉到的訊號也可以連線到IC1。
一般情況下,我們設定成TIMx_CH1捕捉到的訊號可以連線到IC1,TIMx_CH2捕捉到的訊號可以連線到IC2。
設定輸入捕獲分頻器
這裡設定的是每N個事件觸發一次捕捉。也就是說,我們可以設定成,每2次上升沿事件觸發一次捕捉。
輸入捕獲相關配置暫存器
捕獲/比較模式暫存器1(TIMx_CCMR1)
作用:在輸入捕獲模式下,確定數字濾波器、通道對映、預分頻係數。
捕獲/比較使能暫存器(TIMx_CCER)
作用:在輸入捕獲模式下,確定捕捉極性和捕捉使能。
捕獲/比較暫存器1(TIMx_CCR1)
作用:在輸入捕獲模式下,確定上一次輸入捕捉事件傳輸的計數值。
輸入捕獲相關配置庫函式
- 1個輸入初始化函式
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
作用:初始化捕獲通道、濾波器、捕獲極性、對映關係、分頻係數等引數。
注意:由於輸出初始化函式將所有的4個通道的函式分開各自定義了一個函式,而輸入初始化函式並沒有這麼做。所以在輸入初始化函式中,需要指定捕獲通道。
- 1個引數獲取函式
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
作用:在四個通道中選擇一個,確定上一次輸入捕捉事件傳輸的計數值。
- 1個引數設定函式
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
作用:在四個通道中選擇一個,設定通道極性。通常在初始化函式中已經設定了通道極性,此函式用於除初始化之外的修改。
輸入捕獲的一般步驟
例項要求:使用TIM5的通道1(PA0)來作為輸入捕獲,捕獲PA0上高電平的脈寬(用WK_UP按鍵輸入高電平),通過串列埠列印高電平脈衝時間。
- 初始化定時器和通道對應IO的時鐘;
- 初始化IO口,模式為輸入。呼叫函式:GPIO_Init();
- 初始化定時器ARR,PSC。呼叫函式:TIM_TimeBaseInit();
- 初始化輸入捕獲通道。呼叫函式:TIM_ICInit();
- 如果要開啟捕獲中斷。呼叫函式:TIM_ITConfig();NVIC_Init();
- 使能定時器。呼叫函式:TIM_Cmd();
- 編寫中斷服務函式。呼叫函式:TIMx_IRQHandler()。
下面按照這個一般步驟來進行一個簡單的輸入捕獲程式:
//定時器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); //清除中斷標誌位
}
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
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;//開啟下一次捕獲
}
}
}
程式碼邏輯
這裡關於輸入捕獲的初始化部分比較簡單,對照著一般步驟來就行了。但是在中斷處理函式TIM5_IRQHandler()部分就有所難度了,為什麼會比較複雜呢?
由於我們進行輸入捕獲,一旦捕捉到了上升沿,就設定計數器當前值為0,讓它從0開始重新計數:
TIM_SetCounter(TIM5,0);
但是如果脈衝的長度過於寬了,也就是說,從0開始計數到自動重載入值一個迴圈結束了,脈衝還是沒有結束。這個情況下,顯而易見不能只記錄一下最後的計數器當前值。
解決這個問題的辦法:
設定一個變數TIM5CH1_CAPTURE_STA,bit5-0為捕捉高電平後定時器溢位的次數,bit6為捕捉到高電平標誌,bit7為捕獲完場標誌。
同時設定兩個中斷(更新中斷和捕獲中斷):
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允許更新中斷 ,允許CC1IE捕獲中斷
在中斷處理函式中,先判斷是否捕獲成功,如果捕獲成功了,說明是在脈衝低電平的階段,什麼都不需要做;如果捕獲沒有成功,說明是在脈衝高電平的階段,就需要繼續判斷中斷型別,然後再分別進行處理。在更新中斷中,表示此時脈衝長度過長,TIM5CH1_CAPTURE_STA加1。在捕獲中斷中,判斷捕捉到的是否為上升沿,如果是,計數器當前值清零,TIM5CH1_CAPTURE_STA清零,同時標記標誌,設定極性下降沿捕捉;如果不是,標記捕獲完成,儲存當前計數器的值,設定極性上升沿捕獲。
extern關鍵字
C語言中,extern可以置於變數或者函式前,以表示變數或者函式的定義在別的檔案中,提示編譯器遇到此類變數和函式時在其他模組中尋找其定義。
注意:對於extern申明變數可以多次,但是定義只有一次。
相關推薦
【STM32】通用定時器的輸入捕獲(例項:輸入捕獲)
STM32F1xx官方資料:《STM32中文參考手冊V10》-第14章 通用定時器通用定時器輸入捕獲概述輸入捕獲的工作原理在通用定時器框圖中,主要涉及到最頂上的一部分(計數時鐘的選擇)、中間部分(時基單元)、左下部分(輸入捕獲輸入捕獲模式可以用來測量脈衝寬度或者測量頻率。S
【Qt5】QTimer定時器
//標頭檔案 #include <QTimer> //程式碼 funtion()為需要呼叫的槽函式 QTimer *timer = new QTimer(this); connect(
【JavaScript】使用定時器實現Js的延期執行或重複執行setTimeout,setInterval
1.window.setTimeout方法該方法可以延時執行一個函式,例如: <script language="JavaScript" type="text/javascript"> <!-- function hello(){ alert("hello"); } window.setTi
【達內課程】音樂播放器4.0(播放詳情頁下)
效果圖 要更新進度條,需要PlayMusicService中傳送廣播,每秒傳送廣播,廣播中攜帶播放進度資訊 因此在PlayMusicService的onCreate方法中開啟一個執行緒,寫在onCreate中保證只起一條執行緒 private boolean isLoop =
【 Verilog 】暫存器資料型別(reg)與線網資料型別(wire,tri)
暫存器資料型別 Verilog中規定,凡是在程式塊中被賦值的變數,都必須是暫存器型別的。(程式塊:例如always塊) 這裡未免還是會讓人產生疑惑?暫存器資料型別的變數最後一定會被綜合成暫存器嗎?
【STM32】串列埠通訊基本原理(超基礎、詳細版)
STM32F1xx官方資料:《STM32中文參考手冊V10》-第25章通用同步非同步收發器(USART)通訊介面背景知識裝置之間通訊的方式一般情況下,裝置之間的通訊方式可以分成並行通訊和序列通訊兩種。它們的區別是:並、序列通訊的區別並行通訊序列通訊傳輸原理資料各個位同時傳輸資
【HDOJ6118】度度熊的交易計劃(最小費用流)
const 費用流 sign else read true head 最大的 自動調整 題意: 度度熊參與了喵哈哈村的商業大會,但是這次商業大會遇到了一個難題:喵哈哈村以及周圍的村莊可以看做是一共由n個片區,m條公路組成的地區。由於生產能力的區別,第i個片區能夠花費a[i]
【BZOJ3769】spoj 8549 BST again DP(記憶化搜索?)
ret lin 多少 sam 16px char long long cst ini 【BZOJ3769】spoj 8549 BST again Description 求有多少棵大小為n的深度為h的二叉樹。(樹根深度為0;左右子樹有別;答案對1000000007取
【java】java處理隨機浮點數(小數點後兩位)用RMB的大寫數值規則輸出
pen junit toc get code package 部分 amp print 晚上上床前,拿到這個有意思的問題,就想玩弄一番: ====================================================================
【BZOJ4755】扭動的回文串(Manacher,哈希)
ring problem def www. 二分 cpp div char class 【BZOJ4755】扭動的回文串(Manacher,哈希) 題面 BZOJ 題解 不要真的以為看見了回文串就是\(PAM,Manacher\)一類就可以過。 這題顯然不行啊。 我們主要考
【Java】itext根據模板生成pdf(包括圖片和表格)
金額 res report als fields positions 創建模板 bst open() 1、導入需要的jar包:itext-asian-5.2.0.jar itextpdf-5.5.11.jar。 2、新建word文檔,創建模板,將文件另存為pdf,並用Ado
【POJ】2480 Longge's problem(歐拉函數)
sin bit flag += continue its 就是 題意 ace 題目 傳送門:QWQ 分析 題意就是求∑gcd(i, N) 1<=i <=N.。 顯然$ gcd(i,n) = x $時,必然$x|n$。 所以我們枚
【題解】洛谷P3959 [NOIP2017TG] 寶藏(狀壓DP+DFS)
洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017時還很弱(現在也很弱 看出來是DP 但是並不會狀壓DP 現在看來思路並不複雜 只是存狀態有點難想到 思路 因為n最大為12 所以可以想到是狀壓
【ZCMU1437】A Bug's Life(種類並查集)
題目連結 1437: A Bug's Life Time Limit: 1 Sec Memory Limit: 128 MB Submit: 113 Solved: 50 [Submit][Status][Web Board] Descript
還在人工爬資料?不用定期敲爬蟲,也能【自動化】訊息爬取的祕訣(內附Python程式碼)
RSS服務Python實做一、 安裝我們可以透過Python的套件包:「feedparser 」 。讓我們可以輕易的透過Python解析 RSS。Windows 安裝,開啟Command Line:pip install feedparserUbuntu安裝,開啟Terminal:sudo pip insta
【爬蟲】初學爬蟲,瞭解始末(概念類—更新中)
因為對堆糖的圖片心心念念,但是目前從網上翻出來的爬蟲程式碼沒有能夠做到將大圖儲存下來的,所以決定自學爬蟲,直到完成堆糖的大圖片的下載(*^▽^*) 網路爬蟲(又被稱為網頁蜘蛛,網路機器人,在FOAF社群中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取全球資訊
【DP】ssl 1010 方格取數(多執行緒DP)
Description 設有N*N的方格圖(N<=10,我們將其中的某些方格中填入正整數,而其他的方格中則放入數字0。如下圖所示(見樣例): 某人從圖的左上角的A 點出發,可以向下行走,也可以向右走,直到到達右下角的B點。在走過的路上,他可以取走方格中的數(取走後的方格中將變
【DSP】DSP5509A的FFT演算法實現(附:完整程式碼及疑點解惑)
傅立葉變換及FFT原理 說起傅立葉變換,每個人第一反應都是從時域轉換到頻域的手段,如下圖所示: 但除了這一點之外呢?原理呢,推導呢?大概都是一頭霧水…… 而FFT並不是一種新的變換,它是離散傅立葉變換(DFT)的一種快速演算法。 DFT的演算法速度: 由於我們在計算DF
【深入淺出】| 基於深度學習的機器翻譯(附PDF+視訊下載)
由公眾號"機器學習演算法與Python學習"整理源|將門創投本文所分享的是清華大學劉洋副教授講解
【論文】ICCV 2017,2015 best paper(附論文下載地址)
一、2017 Awards Honorable mentions Nicholas Rhinehart, Kris M. Kitani First Person Activity Forecasti