怎樣做已有網(wǎng)站的編輯維護四川聚順成網(wǎng)絡(luò)科技有限公司
本內(nèi)容基于江協(xié)科技STM32視頻內(nèi)容,整理而得。
文章目錄
- 1. TIM
- 1.1 TIM定時器
- 1.2 定時器類型
- 1.3 基本定時器
- 1.4 通用定時器
- 1.4 高級定時器
- 1.5 定時中斷基本結(jié)構(gòu)
- 1.6 預(yù)分頻器時序
- 1.7 計數(shù)器時序
- 1.8 計數(shù)器無預(yù)裝時序
- 1.9 計數(shù)器有預(yù)裝時序
- 1.10 RCC時鐘樹
- 2. TIM庫函數(shù)及代碼
- 2.1 TIM庫函數(shù)
- 2.2 6-1定時器中斷代碼
- 2.2.1 硬件電路:
- 2.2.2 代碼流程
- 2.2.3 代碼
- 2.3 6-2定時器外部時鐘代碼
- 2.3.1 硬件電路
- 2.3.2 代碼流程
- 2.3.3 代碼
1. TIM
1.1 TIM定時器
- 定時器可以對輸入的時鐘進行計數(shù),并在計數(shù)值達到設(shè)定值時觸發(fā)中斷
- 16位計數(shù)器、預(yù)分頻器、自動重裝寄存器的時基單元,在72MHz計數(shù)時鐘下可以實現(xiàn)最大59.65s的定時。1/(72/65536/65536)(計數(shù)器就是用來執(zhí)行計數(shù)定時的一個寄存器,每來一個時鐘,計數(shù)器加1。預(yù)分頻器可以對計數(shù)器的時鐘進行分頻,讓計數(shù)更加靈活。自動重裝寄存器就是計數(shù)的目標(biāo)值,就是想要計多少個時鐘申請中斷。這些寄存器構(gòu)成了定時器最核心的部分,把這一塊電路稱為時基單元)
- 不僅具備基本的定時中斷功能,而且還包含內(nèi)外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發(fā)模式等多種功能。
- 根據(jù)復(fù)雜度和應(yīng)用場景分為高級定時器、通用定時器、基本定時器三種類型。
1.2 定時器類型
類型 | 編號 | 總線 | 功能 |
---|---|---|---|
高級定時器 | TIM1、TIM8 | APB2 | 擁有通用定時器全部功能,并額外具有重復(fù)計數(shù)器、死區(qū)生成、互補輸出、剎車輸入等功能 |
通用定時器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 擁有基本定時器全部功能,并額外具有內(nèi)外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發(fā)模式等功能 |
基本定時器 | TIM6、TIM7 | APB1 | 擁有定時中斷、主模式觸發(fā)DAC的功能 |
- STM32F103C8T6定時器資源:TIM1、TIM2、TIM3、TIM4
- DAC:數(shù)模轉(zhuǎn)換器
1.3 基本定時器
主要功能:
- 16位自動重裝載累加計數(shù)器
- 16位可編程預(yù)分頻器,用于對輸入的時鐘按系數(shù)為1~65536之間的任意數(shù)值分頻
- 觸發(fā)DAC的同步電路
- 在更新事件(計數(shù)器溢出)時產(chǎn)生中斷/DMA請求
-
預(yù)分頻器PSC
預(yù)分頻器之前連接的就是基準(zhǔn)計數(shù)時鐘的輸入。由于基本定時器只能選擇內(nèi)部時鐘,因此可以認(rèn)為預(yù)分頻器的輸入線是連接到內(nèi)部時鐘(CK_INT)。
內(nèi)部時鐘的來源是RCC的TIMxCLK,這里的頻率值一般都是系統(tǒng)的主頻72MHz。
預(yù)分頻器寫0,就是不分頻;寫1就是2分頻,輸出頻率=輸入頻率/2=36MHz。
寫2就是3分頻,輸出= 輸入/3。所以預(yù)分頻器的值和實際的分頻系數(shù)相差了1,實際分頻系數(shù)=預(yù)分頻器的值 + 1。預(yù)分頻器是16位的,所以最大值可以寫65535,也就是65536分頻。 -
計數(shù)器CNT
計數(shù)器可以對預(yù)分頻后的計數(shù)時鐘進行計數(shù),計數(shù)時鐘每來一個上升沿,計數(shù)器的值就加1。計數(shù)器也是16位的,所以里面的值可以從0一直加到65535。如果再加的話,計數(shù)器就會回到0重新開始。所以計數(shù)器的值在計數(shù)過程中會不斷自增運行,當(dāng)自增運行到目標(biāo)值時,會產(chǎn)生中斷,那就完成了定時的任務(wù)。所以還需要一個存儲目標(biāo)值的寄存器,那就是自動重裝寄存器了。 -
自動重裝寄存器
自動重裝寄存器也是16位的,它存的就是要寫入的計數(shù)目標(biāo)。在運行的過程中,計數(shù)值不斷自增,自動重裝值是固定的目標(biāo),當(dāng)計數(shù)值等于自動重裝值時,也就是計時時間到了。那它就會產(chǎn)生一個中斷信號,并且清零計數(shù)器,計數(shù)器自動開始下一次的計數(shù)計時。 -
U和UI箭頭
圖上畫的一個向上的折現(xiàn)箭頭,就代表這里會產(chǎn)生中斷信號,像這種計數(shù)值等于自動重裝值產(chǎn)生的中斷,一般把它叫做“更新中斷”。這個更新中斷之后就會通往NVIC,我們再配置好NVIC的定時器通道,那定時器的更新中斷就能夠得到CPU的響應(yīng)了。向下的箭頭,代表的是會產(chǎn)生一個事件,這里對應(yīng)的事件就叫做“更新事件”。更新事件不會觸發(fā)中斷,但可以觸發(fā)內(nèi)部其他電路的工作。
1.4 通用定時器
主要功能:
-
16位向上、向下、向上/向上 自動重裝載累加計數(shù)器
-
16位可編程預(yù)分頻器,用于對輸入的時鐘按系數(shù)為1~65536之間的任意數(shù)值分頻
-
4個獨立通道:
- 輸入捕獲
- 輸出比較
- PWM生成(邊緣或中間對齊模式)
- 單脈沖模式輸出
-
使用外部信號控制定時器和定時器互連的同步電路
-
如下事件發(fā)生時產(chǎn)生中斷/DMA:
- 更新:計數(shù)器向上溢出/向下溢出,計數(shù)器初始化(通過軟件或內(nèi)部/外部觸發(fā))
- 觸發(fā)事件(計數(shù)器啟動、停止、初始化或者由內(nèi)部/外部觸發(fā)計數(shù))
- 輸入捕獲
- 輸出比較
-
支持針對定位的增量(正交)編碼器和霍爾傳感器電路
-
觸發(fā)輸入作為外部時鐘或者按周期的電流管理
-
CNT計數(shù)器
CNT計數(shù)器支持向上計數(shù)模式、向下計數(shù)模式、中央對齊模式。向下計數(shù)模式就是從重裝值開始,向下自減,減到0之后,回到重裝值同時申請中斷。中央對齊模式是從0開始,先向上自增,計到重裝值,申請中斷,然后再向下自減,減到0,再申請中斷。 -
時鐘源
通用定時器的時鐘源不僅可以選擇內(nèi)部72MHz時鐘,還可以選擇外部時鐘。
(1)第一個外部時鐘就是來自TIMx_ETR引腳上的外部時鐘,也就是可以在TIM2的ETR引腳即PA0上接一個外部方波時鐘,然后配置一下內(nèi)部的極性選擇、邊沿檢測和預(yù)分頻器電路,以及輸入濾波電路,這兩塊電路可以對外部時鐘進行一定的整形。因為是外部引腳的時鐘,所以難免有一些毛刺,那這些電路就可以對輸入的波形進行濾波。濾波后的信號,兵分兩路,上面一路ETRF進入觸發(fā)控制器,緊跟著就可以選擇作為時基單元的時鐘了。這一路也叫做“外部時鐘模式2”。
(2)TRGI也可以提供外部時鐘,主要用作觸發(fā)輸入使用的,這個觸發(fā)輸入可以觸發(fā)定時器的從模式。當(dāng)TRGI當(dāng)作外部時鐘來使用時,這一路就叫做“外部時鐘模式1”。通過這一路的外部時鐘有哪些呢?第一個就是ETR引腳的信號。第二個是ITR信號,這一部分的時鐘信號是來自其他定時器的。主模式的TRGO可以通向其他定時器,通向其他定時器的時候,就接到了其他定時器的ITR引腳上來了,ITR0到ITR3分別來自其他4個定時器的TRGO輸出。也可以選擇TI1F_ED,這里連接的是輸入捕獲單元的CH1引腳,也就是從CH1引腳獲得時鐘,這里后綴加一個ED(Edge)就是邊沿的意思。還可以通過TI1FP1和TI2FP2獲得,TI1FP1是連接到了CH1引腳的時鐘,TI2FP2連接到了CH2引腳的時鐘。
總結(jié):外部時鐘模式1的輸入可以是ETR引腳、其他定時器、CH1引腳的邊沿、CH1引腳和CH2引腳。 -
輸出比較電路
下面的右邊部分是輸出比較電路,總共有四個通道,分別對應(yīng)CH1到CH4的引腳,可以用于輸出PWM波形,驅(qū)動電機。 -
輸入捕獲電路
左邊是輸入捕獲電路,也是有四個通道,對應(yīng)的也是CH1到CH4的引腳,可以用于測輸入方波的頻率等。 -
捕獲/比較寄存器
中間的寄存器是捕獲/比較寄存器,是輸入捕獲和輸出比較電路共用的,因為輸入捕獲和輸出比較不能同時使用,所以這里寄存器是共用的,引腳也是共用的。
1.4 高級定時器
主要功能:
- 16位向上、向下、向上/向上 自動重裝載累加計數(shù)器
- 16位可編程預(yù)分頻器,用于對輸入的時鐘按系數(shù)為1~65536之間的任意數(shù)值分頻
- 4個獨立通道:
- 輸入捕獲
- 輸出比較
- PWM生成(邊緣或中間對齊模式)
- 單脈沖模式輸出
- 死區(qū)時間可編程的互補輸出
- 使用外部信號控制定時器和定時器互連的同步電路
- 允許在指定數(shù)據(jù)的計數(shù)器周期之后更新定時器寄存器的重復(fù)計數(shù)器
- 剎車輸入信號可以將定時器輸出信號置于復(fù)位狀態(tài)或者一個已知狀態(tài)
- 如下事件發(fā)生時產(chǎn)生中斷/DMA:
- 更新:計數(shù)器向上溢出/向下溢出,計數(shù)器初始化(通過軟件或內(nèi)部/外部觸發(fā))
- 觸發(fā)事件(計數(shù)器啟動、停止、初始化或者由內(nèi)部/外部觸發(fā)計數(shù))
- 輸入捕獲
- 輸出比較
- 剎車信號輸入
- 支持針對定位的增量(正交)編碼器和霍爾傳感器電路
- 觸發(fā)輸入作為外部時鐘或者按周期的電流管理
-
重復(fù)次數(shù)計數(shù)器
和通用定時器的區(qū)別:第一個是在申請中斷的地方增加了一個重復(fù)次數(shù)計數(shù)器,有了這個計數(shù)器之后,就可以實現(xiàn)每隔幾個計數(shù)周期才發(fā)生一次更新事件和更新中斷。原來的結(jié)構(gòu)是每個計數(shù)周期完成后就都會發(fā)生更新,這就相當(dāng)于對輸出的更新信號又做了一次分頻。 -
DTG死區(qū)生成器和互補輸出
下面是高級定時器對輸出比較模塊的升級了,DTG(dead time generate)是死區(qū)生成電路。右邊的輸出引腳由原來的一個變?yōu)榱藘蓚€互補的輸出,可以輸出一對互補的PWM波,這些電路是為了驅(qū)動三相無刷電機的,比如四軸飛行器、電動車的后輪、電鉆等,里面都可能是三相無刷電機。因為三相無刷電機的驅(qū)動電路一般需要3個橋臂,每個橋臂2個大功率開關(guān)管來控制,所以總共需要6個大功率開關(guān)管。因此這里的輸出PWM引腳的前三路就變?yōu)榱嘶パa的輸出。另外,為了防止互補輸出的PWM驅(qū)動橋臂時,在開關(guān)切換的瞬間,由于器件的不理想,造成短暫的直通現(xiàn)象,所以前面就加上了死區(qū)生成電路。在開關(guān)切換的瞬間,產(chǎn)生一定時長的死區(qū),讓橋臂的上下管全都關(guān)斷,防止直通現(xiàn)象。 -
剎車輸入
最后一部分就是剎車輸入的功能了,這個是為了給電機驅(qū)動提供安全保障的。如果外部引腳BKIN(Break IN)產(chǎn)生了剎車信號,或內(nèi)部時鐘失效,產(chǎn)生了故障,那么控制電路就會自動切斷電機的輸出,防止意外的發(fā)生。
1.5 定時中斷基本結(jié)構(gòu)
運行控制:控制寄存器中的一些位,如啟動停止、向上或向下計數(shù)等。
右邊就是計時時間到,產(chǎn)生更新中斷后的信號去向,如果是高級定時器的話,還會多一個重復(fù)計數(shù)器。中斷信號會先在狀態(tài)寄存器里置一個中斷標(biāo)志位,這個標(biāo)志位會通過中斷輸出控制,到NVIC申請中斷。中斷輸出控制就是一個中斷輸出的允許位,如果需要某個中斷,就記得允許一下。
1.6 預(yù)分頻器時序
- 計數(shù)器計數(shù)頻率:CK_CNT = CK_PSC / (PSC + 1)
- CK_PSC:預(yù)分頻器時鐘,內(nèi)部時鐘就是72MHz。
- CNT_EN:計數(shù)器使能,高電平計數(shù)器正常運行,低電平計數(shù)器停止。
- CK_CNT:計數(shù)器時鐘,它既是預(yù)分頻器的時鐘輸出,也是計數(shù)器的時鐘輸入。
- 在開始時,計數(shù)器未使能,計數(shù)器時鐘不運行。使能后,前半段,實際分頻系數(shù)為1(PSC=0),計數(shù)器的時鐘等于預(yù)分頻器前的時鐘;后半段,實際分頻系數(shù)為2(PSC=1)了,計數(shù)器的時鐘也就變?yōu)轭A(yù)分頻器前時鐘的一半了。在計數(shù)器時鐘的驅(qū)動下,下面的計數(shù)器寄存器也跟隨時鐘的上升沿不斷自增,在中間的這個位置FC之后,計數(shù)值變?yōu)?了,從這里可以推斷出ARR自動重裝值就是FC。當(dāng)計數(shù)值計到和重裝值相等,并且下一個時鐘來臨時,計數(shù)值才清零,同時下面產(chǎn)生一個更新事件,這就是一個計數(shù)周期的工作流程。
- 下面的三行描述的是預(yù)分頻寄存器的一種緩沖機制,也就是這個預(yù)分頻寄存器實際上是有兩個,一個是預(yù)分頻控制寄存器,是供我們讀寫用的,它并不直接決定分頻系數(shù)。另外還有一個緩沖寄存器或者說是影子寄存器:預(yù)分頻緩沖器,這個緩沖寄存器才是真正起作用的寄存器,比如我們在某個時刻,把預(yù)分頻寄存器由0改成了1,如果在此時立刻改變時鐘的分頻系數(shù),那么就會導(dǎo)致在一個計數(shù)周期內(nèi),前半部分和后半部分的頻率不一樣。因此設(shè)計了緩沖器,當(dāng)計數(shù)計到一半的時候改變了分頻值,這個變化并不會立刻生效,而是會等到本次計數(shù)周期結(jié)束時,產(chǎn)生了更新事件,預(yù)分頻寄存器的值才會被傳遞到緩沖寄存器里,才會生效。
- 最后一行可以看出:預(yù)分頻器內(nèi)部也是靠計數(shù)來分頻的,當(dāng)預(yù)分頻值為0時,計數(shù)器就一直為0,直接輸出原頻率;當(dāng)預(yù)分頻值為1時,計數(shù)器就0、1、0、1、0、1這樣計數(shù),在回到0時,輸出1個脈沖,這樣輸出頻率就是輸入頻率的2分頻,預(yù)分頻器的值和實際的分頻系數(shù)之間有一個數(shù)的偏移。
1.7 計數(shù)器時序
- 計數(shù)器溢出頻率:CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)
- CK_INT:內(nèi)部時鐘72MHz;
- CNT_EN:計數(shù)器使能,上升沿有效;
- CK_CNT:計數(shù)器時鐘,因為分頻系數(shù)為2,所以這個頻率是CK_INT除2。然后計數(shù)器在這個時鐘每個上升沿自增,當(dāng)增到0036的時候,發(fā)生溢出。計到36之后,再來一個上升沿,計數(shù)器清零,計數(shù)器溢出,產(chǎn)生一個更新事件脈沖,另外還會置一個更新中斷標(biāo)志(UIF),更新中斷標(biāo)志(UIF)置1了,就會申請中斷,中斷響應(yīng)后,需要在中斷程序中手動清零。
1.8 計數(shù)器無預(yù)裝時序
更改了自動加載寄存器,由FF改成了36,那計數(shù)值的目標(biāo)值就由FF變成了36,所以這里計到36之后,就直接更新開始下一輪的計數(shù),
1.9 計數(shù)器有預(yù)裝時序
在計數(shù)的中途,把計數(shù)目標(biāo)值由F5改成了36。下面有個影子寄存器,這個影子寄存器才是真正起作用的,它還是F5,所以現(xiàn)在計數(shù)的目標(biāo)還是計到F5,產(chǎn)生更新事件,同時,要更改的36才被傳遞到影子寄存器,在下一個計數(shù)周期這個更改的36才有效,所以這個引入影子寄存器的目的實際上是為了同步,就是讓值的變化和更新事件同步發(fā)生,防止在運行途中,更改造成錯誤。
1.10 RCC時鐘樹
-
時鐘源
在時鐘產(chǎn)生電路,有四個震蕩源:
(1)HSI:內(nèi)部的8MHz高速RC振蕩器;
(2)HSE:外部的4~16MHz高速石英晶體振蕩器,也就是晶振,一般都是接8MHz;
(3)LSE:外部的32.768KHz低速晶振,這個一般是給RTC提供時鐘的;
(4)LSI:最后是內(nèi)部的40KHz低速RC振蕩器,這個可以給看門狗提供時鐘。
上面兩個高速晶振是用提供系統(tǒng)時鐘的,AHB、APB2、APB1的時鐘都是來源于這兩個高速晶振,只不過是外部的石英振蕩器比內(nèi)部的RC振蕩器更加穩(wěn)定,所以一般都是用外部晶振。 -
ST配置時鐘
在SystemInit函數(shù)里,ST配置時鐘:首先會啟動內(nèi)部時鐘HSI,選擇內(nèi)部8MHz為系統(tǒng)時鐘,暫時以內(nèi)部8MHz的時鐘運行。然后再啟動外部時鐘,配置外部時鐘進入PLL鎖相環(huán)進行倍頻,8MHz倍頻9倍,就得到72MHz,等到鎖相環(huán)輸出穩(wěn)定后,選擇鎖相環(huán)輸出為系統(tǒng)時鐘,這樣就把系統(tǒng)時鐘由8MHz切換為了72MHz。 -
CSS時鐘安全系統(tǒng)
CSS(clock security system):時鐘安全系統(tǒng),也是負(fù)責(zé)切換時鐘的,可以監(jiān)測外部時鐘的運行狀態(tài),一旦外部時鐘失效,就會自動把外部時鐘切換回內(nèi)部時鐘,保證系統(tǒng)時鐘的運行,防止程序卡死造成事故。 -
時鐘分配電路
- AHB總線: 首先系統(tǒng)時鐘72MHz進入AHB總線,AHB總線有個預(yù)分頻器,在SystemInit里配置的分配系數(shù)為1,那AHB的時鐘就是72MHz。
- APB1總線:這里配置的分配系數(shù)為2,所以APB1總線的時鐘為72MHz/2=36MHz。下面的如果APB1預(yù)分頻系數(shù)=1,則頻率不變,否則頻率
*2
,然后右邊,是單獨為定時器2-7開通的,因為這里預(yù)分頻系數(shù),我們給的是2,所以這里頻率要再*2
,所以通向定時器2~7的時鐘為72MHz。因此無論是高級定時器、通用定時器還是基本定時器,它們的內(nèi)部基準(zhǔn)時鐘都是72MHz。 - APB2總線:APB2的分頻系數(shù)為1,所以時鐘為72MHz。然后接在APB2上的時鐘也單開了一路,即如果APB2預(yù)分頻系數(shù)=1,則頻率不變,否則頻率
*2
。因為分頻系數(shù)給的是1,所以定時器1和8的時鐘就是72MHz。 - 時鐘輸出部分都有一個與門進行輸出控制,控制位寫的是外部時鐘使能,這就是我們再在程序中寫RCC_APB2/1PeriphClockCmd作用的地方,打開時鐘,就是在這個位置寫1,讓左邊的時鐘能夠通過與門輸出給外設(shè)。
2. TIM庫函數(shù)及代碼
2.1 TIM庫函數(shù)
// 恢復(fù)缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);// 時基單元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);// 結(jié)構(gòu)體變量賦一個默認(rèn)值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);// 使能計數(shù)器---運行控制
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);// 使能中斷輸出---中斷輸出控制
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);/*-----------------時基單元的時鐘選擇---------*/
// 選擇內(nèi)部時鐘
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);// 選擇ITRx其他定時器的時鐘
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);// 選擇TIx捕獲通道的時鐘
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);// 選擇ETR通過外部時鐘模式1輸入的時鐘
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);// 選擇ETR通過外部時鐘模式2輸入的時鐘
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);/*--------------------------------------------------------*/
// 不是用來選擇時鐘的,單獨用來配置ETR引腳的預(yù)分頻器、極性、濾波參數(shù)的
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);// 用來單獨寫預(yù)分頻值的
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);// 用來改變計數(shù)器的計數(shù)模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);// 自動重裝器預(yù)裝功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);// 給計數(shù)器寫入一個值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);// 給自動重裝器寫入一個值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);// 獲取當(dāng)前計數(shù)器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);// 獲取當(dāng)前的預(yù)分頻器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);/*-------------獲取標(biāo)志位和清除標(biāo)志位的------------*/
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
2.2 6-1定時器中斷代碼
2.2.1 硬件電路:
實現(xiàn)功能:定時1s,并在OLED上顯示Num值(中斷觸發(fā)次數(shù),也即定時時間)
2.2.2 代碼流程
-
定時器代碼
- 開啟時鐘RCC,定時器的基準(zhǔn)時鐘和整個外設(shè)的工作時鐘都會同時打開了;
- 選擇時基單元的時鐘源,對于定時中斷,選擇內(nèi)部時鐘源;
- 配置時基單元,包括預(yù)分頻器、自動重裝器、計數(shù)模式等–用結(jié)構(gòu)體;
- 配置中斷輸出控制,允許更新中斷輸出到NVIC;
- 配置NVIC,在NVIC中打開定時器中斷的通道,并分配一個優(yōu)先級;
- 運行控制;
整個模塊配置完成后,還需要使能一下計數(shù)器,要不然計數(shù)器是不會運行的。當(dāng)定時器使能后,計數(shù)器就開始計數(shù)了,當(dāng)計數(shù)器更新時,觸發(fā)中斷,最后再寫一個定時器的中斷函數(shù),這樣這個中斷函數(shù)就每隔一段時間就能自動執(zhí)行一次了。
-
中斷函數(shù)
- 判斷是否進入定時器TIM2的中斷,然后清除中斷標(biāo)志位。
- 使定時器每秒自動加一下Num變量
-
ARR和PSC設(shè)置
- ARR=10000 - 1;PSC = 7200 - 1;
- 定時器時鐘CK_CNT =72M / (PSC + 1) = 10000 ;
- 定時頻率 = CK_CNT / (ARR + 1) = 1。定時1s,也就是定時頻率為1Hz。
預(yù)分頻是對72M進行7200分頻,得到的就是10K的計數(shù)頻率,在10K的頻率下,計10000個數(shù),就是1s的時間。(在1s的時間內(nèi)計10000個數(shù),當(dāng)計到10000個數(shù)后,自動清0,同時申請中斷,在OLED顯示屏上就是顯示Num值每1s加1)。也可以更改ARR和PSC的值。
- main函數(shù)
- OLED顯示Num值。
2.2.3 代碼
- Timer.c代碼
#include "stm32f10x.h" // Device header/*** 函 數(shù):定時中斷初始化* 參 數(shù):無* 返 回 值:無*/
void Timer_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘/*配置時鐘源*/TIM_InternalClockConfig(TIM2); //選擇TIM2為內(nèi)部時鐘,若不調(diào)用此函數(shù),TIM默認(rèn)也為內(nèi)部時鐘/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結(jié)構(gòu)體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數(shù)用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數(shù)器模式,選擇向上計數(shù)TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //計數(shù)周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //預(yù)分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復(fù)計數(shù)器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結(jié)構(gòu)體變量交給TIM_TimeBaseInit,配置TIM2的時基單元 /*中斷輸出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定時器更新標(biāo)志位//TIM_TimeBaseInit函數(shù)末尾,手動產(chǎn)生了更新事件//若不清除此標(biāo)志位,則開啟中斷后,會立刻進入一次中斷//如果不介意此問題,則不清除此標(biāo)志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //開啟TIM2的更新中斷/*NVIC中斷分組*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC為分組2//即搶占優(yōu)先級范圍:0~3,響應(yīng)優(yōu)先級范圍:0~3//此分組配置在整個工程中僅需調(diào)用一次//若有多個中斷,可以把此代碼放在main函數(shù)內(nèi),while循環(huán)之前//若調(diào)用多次配置分組的代碼,則后執(zhí)行的配置會覆蓋先執(zhí)行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定義結(jié)構(gòu)體變量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //選擇配置NVIC的TIM2線NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC線路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC線路的搶占優(yōu)先級為2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC線路的響應(yīng)優(yōu)先級為1NVIC_Init(&NVIC_InitStructure); //將結(jié)構(gòu)體變量交給NVIC_Init,配置NVIC外設(shè)/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"uint16_t Num; //定義在定時器中斷里自增的變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化Timer_Init(); //定時中斷初始化/*顯示靜態(tài)字符串*/OLED_ShowString(1, 1, "Num:"); //1行1列顯示字符串Num:while (1){OLED_ShowNum(1, 5, Num, 5); //不斷刷新顯示Num變量}
}/*** 函 數(shù):TIM2中斷函數(shù)* 參 數(shù):無* 返 回 值:無* 注意事項:此函數(shù)為中斷函數(shù),無需調(diào)用,中斷觸發(fā)后自動執(zhí)行* 函數(shù)名為預(yù)留的指定名稱,可以從啟動文件復(fù)制* 請確保函數(shù)名正確,不能有任何差異,否則中斷函數(shù)將不能進入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判斷是否是TIM2的更新事件觸發(fā)的中斷{Num ++; //Num變量自增,用于測試定時中斷TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中斷標(biāo)志位//中斷標(biāo)志位必須清除//否則中斷將連續(xù)不斷地觸發(fā),導(dǎo)致主程序卡死}
}
2.3 6-2定時器外部時鐘代碼
2.3.1 硬件電路
- 將對射式紅外傳感器接在PA0引腳,即TIM2_CH1_ETR,時鐘配置為外部時鐘模式2。
- 每次對傳感器進行遮擋時,計數(shù)值CNT(TIM_GetCounter(TIM2))加1,當(dāng)加到ARR時,觸發(fā)中斷,使Num值加1。
- 在OLED顯示屏上顯示Num和CNT的值。
2.3.2 代碼流程
- 定時器代碼
- 開啟時鐘RCC,TIM2和GPIOA。
- 配置GPIO,為上拉輸入。
- 選擇時基單元的時鐘源,外部時鐘模式2,時鐘從TIM2_ETR引腳輸入;
- 配置時基單元,包括預(yù)分頻器、自動重裝器、計數(shù)模式等–用結(jié)構(gòu)體;
- 配置中斷輸出控制,允許更新中斷輸出到NVIC;
- 配置NVIC,在NVIC中打開定時器中斷的通道,并分配一個優(yōu)先級;
- 運行控制;
- ARR和PSC
- ARR = 10 - 1; PSC = 1 - 1;
- 因為沒有進行分頻,所以對射式紅外傳感器每遮擋一次,計數(shù)值CNT加1,當(dāng)計數(shù)值加到9后,自動清零,同時申請中斷,Num++。
- main函數(shù)
- 實現(xiàn)在OLED顯示屏上顯示Num和CNT的值。
2.3.3 代碼
- Timer.c
#include "stm32f10x.h" // Device header/*** 函 數(shù):定時中斷初始化* 參 數(shù):無* 返 回 值:無* 注意事項:此函數(shù)配置為外部時鐘,定時器相當(dāng)于計數(shù)器*/
void Timer_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //開啟TIM2的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA0引腳初始化為上拉輸入/*外部時鐘配置*/TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);//選擇外部時鐘模式2,時鐘從TIM_ETR引腳輸入//注意TIM2的ETR引腳固定為PA0,無法隨意更改//最后一個濾波器參數(shù)加到最大0x0F,可濾除時鐘信號抖動/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定義結(jié)構(gòu)體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻,選擇不分頻,此參數(shù)用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數(shù)器模式,選擇向上計數(shù)TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //計數(shù)周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //預(yù)分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重復(fù)計數(shù)器,高級定時器才會用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //將結(jié)構(gòu)體變量交給TIM_TimeBaseInit,配置TIM2的時基單元 /*中斷輸出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定時器更新標(biāo)志位//TIM_TimeBaseInit函數(shù)末尾,手動產(chǎn)生了更新事件//若不清除此標(biāo)志位,則開啟中斷后,會立刻進入一次中斷//如果不介意此問題,則不清除此標(biāo)志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //開啟TIM2的更新中斷/*NVIC中斷分組*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC為分組2//即搶占優(yōu)先級范圍:0~3,響應(yīng)優(yōu)先級范圍:0~3//此分組配置在整個工程中僅需調(diào)用一次//若有多個中斷,可以把此代碼放在main函數(shù)內(nèi),while循環(huán)之前//若調(diào)用多次配置分組的代碼,則后執(zhí)行的配置會覆蓋先執(zhí)行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定義結(jié)構(gòu)體變量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //選擇配置NVIC的TIM2線NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC線路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC線路的搶占優(yōu)先級為2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC線路的響應(yīng)優(yōu)先級為1NVIC_Init(&NVIC_InitStructure); //將結(jié)構(gòu)體變量交給NVIC_Init,配置NVIC外設(shè)/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定時器開始運行
}/*** 函 數(shù):返回定時器CNT的值* 參 數(shù):無* 返 回 值:定時器CNT的值,范圍:0~65535*/
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2); //返回定時器TIM2的CNT
}
- main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"uint16_t Num; //定義在定時器中斷里自增的變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化Timer_Init(); //定時中斷初始化/*顯示靜態(tài)字符串*/OLED_ShowString(1, 1, "Num:"); //1行1列顯示字符串Num:OLED_ShowString(2, 1, "CNT:"); //2行1列顯示字符串CNT:while (1){OLED_ShowNum(1, 5, Num, 5); //不斷刷新顯示Num變量OLED_ShowNum(2, 5, Timer_GetCounter(), 5); //不斷刷新顯示CNT的值}
}/*** 函 數(shù):TIM2中斷函數(shù)* 參 數(shù):無* 返 回 值:無* 注意事項:此函數(shù)為中斷函數(shù),無需調(diào)用,中斷觸發(fā)后自動執(zhí)行* 函數(shù)名為預(yù)留的指定名稱,可以從啟動文件復(fù)制* 請確保函數(shù)名正確,不能有任何差異,否則中斷函數(shù)將不能進入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判斷是否是TIM2的更新事件觸發(fā)的中斷{Num ++; //Num變量自增,用于測試定時中斷TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中斷標(biāo)志位//中斷標(biāo)志位必須清除//否則中斷將連續(xù)不斷地觸發(fā),導(dǎo)致主程序卡死}
}