???本次課程采用單片機(jī)型號(hào)為STM32F103C8T6。
???課程鏈接:江科大自化協(xié) STM32入門(mén)教程
??往期筆記鏈接:
??STM32學(xué)習(xí)筆記(一)丨建立工程丨GPIO 通用輸入輸出
??STM32學(xué)習(xí)筆記(二)丨STM32程序調(diào)試丨OLED的使用
??STM32學(xué)習(xí)筆記(三)丨中斷系統(tǒng)丨EXTI外部中斷
??如果上一篇筆記的內(nèi)容為史詩(shī)級(jí)副本,本篇文章的內(nèi)容我愿稱(chēng)之為傳說(shuō)級(jí)副本(一)。
一、TIM 定時(shí)器
1.1 TIM 定時(shí)器簡(jiǎn)介
??TIM(Timer)定時(shí)器,它的基本功能是 對(duì)輸入的時(shí)鐘進(jìn)行計(jì)數(shù),并在計(jì)數(shù)值達(dá)到定值時(shí)觸發(fā)中斷,即定時(shí)觸發(fā)中斷定時(shí)器就是一個(gè)計(jì)數(shù)器,當(dāng)計(jì)數(shù)器的輸入是一個(gè)準(zhǔn)確可靠的基準(zhǔn)時(shí)鐘時(shí),對(duì)基準(zhǔn)時(shí)鐘進(jìn)行計(jì)數(shù)的過(guò)程就是計(jì)時(shí)的過(guò)程。 在STM32中,定時(shí)器的基準(zhǔn)時(shí)鐘一般都是72MHz。
??定時(shí)器最核心的部分稱(chēng)為 時(shí)基單元,它由以下三個(gè)16位的寄存器組成:
- 計(jì)數(shù)器(Counter):用來(lái)計(jì)數(shù)定時(shí)的寄存器,每來(lái)一個(gè)時(shí)鐘,計(jì)數(shù)器加1。
- 預(yù)分頻器(Prescaler):可以對(duì)計(jì)數(shù)器的時(shí)鐘進(jìn)行分頻,讓時(shí)鐘更加靈活。
- 自動(dòng)重裝寄存器(Auto-reload Register):計(jì)數(shù)的目標(biāo)值,即計(jì)多少個(gè)時(shí)鐘申請(qǐng)中斷。
??由于構(gòu)成時(shí)基單元的寄存器都是16位的,故如果我們將預(yù)分頻器的值設(shè)置為最大,自動(dòng)重裝值也設(shè)置為最大,設(shè)中斷頻率為
f
0
f_0
f0?,在72MHz主頻下的最大定時(shí)時(shí)間
t
m
a
x
t_{max}
tmax?,則:
f
0
=
72
/
(
2
16
)
2
M
H
z
t
m
a
x
=
1
f
0
=
59.65
s
f_0=72/(2^{16})^2MHz\\ t_{max}=\frac 1 f_0=59.65s
f0?=72/(216)2MHztmax?=f1?0?=59.65s
??59.65s的定時(shí)時(shí)間已經(jīng)是比較長(zhǎng)的時(shí)間了。如果由更高的時(shí)間定時(shí)需求,STM32的定時(shí)器支持級(jí)聯(lián) ,即一個(gè)定時(shí)器的輸入為另一個(gè)定時(shí)器的輸出,這時(shí)的定時(shí)時(shí)間約為八千多年。如果再級(jí)聯(lián)一個(gè)定時(shí)器,這時(shí)的定時(shí)時(shí)間將會(huì)達(dá)到34萬(wàn)億年??梢?jiàn),STM32定時(shí)器的定時(shí)時(shí)間選擇是相當(dāng)自由的。
??STM32的定時(shí)器的常用功能有以下幾種(列出的功能本課程都會(huì)涉及):
- 定時(shí)中斷功能
- 內(nèi)外時(shí)鐘源選擇
- 輸入捕獲
- 輸出比較
- 編碼器接口
- 主從觸發(fā)模式
- …
??STM32的定時(shí)器還根據(jù)應(yīng)用場(chǎng)景和復(fù)雜度設(shè)計(jì)了高級(jí)定時(shí)器、通用定時(shí)器、基本定時(shí)器三種類(lèi)型。本課程主要學(xué)習(xí)和使用通用定時(shí)器。
1.2 TIM 定時(shí)器類(lèi)型及其工作原理簡(jiǎn)介
??有關(guān)STM32的定時(shí)器類(lèi)型,編號(hào)和功能簡(jiǎn)述如下表所示:
??同一個(gè)芯片一般有很多個(gè)定時(shí)器,它們都用TIMx表示。上表中只列出了TIM1~TIM8,但是再庫(kù)函數(shù)中還出現(xiàn)了TIM9、TIM10、TIM11等,這些都不常用。
??不同的定時(shí)器連接的總線也不相同,高級(jí)定時(shí)器連接的是性能更高的APB2總線,而通用定時(shí)器和基本定時(shí)器連接的是APB1總線。在使用RCC開(kāi)啟定時(shí)器時(shí)鐘的時(shí)候要注意庫(kù)函數(shù)的調(diào)用。
??定時(shí)器的功能是從高級(jí)向低級(jí)向下兼容的,高級(jí)定時(shí)器包含通用定時(shí)器的全部功能,通用定時(shí)器包含基本定時(shí)器的全部功能。高級(jí)定時(shí)器相較于通用定時(shí)器的額外功能,例如重復(fù)計(jì)數(shù)器,死區(qū)生成,互補(bǔ)輸出,剎車(chē)輸入等,這些都是為了三相無(wú)刷電機(jī)FOC的驅(qū)動(dòng)設(shè)計(jì)的,本課程暫時(shí)不會(huì)涉及。
??本課程使用的STM32F103C8T6芯片的定時(shí)器資源有:TIM1,TIM2,TIM3,TIM4,即擁有一個(gè)高級(jí)定時(shí)器,三個(gè)通用定時(shí)器,沒(méi)有基本定時(shí)器。不同的芯片型號(hào),定時(shí)器的數(shù)量和類(lèi)型是不同的。在使用外設(shè)之前,一定要查詢(xún)外設(shè)是否存在。
1.2.1 基本定時(shí)器工作原理及其結(jié)構(gòu)
??基本定時(shí)器擁有定時(shí)中斷,主模式觸發(fā)DAC(數(shù)模轉(zhuǎn)換器)的功能。基本定時(shí)器結(jié)構(gòu)框圖如下圖所示:
??由上圖可以看到,基本定時(shí)器擁有由預(yù)分頻器PSC(Prescaler),計(jì)數(shù)器CNT(Counter),ARR(Auto-reload Register)自動(dòng)重裝寄存器組成的基本時(shí)基單元。預(yù)分頻器之前連接的是 基準(zhǔn)時(shí)鐘的輸入,由于基本定時(shí)器只能連接內(nèi)部時(shí)鐘,故可以直接認(rèn)為預(yù)分頻器的時(shí)鐘輸入CK_PSC就是連接到內(nèi)部時(shí)鐘CK_INT上的。內(nèi)部時(shí)鐘的來(lái)源是RCC_TIMxCLK,這里的頻率一般都是系統(tǒng)的主頻72MHz。所以基本定時(shí)器的基準(zhǔn)時(shí)鐘輸入只能是72MHz。
??PSC預(yù)分頻器可以對(duì)基準(zhǔn)時(shí)鐘進(jìn)行預(yù)分頻。如果預(yù)分頻器寫(xiě)0,就是不分頻,或者說(shuō)1分頻,這時(shí) 輸出頻率 = 輸入頻率 = 72MHz;如果預(yù)分頻器寫(xiě)1,就是2分頻,輸出頻率 = 輸入頻率 / 2 = 36MHz;如果預(yù)分頻器寫(xiě)2,就是3分頻……依次類(lèi)推。所以我們有如下的關(guān)系:
實(shí)際分頻系數(shù)
=
預(yù)分配器寫(xiě)入的值
+
1
實(shí)際分頻系數(shù)=預(yù)分配器寫(xiě)入的值+1
實(shí)際分頻系數(shù)=預(yù)分配器寫(xiě)入的值+1
??預(yù)分頻器是16位的寄存器,故最大值可以寫(xiě)入65535(
2
16
2^{16}
216),也就是65536分頻。
??CNT計(jì)數(shù)器可以對(duì)預(yù)分頻后的計(jì)數(shù)時(shí)鐘CK_CNT進(jìn)行計(jì)數(shù)。計(jì)數(shù)時(shí)鐘CK_CNT每來(lái)一個(gè)上升沿,計(jì)數(shù)器的值就+1(只能遞增)。計(jì)數(shù)器也是16位的,它的值可以是0~65535,如果再+1,計(jì)數(shù)器就會(huì)回到0重新開(kāi)始計(jì)數(shù)。
??ARR自動(dòng)重裝寄存器存入計(jì)數(shù)器的計(jì)數(shù)目標(biāo),它也是16位的寄存器。在計(jì)數(shù)器運(yùn)行不斷自增的過(guò)程中,自動(dòng)重裝置就是一個(gè)固定的目標(biāo)。計(jì)數(shù)器不斷與重裝寄存器進(jìn)行比較,當(dāng)計(jì)數(shù)值 = 自動(dòng)重裝值時(shí),就說(shuō)明計(jì)時(shí)時(shí)間到,這時(shí)自動(dòng)重裝寄存器就會(huì)輸出一個(gè)中斷信號(hào),并且清零計(jì)數(shù)器,計(jì)數(shù)器自動(dòng)開(kāi)始下一次的計(jì)數(shù)計(jì)時(shí)。
??在圖中的折線向上的箭頭,就代表產(chǎn)生的一個(gè)中斷信號(hào)。UI意為Update Interrupt,即更新中斷,它是計(jì)數(shù)值等于自動(dòng)重裝值的中斷。更新中斷會(huì)通往NVIC,只要配置好NVIC的中斷通道,那么定時(shí)器的更新中斷就能得到CPU的響應(yīng)了。
??在圖中折線向下的箭頭,就代表產(chǎn)生的一個(gè)事件。這里的U意為Update,即更新事件。更新中斷不會(huì)觸發(fā)中斷,但可觸發(fā)其他內(nèi)部電路的工作。圖中可以看到,自動(dòng)重裝寄存器也可響應(yīng)其他外設(shè)產(chǎn)生的事件。
主從觸發(fā)模式
??STM32的一大特色就是主從觸發(fā)模式。它能讓內(nèi)部的硬件在不受程序的控制下實(shí)現(xiàn)自動(dòng)運(yùn)行,即實(shí)現(xiàn)硬件自動(dòng)化。
??這里我的理解是:主模式,即作為信號(hào)的主人的模式,電路輸出的信號(hào)能觸發(fā)其他處于從模式的電路的響應(yīng)。 正常來(lái)講,在一個(gè)時(shí)刻只能執(zhí)行一段代碼,完成一個(gè)操作,如果能合理使用主從觸發(fā),就可以實(shí)現(xiàn)”一心二用“,將會(huì)極大地減少CPU運(yùn)行負(fù)擔(dān)(這里僅做簡(jiǎn)單了解即可,后續(xù)還會(huì)講到)。
??以基本定時(shí)器主模式觸發(fā)DAC為例:在我們使用DAC的時(shí)候,可能會(huì)用DAC輸出一段波形,那就需要每隔一段時(shí)間來(lái)觸發(fā)一次DAC,讓它輸出下一個(gè)電壓點(diǎn)。如果按中斷的思路,需要先設(shè)置一個(gè)定時(shí)器產(chǎn)生中斷,每隔一段時(shí)間在中斷程序中用代碼手動(dòng)觸發(fā)一次DAC轉(zhuǎn)換,然后DAC輸出波形。這樣操作沒(méi)有錯(cuò),但是這樣會(huì)使主程序頻繁處于被中斷的狀態(tài),有可能會(huì)影響主程序的運(yùn)行和其他中斷的響應(yīng)。所以定時(shí)器的主模式可以把定時(shí)器的更新事件U映射到觸發(fā)輸出TRGO(Trigger Out)的位置,再通過(guò)TRGO直接連接到DAC的觸發(fā)轉(zhuǎn)換引腳上。這樣DAC轉(zhuǎn)換就不需要定時(shí)器產(chǎn)生的中斷來(lái)觸發(fā)了,僅需要把更新事件U通過(guò)主模式映射到TRGO,TRGO自動(dòng)觸發(fā)DAC就可以實(shí)現(xiàn)。這里體現(xiàn)了主模式的作用:整個(gè)過(guò)程不需要軟件的參與,實(shí)現(xiàn)了硬件的自動(dòng)化。
1.2.2 通用定時(shí)器工作原理及其結(jié)構(gòu)
??通用定時(shí)器擁有基本定時(shí)器全部功能,并額外具有內(nèi)外時(shí)鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發(fā)模式等功能。 通用定時(shí)器的結(jié)構(gòu)框圖如下如所示:
- 通用定時(shí)器與基本定時(shí)器的異同
??通用定時(shí)器最基本的結(jié)構(gòu)也是時(shí)基單元,與基本定時(shí)器相同??梢哉J(rèn)為通用定時(shí)器就是在基本定時(shí)器的基礎(chǔ)上擴(kuò)展了許多功能得到的。它在時(shí)基單元模塊與基本定時(shí)器的區(qū)別是:計(jì)數(shù)器的計(jì)數(shù)模式不止向上計(jì)數(shù)一種。除了與基本定時(shí)器相同的向上計(jì)數(shù)的模式外,它還擁有以下兩種模式:
- 向下計(jì)數(shù)模式:計(jì)數(shù)從重裝值開(kāi)始,向下自減,到0之后回到重裝值,同時(shí)產(chǎn)生更新中斷和更新事件,依此循環(huán)。
- 中央對(duì)齊模式:計(jì)數(shù)從0開(kāi)始,向上自增,達(dá)到重裝值后產(chǎn)生更新中斷和更新之間,然后向下自減,達(dá)到重裝值之后產(chǎn)生更新中斷和更新事件,以此循環(huán)。
- 內(nèi)外時(shí)鐘源選擇功能
??結(jié)構(gòu)圖的上部分,為內(nèi)外時(shí)鐘源選擇和主從觸發(fā)模式的工作結(jié)構(gòu):
??首先來(lái)看內(nèi)外時(shí)鐘源選擇的工作原理?;径〞r(shí)器只能選擇來(lái)自芯片內(nèi)部的內(nèi)部時(shí)鐘CK_INT,也就是系統(tǒng)頻率72MHz。而通用定時(shí)器不僅可以選擇內(nèi)部時(shí)鐘,還可以選擇外部時(shí)鐘。具體有以下三種:
-
來(lái)自TIMx_ETR引腳上的外部時(shí)鐘ETR
??TIMx_ETR引腳的位置可以參考引腳定義表中關(guān)于默認(rèn)復(fù)用功能和重定義功能的定義,如下圖所示??梢钥吹絋IM2的CH1和ETR都復(fù)用在了引腳PA0上。其他定時(shí)器的引腳也可以在表中找到。
??我們?cè)赥IMx_ETR引腳上外接一個(gè)方波時(shí)鐘,進(jìn)入的外部時(shí)鐘ETR通過(guò)內(nèi)部的極性選擇、邊沿檢測(cè)和預(yù)分頻器電路,產(chǎn)生外部時(shí)鐘脈沖ETRP(這里我猜測(cè)P就是Pulse,即脈沖,暫時(shí)沒(méi)有找到相關(guān)的解釋)。之后再通過(guò)輸入濾波電路,之后的信號(hào)兵分兩路,上面的信號(hào)ETRF進(jìn)入觸發(fā)控制器(與內(nèi)部時(shí)鐘流程類(lèi)似),之后就可以作為時(shí)基單元的時(shí)鐘輸入了,在STM32中,這一路稱(chēng)為 外部時(shí)鐘模式2(如圖中紅線所示);另一路與其他信號(hào)通過(guò)一個(gè)數(shù)據(jù)選擇器輸出TRGI(Trigger In),當(dāng)這個(gè)TRGI當(dāng)作外部時(shí)鐘來(lái)使用時(shí),這一路就稱(chēng)為 外部時(shí)鐘模式1(如圖中黃線所示)。后者從名字上看,它主要是作為觸發(fā)輸入來(lái)使用的,這個(gè)觸發(fā)輸入可以觸發(fā)定時(shí)器的從模式。關(guān)于從模式的內(nèi)容之后再涉及,本節(jié)主要考量把這個(gè)觸發(fā)輸入當(dāng)作外部時(shí)鐘來(lái)考慮的情況。 -
來(lái)自其他定時(shí)器的信號(hào)ITR
??主模式的輸出TRGO可以通向其他定時(shí)器,實(shí)際上通向的就是ITR引腳,通過(guò)這一路就可以實(shí)現(xiàn)定時(shí)器級(jí)聯(lián)的功能。如上如黃線所示,ITR0到ITR3分別來(lái)自其他4個(gè)定時(shí)器的TRGO輸出,具體的連接方式如下表所示: -
來(lái)自TIMx_CH1的TI1_ED,即從CH1引腳連接的輸入捕獲模塊獲得時(shí)鐘,ED意為Edge,意為通過(guò)這一路的時(shí)鐘,上升沿和下降沿均有效。
-
來(lái)自TIMx_CH1的TI1FP1和來(lái)自TIMx_CH2的TI2FP2
??總結(jié)一下,外部時(shí)鐘模式1的輸入可以是ETR引腳、其他定時(shí)器、CH1引腳的邊沿、CH1引腳和CH2引腳;外部時(shí)鐘模式2的輸入只能是ETR引腳。
- 編碼器接口功能
??圖中的編碼器接口,它可以讀取正交編碼器的輸出模型,后續(xù)的課程也會(huì)講到。
- 主從觸發(fā)模式功能
??圖中的TRGO與基本定時(shí)器類(lèi)似,它可以將定時(shí)器內(nèi)部的一些事件映射到其他電路,從而完成其他電路的功能。
- 輸出比較功能
??通用定時(shí)器結(jié)構(gòu)圖的右下角即為定時(shí)器的輸出比較功能的結(jié)構(gòu),如下圖所示。有四個(gè)輸出通道,分別對(duì)應(yīng)CH1到CH4的引腳,可以用來(lái)輸出PWM波形,驅(qū)動(dòng)電機(jī)。
- 輸入捕獲電路
??通用定時(shí)器的左下角即為輸入捕獲電路的結(jié)構(gòu)圖,它同輸出比較功能一樣有四個(gè)通道,對(duì)應(yīng)CH1到CH4??梢杂糜跍y(cè)量輸入方波的頻率。因?yàn)檩斎氩东@和輸出比較不能同時(shí)使用,故中間的捕獲/比較寄存器是輸入捕獲和輸出比較電路共用的,CH1到CH4的引腳也是共用的。
1.2.3 高級(jí)定時(shí)器工作原理及其結(jié)構(gòu)
??高級(jí)定時(shí)器擁有通用定時(shí)器全部功能,并額外具有重復(fù)計(jì)數(shù)器、死區(qū)生成、互補(bǔ)輸出、剎車(chē)輸入等功能。高級(jí)定時(shí)器結(jié)構(gòu)框圖如下圖所示:
??高級(jí)定時(shí)器的大部分結(jié)構(gòu)和通用定時(shí)器相同,只在部分作了功能拓展。
- 重復(fù)次數(shù)計(jì)數(shù)器
??在申請(qǐng)中斷的的信號(hào)輸出處,增加了一個(gè)重復(fù)次數(shù)計(jì)數(shù)器,它的作用是:可以實(shí)現(xiàn)每隔幾個(gè)計(jì)數(shù)周期,才發(fā)生一次更新事件和中斷。相當(dāng)于對(duì)輸出的更新信號(hào)又作了一次分頻。
- 死區(qū)生成電路與三相無(wú)刷電機(jī)
??圖中的DTG和DTG寄存器組成死區(qū)生成電路,右側(cè)的引腳TIMx_CH1/CH2/CH3由原來(lái)的每路一個(gè)變成了兩個(gè)互補(bǔ)的輸出引腳(TIMx_CH1/CH2/CH3和TIMx_CH1N/CH2N/CH3N),可以輸出一對(duì)互補(bǔ)的PWM波。這些電路是為了驅(qū)動(dòng)三相無(wú)刷電機(jī)設(shè)計(jì)的。在四軸飛行器、電動(dòng)車(chē)后輪、電鉆中都可以發(fā)現(xiàn)三相無(wú)刷電機(jī)。三相無(wú)刷電機(jī)的驅(qū)動(dòng)電路需要三個(gè)橋臂,每個(gè)橋臂需要2個(gè)大功率開(kāi)關(guān)管來(lái)控制,總共需要6個(gè)大功率開(kāi)關(guān)管控制。所以輸出的PWM引腳的前三路就變?yōu)榱嘶パa(bǔ)的輸出引腳,而第四路TIMx_CH4沒(méi)有變化。
??為了防止互補(bǔ)輸出的PWM驅(qū)動(dòng)橋臂時(shí),在開(kāi)關(guān)切換的瞬間,由于器件的不理想,造成短暫的直通現(xiàn)象,故添加了死區(qū)生成電路。在開(kāi)關(guān)切換的瞬間,產(chǎn)生一定時(shí)長(zhǎng)的死區(qū),讓橋臂的上下管全部關(guān)斷,防止出現(xiàn)直通現(xiàn)象。
- 剎車(chē)輸入
??剎車(chē)輸入的主要作用是給電機(jī)驅(qū)動(dòng)提供安全保障。如果外部引腳BKIN(Break In)產(chǎn)生了剎車(chē)信號(hào),或者內(nèi)部時(shí)鐘失效,產(chǎn)生了故障,控制電路就會(huì)自動(dòng)切斷電機(jī)的輸出,防止意外的發(fā)生。
二、定時(shí)中斷和內(nèi)外時(shí)鐘源選擇
2.1 定時(shí)中斷的基本結(jié)構(gòu)
??定時(shí)中斷的基本結(jié)構(gòu)如下圖所示:
??在定時(shí)器中最核心的部分是時(shí)基單元。圖中的“運(yùn)行控制”就是控制寄存器的一些位,用來(lái)啟動(dòng)或停止計(jì)數(shù)器,配置向上向下計(jì)數(shù)方式等,操作這些寄存器就能控制時(shí)基單元的運(yùn)行了。關(guān)于時(shí)鐘源選擇的部分,在上文都有詳細(xì)的敘述,這里不再贅述。
??計(jì)時(shí)時(shí)間到后,產(chǎn)生的中斷信號(hào)會(huì)先在狀態(tài)寄存器中置一個(gè)中斷標(biāo)志位,這個(gè)標(biāo)志位會(huì)通過(guò)中斷輸出控制,到NVIC申請(qǐng)中斷。這個(gè)中斷輸出控制存在的原因是:定時(shí)器模塊有很多地方都要申請(qǐng)中斷,如果需要該中斷,就允許輸出;如果不需要這個(gè)中斷,就禁止輸出。簡(jiǎn)單來(lái)說(shuō),中斷輸出控制就是中斷輸出的允許位。
2.2 時(shí)基單元運(yùn)行時(shí)序舉例
??STM32中,關(guān)于時(shí)序運(yùn)行的內(nèi)容很多,具體請(qǐng)見(jiàn)手冊(cè)的詳細(xì)討論,這里僅舉一些時(shí)基單元的例子作簡(jiǎn)要分析。
2.2.1 緩沖(影子)寄存器
??STM32在設(shè)計(jì)之初,為了保證能適用于多種多樣的情況,故對(duì)時(shí)序運(yùn)行過(guò)程中突然手動(dòng)更改寄存器對(duì)時(shí)序的影響作了嚴(yán)謹(jǐn)?shù)脑O(shè)計(jì)。這里引入緩沖(影子)寄存器,主要目的就是同步,即可以讓寄存器設(shè)定的某些目標(biāo)值的變化和更新事件同時(shí)發(fā)生,防止在運(yùn)行途中更改造成錯(cuò)誤。在定時(shí)器結(jié)構(gòu)圖中,有些寄存器的畫(huà)法采用了方框下加陰影的方式,就說(shuō)明該寄存器不是只有一個(gè)寄存器,而是有兩個(gè)寄存器來(lái)形成緩沖機(jī)制。實(shí)際上,真正使時(shí)序電路狀態(tài)發(fā)生更改的都是影子寄存器。
2.2.2 預(yù)分頻器時(shí)序分析
計(jì)數(shù)器計(jì)數(shù)頻率:CK_CNT = CK_PSC / (PSC + 1)
??上圖描述了當(dāng)預(yù)分頻器的分頻系數(shù)從1變?yōu)?時(shí),計(jì)數(shù)器的時(shí)序圖。當(dāng)計(jì)數(shù)器使能信號(hào)CNT_EN變?yōu)楦唠娖胶蟮南乱粋€(gè)CK_PSC的高電平,定時(shí)器時(shí)鐘CK_CNT接收CK_PSC。且此時(shí)預(yù)分頻器的分頻系數(shù)為1,PSC = 0,預(yù)分頻器完成一分頻,CK_PSC = CK_CNT。
??可以看到,當(dāng)計(jì)數(shù)器寄存器的值依次遞增達(dá)到0xFC后立即跳變?yōu)?x00,說(shuō)明重裝載寄存器ARR設(shè)計(jì)的目標(biāo)計(jì)數(shù)值就是0xFC,此時(shí)電路產(chǎn)生一個(gè)更新事件脈沖信號(hào)UEV,并產(chǎn)生中斷信號(hào)。
??在更新事件信號(hào)之前在TIMx_PSC中寫(xiě)入新數(shù)值,將預(yù)分頻器的分頻系數(shù)從1改為2,但是由于緩沖寄存器的存在,CK_CNT不會(huì)立即變?yōu)镃K_PSC / 2,而是在下一次更新中斷產(chǎn)生的同時(shí),由預(yù)分頻緩沖器(影子寄存器)修改分頻系數(shù)為2,PSC = 1。
??由預(yù)分頻計(jì)數(shù)器時(shí)序可以看到,預(yù)分頻的分頻功能實(shí)際上也是通過(guò)計(jì)數(shù)器來(lái)實(shí)現(xiàn)的。當(dāng)分頻系數(shù)變?yōu)?后,預(yù)分頻計(jì)數(shù)器按0、1、0、1依次計(jì)數(shù),每當(dāng)預(yù)分頻計(jì)數(shù)器回到0時(shí),預(yù)分頻器輸出信號(hào)CN_CNT輸出一個(gè)脈沖。
2.2.3 計(jì)數(shù)器時(shí)序分析
- 計(jì)數(shù)器工作時(shí)序圖
計(jì)數(shù)器溢出頻率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
- 計(jì)數(shù)器無(wú)預(yù)裝時(shí)序圖(緩沖機(jī)制失效 APRE = 0)
??如圖所示,當(dāng)計(jì)數(shù)器沒(méi)有預(yù)裝(緩沖機(jī)制)時(shí),更改TIMx_ARR寄存器的值還有一種情況:當(dāng)更改自動(dòng)加載寄存器的值時(shí),計(jì)數(shù)器寄存器的值已經(jīng)大于了更改后的值;但是此時(shí)計(jì)數(shù)器寄存器的值只能遞增,故該寄存器會(huì)一直遞增到最大值0xFFFF之后回到0x0000,再依次遞增,直到計(jì)數(shù)器寄存器的值與ARR寄存器的值相同時(shí)申請(qǐng)中斷。這里就可以看出,如果不使用緩沖機(jī)制,可能會(huì)給電路時(shí)序的工作造成一些問(wèn)題。 - 計(jì)數(shù)器有預(yù)裝時(shí)序(緩沖機(jī)制有效 APRE = 1)
2.2.4 RCC時(shí)鐘樹(shù)簡(jiǎn)介
??RCC時(shí)鐘樹(shù):在STM32中用來(lái)產(chǎn)生和配置時(shí)鐘,并且把配置好的各個(gè)外設(shè)都發(fā)射到各個(gè)外設(shè)的系統(tǒng)。 時(shí)鐘是所有外設(shè)運(yùn)行的基礎(chǔ),所以時(shí)鐘是最先配置的東西。在程序執(zhí)行時(shí),在執(zhí)行主程序之前還會(huì)執(zhí)行一個(gè)SystemInit
函數(shù)(詳見(jiàn):STM32學(xué)習(xí)筆記(一)丨建立工程丨GPIO 通用輸入輸出),這個(gè)函數(shù)的作用就是配置RCC時(shí)鐘樹(shù)。
??RCC時(shí)鐘樹(shù)可以分為兩部分:時(shí)鐘產(chǎn)生電路和時(shí)鐘分配電路。
- 時(shí)鐘產(chǎn)生電路
??在時(shí)鐘產(chǎn)生電路,有四個(gè)振蕩源,分別是內(nèi)部的8MHz高速RC振蕩器、外部的4-16MHz高速晶振振蕩器(一般都外接8MHz)、外部的32.768kHz低速晶振振蕩器(一般給RTC提供時(shí)鐘)、內(nèi)部的40kHz低速RC振蕩器(給看門(mén)狗WDG提供時(shí)鐘)。外部的石英振蕩器比內(nèi)部的RC振蕩器更加穩(wěn)定。如果系統(tǒng)非常簡(jiǎn)單,且不需要過(guò)于精確的時(shí)鐘,就可以使用內(nèi)部的RC振蕩器,這樣可以省下外部的晶振電路。
??在SystemInit
函數(shù)中是這樣來(lái)配置時(shí)鐘的:首先會(huì)啟動(dòng)內(nèi)部的8MHz高速RC振蕩器產(chǎn)生時(shí)鐘,選擇該時(shí)鐘為系統(tǒng)時(shí)鐘,暫時(shí)以8MHz的內(nèi)部時(shí)鐘運(yùn)行;然后再啟動(dòng)外部時(shí)鐘,配置外部時(shí)鐘信號(hào)流經(jīng)如下圖所示的電路:
外部晶振信號(hào)進(jìn)入PLLMUL鎖相環(huán)進(jìn)行倍頻,8MHz倍頻9倍,得到72MHz,待鎖相環(huán)輸出穩(wěn)定后,選擇鎖相環(huán)輸出為系統(tǒng)時(shí)鐘。這樣就把系統(tǒng)時(shí)鐘從8MHz切換為了72MHz。這樣分析可以得出一個(gè)結(jié)論:如果外部晶振出問(wèn)題,可能會(huì)出現(xiàn)程序時(shí)鐘慢大概10倍的現(xiàn)象。如果外部時(shí)鐘的硬件電路有問(wèn)題(晶振短路或連接錯(cuò)誤等),系統(tǒng)的時(shí)鐘就無(wú)法切換到72MHz,會(huì)保持內(nèi)部的8MHz運(yùn)行。
??圖中的CSS稱(chēng)為時(shí)鐘安全系統(tǒng),它同樣負(fù)責(zé)切換時(shí)鐘。CSS可以檢測(cè)時(shí)鐘的運(yùn)行狀態(tài),一旦外部時(shí)鐘失效,它就會(huì)自動(dòng)把外部時(shí)鐘切換為內(nèi)部時(shí)鐘,從而保證程序可以正常運(yùn)行,不會(huì)卡死造成事故。另外在高級(jí)定時(shí)器的剎車(chē)輸入功能中,CSS同樣負(fù)責(zé)檢測(cè)當(dāng)外部時(shí)鐘失效時(shí),立即切斷輸出控制引腳,切斷電機(jī)輸出,防止發(fā)生意外。- 時(shí)鐘分配電路
??時(shí)鐘產(chǎn)生電路產(chǎn)生的之中信號(hào)SYSCLK(72MHz)首先進(jìn)入AHB總線,在AHB總線上有一個(gè)預(yù)分頻器,在SystemInit
函數(shù)配置的默認(rèn)分頻系數(shù)為1,所以AHB總線的時(shí)鐘自然是72MHz。
??之后信號(hào)進(jìn)入APB1總線,APB1上同樣有預(yù)分頻器,這里SystemInit
默認(rèn)配置的分頻系數(shù)為2,輸出為36MHz,所以APB1總線的時(shí)鐘為36MHz。通用定時(shí)器和基本定時(shí)器是接在APB1上的,但是APB1(APB2同理)連接定時(shí)器還有如圖所示的以下結(jié)構(gòu):
通用定時(shí)器和基本定時(shí)器通過(guò)圖中APB1下方的支路與APB1連接。由于APB1的預(yù)分頻系數(shù)默認(rèn)為2,則輸出到定時(shí)器的時(shí)鐘頻率×2。APB2的預(yù)分頻器的分頻系數(shù)默認(rèn)配置為1,其他其他流程與APB1同理。所以基本定時(shí)器,通用定時(shí)器,高級(jí)定時(shí)器的內(nèi)部基準(zhǔn)時(shí)鐘都是72MHz,這樣設(shè)計(jì)為我們使用定時(shí)器帶來(lái)了方便,不用考慮不同定時(shí)器時(shí)鐘不同的問(wèn)題了(前提是不亂修改SystemInit
函數(shù)中的默認(rèn)配置)。
??在時(shí)鐘輸出端口,都有一個(gè)與門(mén)進(jìn)行時(shí)鐘輸出控制。控制端外部時(shí)鐘使能就是程序中RCC_APB2/1PeriphClockCmd
函數(shù)作用的地方。
2.3 定時(shí)中斷和時(shí)鐘源選擇相關(guān)庫(kù)函數(shù)使用
??定時(shí)器相關(guān)的庫(kù)函數(shù)非常多,本節(jié)僅對(duì)將要使用的庫(kù)函數(shù)和 億些使用細(xì)節(jié) 進(jìn)行說(shuō)明(即使這樣也還是很多)。
- 定時(shí)器初始化配置
- 時(shí)基單元配置函數(shù)
// 恢復(fù)定時(shí)器缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);
// 時(shí)基單元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 把時(shí)基單元初始化函數(shù)所用的結(jié)構(gòu)體變量賦一個(gè)默認(rèn)值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 使能計(jì)數(shù)器(對(duì)應(yīng)定時(shí)中斷結(jié)構(gòu)圖中的“運(yùn)行控制”功能)
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
// 使能中斷輸出信號(hào)(對(duì)應(yīng)定時(shí)中斷結(jié)構(gòu)圖中的“中斷輸出控制”功能)
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
- 時(shí)基單元的時(shí)鐘源選擇配置函數(shù)
// 時(shí)基單元的時(shí)鐘選擇相關(guān)函數(shù)
// 選擇內(nèi)部時(shí)鐘
TIM_InternalClockConfig(TIM_TypeDef* TIMx);
// 選擇ITRx其他定時(shí)器的時(shí)鐘,TIM_InputTriggerSource為選擇要接入哪個(gè)定時(shí)器
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
// 選擇TIx捕獲通道的時(shí)鐘,TIM_InputTriggerSource為選擇的引腳,TIM_ICPolarity為輸入極性選擇,ICFilter為濾波配置
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
// 選擇ETR外部時(shí)鐘模式1輸入的時(shí)鐘,TIM_ExtTRGPrescaler為ETR外部時(shí)鐘預(yù)分頻器,TIM_ExtTRGPolarity為輸入極性選擇,ExtTRGFilter為濾波配置
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
// 選擇ETR外部時(shí)鐘模式1輸入的時(shí)鐘,參數(shù)與上一個(gè)函數(shù)完全相同,且對(duì)于ETR外部時(shí)鐘輸入而言,兩個(gè)函數(shù)等效,如果不需要觸發(fā)輸入的功能,則兩個(gè)函數(shù)可以互換
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// 單獨(dú)配置ETR外部引腳的預(yù)分頻器,極性,濾波參數(shù)
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
- 參數(shù)(PSC、ARR等)更改函數(shù)(在程序運(yùn)行過(guò)程中修改)
// 預(yù)分頻值設(shè)置,TIM_PSCReloadMode為是否應(yīng)用輸入緩沖功能配置
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
// 改變計(jì)數(shù)器的計(jì)數(shù)模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
// 自動(dòng)重裝寄存器預(yù)裝功能配置(計(jì)數(shù)器有無(wú)預(yù)裝功能)
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
// 手動(dòng)給計(jì)數(shù)器寫(xiě)入一個(gè)值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
// 手動(dòng)給自動(dòng)重裝寄存器寫(xiě)入一個(gè)值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
// 獲取當(dāng)前計(jì)數(shù)器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
// 獲取當(dāng)前的預(yù)分頻器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
// 獲取定時(shí)中斷的標(biāo)志位和清除標(biāo)志位,使用方法與EXTI相同
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);
- 使用定時(shí)器庫(kù)函數(shù)的一些細(xì)節(jié)
-
選擇內(nèi)部時(shí)鐘函數(shù):定時(shí)器上電后默認(rèn)選擇內(nèi)部時(shí)鐘,如果要選擇內(nèi)部時(shí)鐘,這一句可以省略。
TIM_InternalClockConfig(TIM_TypeDef* TIMx);
-
時(shí)基單元初始化函數(shù)
TIN_TimeBaseInit
:在配置結(jié)構(gòu)體變量時(shí),會(huì)遇到以下幾個(gè)細(xì)節(jié)問(wèn)題
TIM_TimeBaseInitStructure.TIM_ClockDivision
(采樣)時(shí)鐘分頻頻率選擇
??在定時(shí)器的外部信號(hào)輸入引腳一般都有一個(gè)濾波器來(lái)消除信號(hào)的抖動(dòng)干擾,它的工作原理是:在一個(gè)固定的時(shí)鐘頻率 f f f下進(jìn)行采樣,如果連續(xù) N N N個(gè)采樣點(diǎn)都是相同的電平,就代表輸入信號(hào)穩(wěn)定了,就將采樣值輸出到下一級(jí)電路;如果 N N N個(gè)采樣點(diǎn)不全都相同,就說(shuō)明信號(hào)有抖動(dòng),這時(shí)保持上一次的輸出,或直接輸出低電平。 這樣就能保證輸出信號(hào)在一定程度上的濾波。這里的采樣頻率 f f f和采樣點(diǎn)數(shù) N N N都是濾波器的參數(shù),頻率越低,采樣點(diǎn)數(shù)越多,濾波效果就越好,不過(guò)相應(yīng)的信號(hào)延遲就越大。
??采樣頻率 f f f的來(lái)源可以是內(nèi)部時(shí)鐘直接提供,也可以是內(nèi)部時(shí)鐘加一個(gè)時(shí)鐘分頻而來(lái)。 分頻是多少,就由參數(shù)TIM_ClockDivision
決定。可見(jiàn)TIM_ClockDivision
與時(shí)基單元的關(guān)系并不大,它的可選值可以選擇1分頻,2分頻和4分頻。- 在配置結(jié)構(gòu)體變量時(shí),并沒(méi)有能直接操作計(jì)數(shù)器CNT的參數(shù)。如果需要,可以采用
SetCounter
和GetCounter
兩個(gè)函數(shù)來(lái)操作計(jì)數(shù)器。TIM_TimeBaseInitStructure.TIM_RepetitionCounter
重復(fù)計(jì)數(shù)寄存器,通過(guò)這個(gè)參數(shù)可以設(shè)置重復(fù)計(jì)數(shù)寄存器。但是通用定時(shí)器中沒(méi)有這一個(gè)寄存器,故可以直接設(shè)置為0。- 定時(shí)時(shí)間的計(jì)算
??參考公式: 計(jì)數(shù)器溢出頻率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1),注意PSC(TIM_Prescaler
)和ARR(TIM_Period
)的取值都要在0~65535之間。- 在
TIM_TimeBaseInit
函數(shù)的最后,會(huì)立刻生成一個(gè)更新事件,來(lái)重新裝載預(yù)分頻器和重復(fù)計(jì)數(shù)器的值。預(yù)分頻器有緩沖寄存器,我們寫(xiě)入的PSC和ARR只有在更新事件時(shí)才會(huì)起作用。但是更新事件和更新中斷是同時(shí)發(fā)生的,更新中斷會(huì)置更新中斷標(biāo)志位,手動(dòng)生成一個(gè)更新事件,就相當(dāng)于在初始化時(shí)立刻進(jìn)入更新函數(shù)執(zhí)行一次,在開(kāi)啟中斷之前手動(dòng)清除一次更新中斷標(biāo)志位,就可以避免剛初始化完成就進(jìn)入中斷函數(shù)的問(wèn)題。
- 外部時(shí)鐘配置函數(shù)
TIM_ETRClockMode2Config
TIM_ExtTRGPrescaler
外部時(shí)鐘預(yù)分頻器:可以選擇外部時(shí)鐘分頻關(guān)閉(1分頻)、2分頻、4分頻、8分頻。TIM_ExtTRGPolarity
外部觸發(fā)的極性:TIM_ExtTRGPolarity_Inverted
為反向極性,即低電平和下降沿有效;TIM_ExtTRGPolarity_NonInverted
為不反向,即高電平和上升沿有效。ExtTRGFilter
外部輸入濾波器:工作原理與內(nèi)部時(shí)鐘的濾波器相似,它的值可以是0x00到0x0F之間的一個(gè)值,其決定了采樣的 f f f和 N N N,具體的對(duì)應(yīng)關(guān)系在手冊(cè)中有對(duì)應(yīng)表:![]()
- GPIO配置:因?yàn)槭鞘褂猛獠拷涌谳斎霑r(shí)鐘,故在使用該函數(shù)之前還需要配置GPIO端口。對(duì)于定時(shí)器,手冊(cè)中給的推薦配置是浮空輸入。但是浮空輸入會(huì)導(dǎo)致引腳的輸入電平極易受干擾,所以輸入信號(hào)的功率不小時(shí)一般選擇上拉或下拉輸入。當(dāng)外部的輸入信號(hào)功率很小,內(nèi)部的上拉/下拉電阻(較大)可能會(huì)影響到這個(gè)輸入信號(hào),這時(shí)就需要用浮空輸入,防止影響外部輸入的電平。
![]()
2.4 定時(shí)器定時(shí)中斷實(shí)例
??本次實(shí)驗(yàn)要完成的現(xiàn)象是:定義一個(gè) uint16_t 的 Num 變量,使其每秒+1。器件連接圖和程序源碼如下所示:
Timer.c
#include "stm32f10x.h" // Device header
// 用extern聲明變量后,這里的Num就會(huì)成為main.c文件中Num的引用
extern uint16_t Num;
void Timer_Init(void)
{
// 用RCC外設(shè)時(shí)鐘控制打開(kāi)定時(shí)器的基準(zhǔn)時(shí)鐘和外設(shè)的工作時(shí)鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 選擇時(shí)基單元的時(shí)鐘源(這里使用內(nèi)部時(shí)鐘)
// 定時(shí)器上電后默認(rèn)使用內(nèi)部時(shí)鐘,如果使用內(nèi)部時(shí)鐘這一步也可以省略
TIM_InternalClockConfig(TIM2);
// 配置時(shí)基單元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 配置時(shí)鐘分頻頻率(用于采樣濾波,在這里它的取值不重要,取哪一個(gè)都可以)
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 配置計(jì)數(shù)模式
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // 周期,即自動(dòng)重裝寄存器的值A(chǔ)RR
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // 預(yù)分頻系數(shù)PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重復(fù)計(jì)數(shù)寄存器,高級(jí)定時(shí)器才有的模塊,這里配置為0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 在TIM_TimeBaseInit函數(shù)的最后,會(huì)立刻生成一個(gè)更新事件,來(lái)重新裝載預(yù)分頻器和重復(fù)計(jì)數(shù)器的值
// 預(yù)分頻器有緩沖寄存器,我們寫(xiě)入的PSC和ARR只有在更新事件時(shí)才會(huì)起作用
// 為了讓寫(xiě)入的值立刻起作用,故在函數(shù)的最后手動(dòng)生成了一個(gè)更新事件
// 但是更新事件和更新中斷是同時(shí)發(fā)生的,更新中斷會(huì)置更新中斷標(biāo)志位,手動(dòng)生成一個(gè)更新事件,就相當(dāng)于在初始化時(shí)立刻進(jìn)入更新函數(shù)執(zhí)行一次
// 在開(kāi)啟中斷之前手動(dòng)清除一次更新中斷標(biāo)志位,就可以避免剛初始化完成就進(jìn)入中斷函數(shù)的問(wèn)題
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
// 配置中斷輸出控制,允許中斷輸出到NVIC
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 配置NVIC,在NVIC中打開(kāi)定時(shí)器中斷的通道,并分配優(yōu)先級(jí)
// NVIC通道優(yōu)先級(jí)分組
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 初始化NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 配置NVIC中斷通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 中斷通道命令
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 響應(yīng)優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 搶占優(yōu)先級(jí)
NVIC_Init(&NVIC_InitStructure);
// 配置運(yùn)行控制
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main()
{
OLED_Init();
Timer_Init();
OLED_ShowString(1, 1, "Num:"); // 顯示一個(gè)字符串
while(1)
{
OLED_ShowNum(1, 5, Num, 5);
// OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);
}
}
2.5 定時(shí)器外部時(shí)鐘選擇
??本次實(shí)驗(yàn)要完成的現(xiàn)象是:用光敏傳感器手動(dòng)模擬一個(gè)外部時(shí)鐘,定義一個(gè) uint16_t 的 Num 變量,當(dāng)外部時(shí)鐘觸發(fā)10次(預(yù)分頻之后的脈沖)后Num + 1。器件連接圖和程序源碼如下所示:
Timer.c
#include "stm32f10x.h" // Device header
// 用extern聲明變量后,這里的Num就會(huì)成為main.c文件中Num的引用
extern uint16_t Num;
void Timer_Init(void)
{
// 用RCC外設(shè)時(shí)鐘控制打開(kāi)定時(shí)器的基準(zhǔn)時(shí)鐘和外設(shè)的工作時(shí)鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// RCC打開(kāi)GPIO的時(shí)鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
// 選擇時(shí)基單元的時(shí)鐘源(使用ETR外部時(shí)鐘)
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
// 配置時(shí)基單元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 配置時(shí)鐘分頻頻率(用于采樣濾波,在這里它的取值不重要,取哪一個(gè)都可以)
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 配置計(jì)數(shù)模式
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; // 周期,即自動(dòng)重裝寄存器的值A(chǔ)RR
TIM_TimeBaseInitStructure.TIM_Prescaler = 2 - 1; // 預(yù)分頻系數(shù)PSC(這里如果PSC設(shè)置為0,會(huì)連續(xù)觸發(fā)中斷,導(dǎo)致主程序不運(yùn)行,是我的芯片Bug問(wèn)題)
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重復(fù)計(jì)數(shù)寄存器,高級(jí)定時(shí)器才有的模塊,這里配置為0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 在TIM_TimeBaseInit函數(shù)的最后,會(huì)立刻生成一個(gè)更新事件,來(lái)重新裝載預(yù)分頻器和重復(fù)計(jì)數(shù)器的值
// 預(yù)分頻器有緩沖寄存器,我們寫(xiě)入的PSC和ARR只有在更新事件時(shí)才會(huì)起作用
// 為了讓寫(xiě)入的值立刻起作用,故在函數(shù)的最后手動(dòng)生成了一個(gè)更新事件
// 但是更新事件和更新中斷是同時(shí)發(fā)生的,更新中斷會(huì)置更新中斷標(biāo)志位,手動(dòng)生成一個(gè)更新事件,就相當(dāng)于在初始化時(shí)立刻進(jìn)入更新函數(shù)執(zhí)行一次
// 在開(kāi)啟中斷之前手動(dòng)清除一次更新中斷標(biāo)志位,就可以避免剛初始化完成就進(jìn)入中斷函數(shù)的問(wèn)題
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
// 配置中斷輸出控制,允許中斷輸出到NVIC
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 配置NVIC,在NVIC中打開(kāi)定時(shí)器中斷的通道,并分配優(yōu)先級(jí)
// NVIC通道優(yōu)先級(jí)分組
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 初始化NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 配置NVIC中斷通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 中斷通道命令
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 響應(yīng)優(yōu)先級(jí)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 搶占優(yōu)先級(jí)
NVIC_Init(&NVIC_InitStructure);
// 配置運(yùn)行控制
TIM_Cmd(TIM2, ENABLE);
}
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main()
{
OLED_Init();
Timer_Init();
OLED_ShowString(1, 1, "Num:"); // 顯示一個(gè)字符串
OLED_ShowString(2, 1, "CNT:");
while(1)
{
OLED_ShowNum(1, 5, Num, 5);
OLED_ShowNum(2, 5, Timer_GetCounter(), 5);
}
}
??下一篇:STM32學(xué)習(xí)筆記(五)丨TIM定時(shí)器及其應(yīng)用(輸出比較丨PWM驅(qū)動(dòng)呼吸燈、舵機(jī)、直流電機(jī))文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-412470.html
??原創(chuàng)筆記,碼字不易,歡迎點(diǎn)贊,收藏~ 如有謬誤敬請(qǐng)?jiān)谠u(píng)論區(qū)不吝告知,感激不盡!博主將持續(xù)更新有關(guān)嵌入式開(kāi)發(fā)、機(jī)器學(xué)習(xí)方面的學(xué)習(xí)筆記~文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-412470.html
到了這里,關(guān)于STM32學(xué)習(xí)筆記(四)丨TIM定時(shí)器及其應(yīng)用(定時(shí)中斷、內(nèi)外時(shí)鐘源選擇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!