ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

中斷是單片機系統中非常重要的組成部分,因為有了中斷,單片機就具備了快速協調多個模塊"同時"工作的能力,可以完成複雜的任務。中斷功能的存在,讓單片機處理事件時不再需要"傻傻的等待",本章內容就來揭開中斷這層神秘的面紗。

3.1中斷的產生背景

請設想這樣一個場景:此刻我正在廚房用煤氣燒一壺水,而燒開一壺水剛好需要10分鐘,我是一個主體,燒水是一個目的,而且我時時刻刻在這裡等待水燒開,因為一旦水燒開了澆滅煤氣的話,有可能引發一場災難。而這個時候,在我家裡玩耍的小夥伴要我過去陪他下圍棋,我真的想立馬奪門而出,去和小夥伴下圍棋。然而我很清楚,除非等水開了,否則我無法和我的小夥伴進入圍棋世界。

這裡面主體只有一個,而要做的有兩件事情,一件是下圍棋,一件是燒水,而圍棋和燒水是兩件獨立的客體,兩個客體是同時存在的。其中燒水需要10分鐘,但是不需要燒水的過程,只需要水開以後,提下水壺和關閉煤氣這樣的動作即可,而這個動作只需要幾秒的時間。所以我採取的辦法就是:燒水的時候,定一個鬧鐘,定時10分鐘,然後我就可以安心下圍棋了。當10分鐘時間到了,鬧鐘響了,此刻水也燒開了,我就去把煤氣關掉,然後繼續回來下圍棋。

那這些場景和單片機有什麼關係呢?

單片機的程序處理過程中也有很多類似的場景,當單片機正在專心致志的做一件事情(下圍棋)的時候,總會有一件或者多件緊迫或者不緊迫的事情發生,需要去關注,有一些需要停下手頭的工作馬上去處理(比如水開了),只有處理完了,才能回頭繼續完成剛才的工作(下圍棋)。

3.2中斷優先級的意義

假設還有一種更為複雜的場景:當我在和小夥伴下圍棋的時候,正在做作業的妹妹有一道題不會,讓我給她講題。雖然我也很喜歡下跳棋,但是給妹妹講題更重要一些,因此我決定先給妹妹講完題後再繼續回來下圍棋。講題的過程中,鬧鐘響了,水燒開了,因此我必須先去把煤氣滅掉,然後繼續回來講題,講完了題以後,再回去下圍棋。

或者還有另外一種場景:當我在和小夥伴下圍棋的時候,這個時候鬧鐘響了,水燒開了,我正走在去廚房的路上的時候,正在做作業的妹妹讓我給她講題。相比而言,講題比下圍棋更重要一些,而滅掉煤氣卻是更加重要且緊急。因此我決定先去把煤氣滅掉,然後再給妹妹講題,講完題以後再去繼續和小夥伴下圍棋。

單片機編程時,需要將單片機的所有可能處理的事件進行時間管理,優先處理重要緊急的事件。因此編程時重要且緊急的事件要放在最高優先級,不管做什麼事情,只要這個重要且緊急的事件發生,停下手頭的任何事件去處理;如果當前正在處理這個重要且緊急的事件,不管發生其他什麼事情,都不能打斷他。

單片機有的時候在處理緊迫事情的過程中,會同時出現幾件其他的事情發生,這個時候需要根據每件事情的緊迫和重要性來判斷優先處理哪件事情。因此,這種情況下就可以依託單片機強大的中斷系統,合理巧妙的利用中斷,不僅可以使其獲得處理突發狀況的能力,而且可以使單片機能夠"同時"完成多項任務。

3.3嵌入式中斷向量器NVIC

ARM Cortex-M3內核搭載了一個強大而方便的中斷系統,稱之為NVIC(嵌套向量中斷控制器)。NVIC把能夠打斷當前代碼執行流程的事件分為異常(exception)和中斷(interrupt),這裡可以全部理解成中斷,叫法不同是為了區分中斷的來源。

Cortex-M3支持256個"中斷通道"(16個異常+240箇中斷)以及對應的可編程256級中斷優先級設置。STM32雖然採用Cortex-M3內核,但是隻使用了其中的84個"中斷通道"(16個異常+68箇中斷)以及16級中斷優先級設置。表3-1分別列舉了中斷向量表的部分清單,完整的中斷向量表清單可以從《STM32中文參考手冊》9.1.2節查找到,即用即查。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

3.4單片機中斷服務函數

中斷的出現使單片機系統具備應付突發事件的處理能力。單片機系統處理"緊急事件",可理解為是一種服務,是通過執行事先編好的某個特定的程序來完成的,這種處理"緊急事件"的程序被稱為中斷服務函數或中斷響應函數。

單片機通過中斷向量表查找中斷服務函數的入口地址,用來區分不同的中斷源相對應的中斷函數。51單片機使用"inturrupt"關鍵字,通過這個關鍵字後邊的數字來映射中斷向量地址,而STM32在啟動文件中已經為用戶做好這一切,用戶要編寫中斷函數時,只需要將中斷函數名定義成和啟動文件中的名稱一致,一旦產生中斷,系統會自動調用對應的中斷函數執行。

STM32提供了stm32f10x_it.c這個文件用來統一維護和管理中斷服務函數,當然也可以寫到其他文件中,但是為了保持與官方固件規範的一致性,本書所有例程的中斷服務函數全部寫在stm32f10x_it.c這個文件內。

3.5中斷優先級處理機制

設置中斷時需要配置中斷的優先級,STM32將中斷優先級分為搶佔優先級(也叫先佔優先級)和響應優先級(亞優先級或從優先級),每個中斷源都需要設定這兩種優先級,而51單片機所設置優先級屬於搶佔優先級,響應優先級是默認的,不需要設置。

當中斷系統正在執行一箇中斷服務時,有另一個優先級更高的中斷提出中斷請求,這時會暫時中止當前正在執行的級別較低的中斷源的服務程序,去處理級別更高的中斷源,待處理完畢,再返回到被中斷了的中斷服務程序繼續執行,這個過程叫做中斷嵌套。什麼樣的中斷能嵌套,什麼樣的不能嵌套?

1、高搶佔優先級的中斷可以嵌套低搶佔優先級的中斷。

2、當兩個中斷源的搶佔優先級相同時,不管這兩個中斷源的響應優先級誰高誰低,這兩個中斷將沒有嵌套關係,這種情況下當STM32正在處理一箇中斷時,如果另一箇中斷到來,這個後到來的中斷就要等到前一箇中斷處理完之後才能被處理。

3、當兩個中斷源的搶佔優先級相同時,並且兩個中斷同時到達,則中斷控制器將根據響應優先級高低來決定先處理哪一個;如果這兩個中斷的響應優先級也相等,則根據在中斷清單表3-1中的排位順序決定先處理哪一個,序號越小,優先級越高。

Cortex-M3內核中定義了8個比特位用於設置中斷源的優先級,STM32F103只使用了其中的4個位,這4個位又被搶佔優先級和響應優先級共同使用。而搶佔優先級和響應優先級各自優先級的級數是可設的,共分為5組,如表3-2所示。無論搶佔優先級還是響應優先級遵循數值越小,優先級越高。編寫程序時,首先要預先對程序的各個功能模塊進行計算論證,開機初始化時一次性設置好優先級分組,以後就再也不動他了,避免同一個程序中使用多個優先級分組。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

根據工程開發經驗,在對響應中斷優先級沒有特殊要求的前提下,初始化時可以將優先級分組設置為NVIC_PriorityGroup_4的方式,即只設置成搶佔優先級,或者不設置優先級分組,使用系統默認優先級。當需要中斷嵌套時,通過設置搶佔優先級區分中斷高低優先級;當不需要中斷嵌套時,設置成相同的搶佔優先等級,那麼STM32將會按照表3-1中的默認排位順序決定其響應優先級的高低,確定先處理哪一個中斷。

庫函數中已經定義了NVIC相關的結構體,包括中斷通道、搶佔優先級、響應優先級、通道使能四個成員,如下所示:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

注意FunctionalState為定義在內的枚舉,常用來表達使能和非使能:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

根據NVIC結構體定義,調用庫函數中的NVIC初始化配置函數,可以實現對STM32中的中斷優先級設置以及使能,以配置定時器6中斷為例,詳細過程如下:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

STM32F103ZE的NVIC_IRQChannel描述如表3-3所示,不用記憶,即用即查,並且通過名稱可以在stm32f10x.h頭文件中找到中斷清單。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

11

3.6基本定時器

STM32F103ZE中一共有11個定時器,包括2個高級控制定時器、4個通用定時器、2個基本定時器,以及2個看門狗定時器和1個系統定時器(SysTick)。其中基本定時器、通用定時器、高級定時器類似於一種低中高搭配的組合,級別越高,功能越強大。三種類型的定時器功能對比如表3-4所示:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

3.6.1基本定時器結構

基本定時器包括TIM6和TIM7兩個定時器模塊,由計數器寄存器(TIMx_CNT)、預分頻寄存器(TIMx_PSC)、自動重裝載寄存器(TIMx_ARR)三部分構成,如圖3-1所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-1 基本定時器結構框圖

計數器寄存器(TIMx_CNT):和51單片機的TH、TL的作用一樣,用來存儲定時器的數值。和51單片機不同的是,TIM6和TIM7這兩個基本定時器,是從0開始向上計數,一直計到和自動重裝載寄存器的數值一致時,計數器自動歸零,並且產生一個計數器溢出事件。

當TIMx_CR1寄存器中的UDIS位等於'0'時,每次計數器溢出時可以產生更新事件;(通過軟件方式)手動設置TIMx_EGR寄存器的UG位也可以產生更新事件。這裡所提到的"更新事件"和51單片機教程裡講過的"標誌位"類似, 產生"更新事件"後,會觸發相應的功能。

預分頻寄存器(TIMx_PSC):STM32的定時器,除了可以每經過一個時鐘週期加1外,還可以通過設定預分頻係數,寫入到預分頻寄存器中,實現每經過'N+1'個時鐘週期加1(N為大於等於0,小於等於65535的正整數)。事實上在STM32內部,預分頻寄存器是由兩個寄存器組成,其中預分頻控制寄存器是提供給用戶寫入預分頻係數,當產生更新事件時,預分頻控制寄存器會將用戶寫入的預分頻係數寫入到預分頻緩衝器中。如圖3-2所示預分頻係數從0變到3的時序圖,更新事件之前,計數器寄存器每經過1個CK_PSC週期加1一次;而後用戶寫入預分頻值到控制寄存器中,更新事件後,控制寄存器將預分頻值寫入到預分頻緩衝器中,定時器進行了4分頻,計數器寄存器每經過4個CK_PSC週期加1一次。

但值得注意的是,由於預分頻緩衝器是不受用戶控制的,因此有的時候一些手冊資料會用預分頻寄存器這個說法直接代表預分頻控制寄存器。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-2 預分頻係數從0變到3的計數器時序圖

自動重裝載寄存器(TIMx_ARR):用來存儲用戶寫入的讓計數器寄存器達到溢出條件的值。自動重裝載寄存器也是由兩個寄存器組成,其中一個叫預裝載寄存器,一個叫自動重裝載影子寄存器。其中預裝載寄存器是提供給用戶寫入定時器溢出值,當事件更新時,預裝載寄存器就將這個值寫入到自動重裝載影子寄存器中。而計數器寄存器判斷是否溢出的閾值實際上是自動重裝載影子寄存器中的值。但是自動重裝載影子寄存器是用戶所不能操作的。

有的時候一些手冊資料會用自動重裝載寄存器這個說法直接代表預裝載寄存器。如圖3-3所示中文手冊中的時序圖,就直接寫成自動重裝載寄存器。(實際上這裡翻譯不是很準確,和英文原版資料有誤差)。

此外,TIMx_CR1寄存器中有一位ARPE位,這一位清0則用戶寫入預裝載寄存器的數據在沒有產生更新事件的情況下也會立即傳輸給自動重裝載影子寄存器中,但是這種情況下很少使用。絕大多數情況下都會將ARPE置1,即當發生更新事件時,將數據傳輸給影子寄存器,教材後邊的內容都是以ARPE置1的情況舉例。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-3 自動重裝載寄存器更新時序圖

3.6.2 定時器時鐘來源

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-4 定時器時鐘源

為定時器提供時鐘的時鐘源有三個,HSI、HSE和PLL。其中HSI是內部時鐘,由8MHz的RC振盪器產生,精度較差,對時鐘精度不敏感的情況下使用;HSE為外部時鐘源(板載8MHz晶振);PLL是將HSI或者HSE的時鐘倍頻後提供的時鐘頻率,STM32F103通過PLL最高可以提供72M的時鐘頻率,如圖3-4所示。

AHB、APB1和APB2自身就可以通過各自的預分頻器進行分頻。其中AHB域和APB2域最大時鐘頻率為72MHz,而APB1域允許的最大時鐘頻率為36MHz。如果AHB提供的時鐘源頻率為72MHz,那APB1的預分頻器最小需要配置成2分頻。

此外,特別注意的是,當APB1和APB2的預分頻不是1的時候,所有的定時器都自帶自動實現2倍頻的功能(圖3-4紅色方框所示模塊)。比如:當AHB的時鐘是36M,APB1和APB2都不分頻時頻率為36M,那麼所有的定時器的最大頻率就是36M;如果AHB的時鐘是36M,而APB1和APB2都經過2分頻為18M,那麼這個時候定時器倍頻起作用,所有的定時器的最大頻率依然是36M。

AHB、APB和定時器都具有預分頻器,並且定時器自身還具備在特定條件下2倍頻的功能,因此計算定時器的計數頻率要充分考慮所有的影響因素,定時器時鐘CK_CNT = CK_PSC / (TIM_Prescaler + 1),其中TIM_Prescaler為定時器預分頻係數,係數加1為實際定時器分頻數。

3.6.3 基本定時器配置流程

STM32的固件庫函數中沒有針對基本定時器的配置函數,基本定時器比通用定時器少一些功能,但結構是類似的,因此可以通過共用的TIM_TimeBaseInit函數進行初始化配置,只需要配置自動重裝載寄存器值和預分頻係數。

1、配置的結構體成員

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

2、 NVIC中斷優先級設置:中斷部分已經介紹過。

3、 清除中斷標誌位:由於固件庫的定時器初始化函數默認會產生更新事件並觸發更新

中斷標誌位(可以設置僅產生更新事件但不觸發更新中斷標誌位),如果此時使能中斷,程序會立即執行中斷函數。雖然一般條件下沒有什麼影響,但是在某些特定條件下會產生困擾。為了保證嚴謹性,可以使用TIM_ClearITPendingBit(TIM_TypeDef* TIMx, u16 TIM_IT) 中斷標誌位清除函數預先清除標誌位。

4、中斷初始化,使用TIM_ITConfig(TIM_TypeDef* TIMx, u16 TIM_IT, FunctionalState

NewState)函數,其中TIMx的x為1~8,表示使用哪個定時器;TIM_IT表示中斷源類型,NewState為中斷是否使能,定時器的幾種中斷源類型如表3-5所示:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

STM32的定時器有6種中斷源類型,這6箇中斷源不僅都可以觸發中斷,每種類型還可以單獨設置中斷使能。

TIM_IT_Update:表示更新事件中斷,比如計數器向上溢出/向下溢出,計數器初始化等都可以導致定時器的標誌位更新,基本定時器通常都是用這個中斷源。

TIM_IT_CC1~4:都是捕獲/比較中斷,即外部信號輸入捕獲、輸出比較,高級和通用寄存器有這個功能,後邊詳細敘述,基本定時器沒有這個功能。

TIM_IT_Trigger:表示觸發事件中斷。和51單片機的外部計數器功能類似,STM32也可以通過外部信號實現定時器的啟動、停止、初始化等,這種中斷源叫觸發中斷源。這個功能應用比較少,基本定時器也沒有,高級和通用定時器具有這個功能。

5、使能定時器,定時器初始化完畢後,使用TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)函數使能定時器,TIMx表示哪個定時器模塊,NewState的值為ENABLE和DISABLE,分別表示使能和失能。定時器一旦使能,便開始計數。

注意:本書所有中斷服務函數都放在stm32f10x_it.c文件中,該文件是專門用來存放中斷服務函數的。stm32f10x_it.c文件中默認有9個與Cortex-M3內核相關的異常處理程序,使用時切記不要刪除,只需要在最後添加自己編寫的中斷服務函數即可。關於stm32f10x_it.c文件,僅張貼所使用的程序代碼。

配置TIM6定時1s實現LED間隔1s亮滅,具體代碼如下:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

3.7 數碼管

ARM嵌入式編程與實戰應用(STM32F1系列)(更新:2018-4-16) http://www.qdkingst.com

數碼管是一種廣泛應用的顯示器件,由8個LED小燈組成,分為共陰和共陽兩種類型,Kingst-32F1採用的是2位一體的共陽數碼管,為了方便對數碼管寫入數據,硬件設計採用連續的8個IO口作為數碼管的段選,如圖3-5和表3-6所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

通過原理圖可以計算出數碼管的真值表,如表3-7所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

3.8 蜂鳴器驅動電路

蜂鳴器按照驅動方式分為有源蜂鳴器和無源蜂鳴器。Kingst-32F1開發板使用的是有源蜂鳴器。蜂鳴器額定電流大概在30mA,而STM32的IO口最大輸出電流僅為25mA,因此採用三極管搭建驅動電路,如圖3-6所示,蜂鳴器驅動引腳BUZZ連接Kingst-32F1開發板PG9引腳。蜂鳴器的配置過程與LED類似,配置引腳為推輓輸出,控制引腳輸出高低電平來開關蜂鳴器,如表3-7所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-6 蜂鳴器電路

3.9按鍵檢測

Kingst-32F1開發板集成有4個獨立按鍵,其中Key1~Key3為普通獨立按鍵,接了上拉電阻,而key4除了要做獨立按鍵外,還要擔負STM32單片機的外部喚醒功能。由於喚醒功能需要上升沿觸發,因此key4接了一個下拉電阻,如圖3-7所示,初始化按鍵引腳時,相應的GPIO引腳配置成浮空輸入,按鍵對應引腳如表3-8所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

通常按鍵所用的開關都是機械彈性開關,當機械觸點斷開、閉合時,由於機械觸點的彈性作用,一個按鍵開關在閉合時不會馬上就穩定的接通,在斷開時也不會一下子徹底斷開,而是在閉合和斷開的瞬間伴隨了一連串的抖動,如圖3-8所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-8 按鍵抖動狀態圖

按鍵穩定閉合時間長短是由操作人員決定的,通常都會在100ms以上,刻意快速按的話能達到40-50ms左右,很難再低了。抖動時間是由按鍵的機械特性決定的,一般都會在10ms以內,為了確保程序對按鍵的一次閉合或者一次斷開只響應一次,必須進行按鍵的消抖處理。當檢測到按鍵狀態變化時,不是立即去響應動作,而是先等待閉合或斷開穩定後再進行處理。

《手把手教你學51單片機-C語言》一書中已經強調過不能使用delay延時進行消抖,尤其對於STM32這種處理性能更優,功能更強的單片機來講,delay消抖的時間可能會耽誤很多事情,因此依然採用連續採集多次按鍵狀態,通過判斷多次狀態是否一致來確定按鍵最終狀態,如圖3-9所示。

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

圖3-9 按鍵連續掃描判斷

最左側為0時刻,從左到右,每經過2ms讀取一次按鍵的狀態,將讀取到的按鍵狀態進行統計,判斷連續的8次按鍵的狀態是否一致,如果連續8次全1,則是按鍵彈起,如果連續8次中有1有0,則是抖動,如果連續8次全0,則是按鍵按下狀態。值得注意的是,和移位寄存器類似,每經過2ms讀到一次右側新數據,則摒棄一次最左邊的舊數據,因此是每2ms就可以判斷一次按鍵狀態,而不是每16ms。

利用這種方法,就可以避免通過延時消抖佔用單片機執行時間,而是轉化成了一種按鍵狀態判定而非按鍵過程判定,只對當前按鍵的連續16ms的8次狀態進行判斷,而不再關心它在這16ms內都做了什麼事情,不耽誤單片機做其他的工作。

下面按照這種思路來做一個實現按鍵判斷和消抖、數碼管計數並且讓蜂鳴器發聲的實驗。當按下按鍵1時,蜂鳴器發聲100ms,同時數值加1;當按下按鍵2時,蜂鳴器發聲長鳴,同時數值減1,按下按鍵3清空計數,蜂鳴器停止發聲。

驅動代碼如下:

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器

ARM嵌入式編程與實戰應用(STM32F1系列)第3章 中斷與基本定時器


分享到:


相關文章: