開發(fā)環(huán)境:
MDK:Keil 5.30
開發(fā)板:GD32F207I-EVAL
MCU:GD32F207IK
1 定時(shí)器的工作原理概述
系統(tǒng)滴答定時(shí)器一般用來提供“心跳”作用,而GD32定時(shí)器最基本功能也是定時(shí),可以設(shè)置不同時(shí)間長(zhǎng)度的定時(shí)。定時(shí)器除了最基本的定時(shí)功能外,定時(shí)器與GPIO有掛鉤使得它可以發(fā)揮強(qiáng)大的作用,比如可以輸出不同頻率、不同占空比的方波信號(hào)、PWM信號(hào),同時(shí)做為輸入捕獲功能時(shí),可以測(cè)量脈沖寬度、實(shí)現(xiàn)電容按鍵檢測(cè)等等。
GD32有三類定時(shí)器,基本定時(shí)器就是單純的定時(shí)計(jì)數(shù)器,通用定時(shí)器多了四個(gè)通道,相對(duì)應(yīng)的增加了功能,高級(jí)定時(shí)器具有基本,通用定時(shí)器的所有的功能,并且添加了其他功能。定時(shí)器的對(duì)比特性如下表所示。
定時(shí)器 | 定時(shí)器****0/7 | 定時(shí)器****1/2/3/4 | 定時(shí)器****8/11 | 定時(shí)器****9/10/12/13 | 定時(shí)器****5/6 |
---|---|---|---|---|---|
類型 | 高級(jí) | 通用(L0) | 通用(L1) | 通用(L2) | 基本 |
預(yù)分頻器 | 16位 | 16位 | 16位 | 16位 | 16位 |
計(jì)數(shù)器 | 16位 | 16位 | 16位 | 16位 | 16位 |
計(jì)數(shù)模式 | 向上, 向下, 中央對(duì)齊 | 向上, 向下, 中央對(duì)齊 | 向上, 向下, 中央對(duì)齊 | 向上, 向下, 中央對(duì)齊 | 向上 |
1.1 基本定時(shí)器
TIMER5和TIMER6定時(shí)器的主要功能包括:
● 16位自動(dòng)重裝載累加計(jì)數(shù)器
● 16位可編程(可實(shí)時(shí)修改)預(yù)分頻器,用于對(duì)輸入的時(shí)鐘按系數(shù)為1 ~ 65536之間的任意數(shù)值分頻
● 時(shí)鐘源只有內(nèi)部時(shí)鐘
● 在更新事件(計(jì)數(shù)器溢出)時(shí)產(chǎn)生中斷/DMA請(qǐng)求
總的說來,基本定時(shí)器 TIMER5和TIMER6只具備最基本的定時(shí)功能,就是累加的時(shí)鐘脈沖數(shù)超過預(yù)定值時(shí),能觸發(fā)中斷或觸發(fā) DMA 請(qǐng)求。由于在芯片內(nèi)部與 DAC 外設(shè)相連,可通過觸發(fā)輸出驅(qū)動(dòng) DAC,也可以作為其他通用定時(shí)器的時(shí)鐘基準(zhǔn)。
這兩個(gè)基本定時(shí)器使用的時(shí)鐘源都是CK_TIMER驅(qū)動(dòng),時(shí)鐘源經(jīng)過 TIMERx_PSC預(yù)分頻器輸入至脈沖計(jì)數(shù)器TIMERx_CNT,基本定時(shí)器只能工作在向上計(jì)數(shù)模式,在重載寄存器TIMERx_CAR中保存的是定時(shí)器的溢出值。
工作時(shí),脈沖計(jì)數(shù)器TIMERx_CNT由時(shí)鐘觸發(fā)進(jìn)行計(jì)數(shù),當(dāng) TIMx_CNT 的計(jì)數(shù)值 X 等于重載寄存器TIMERx_CAR中保存的數(shù)值 N 時(shí),產(chǎn)生溢出事件,可觸發(fā)中斷或 DMA 請(qǐng)求。然后TIMERx_CNT的值重新被置為 0,重新向上計(jì)數(shù)。
1.2 通用定時(shí)器
通用TIMERx(TIMER1/ TIMER2/ TIMER3/ TIMER4/ TIMER8/ TIMER11/ TIMER9/ TIMER10/ TIMER12/ TIMER13)定時(shí)器功能包括:
● 16位向上、向下、向上/向下自動(dòng)裝載計(jì)數(shù)器;
● 16位可編程(可以實(shí)時(shí)修改)預(yù)分頻器,計(jì)數(shù)器時(shí)鐘頻率的分頻系數(shù)為1~65536之間的任意數(shù)值;
● 4個(gè)獨(dú)立通道:輸入捕獲,輸出比較,PWM生成(邊緣或中間對(duì)齊模式),單脈沖模式輸出;
● 使用外部信號(hào)控制定時(shí)器和定時(shí)器互連的同步電路;
● 如下事件發(fā)生時(shí)產(chǎn)生中斷/DMA:更新:計(jì)數(shù)器向上溢出/向下溢出,計(jì)數(shù)器初始化(通過軟件或者內(nèi)部/外部觸發(fā)), 觸發(fā)事件(計(jì)數(shù)器啟動(dòng)、停止、初始化或者由內(nèi)部/外部觸發(fā)計(jì)數(shù)),輸入捕獲,輸出比較;
● 支持針對(duì)定位的增量(正交)編碼器和霍爾傳感器電路;
● 時(shí)鐘源可選:內(nèi)部時(shí)鐘,內(nèi)部觸發(fā),外部輸入,外部觸發(fā);
相比之下,通用定時(shí)器就比基本定時(shí)器復(fù)雜得多了。除了基本的定時(shí),它主要用在測(cè)量輸入脈沖的頻率、脈沖寬與輸出 PWM 脈沖的場(chǎng)合,還具有編碼器的接口。
從時(shí)鐘源方面來說,通用定時(shí)器比基本定時(shí)器多了一個(gè)選擇,它可以使用外部脈沖作為定時(shí)器的時(shí)鐘源。使用外部時(shí)鐘源時(shí),要使用寄存器進(jìn)行觸發(fā)邊沿、濾波器帶寬的配置。如果選擇內(nèi)部時(shí)鐘源的話則與基本定時(shí)器一樣,也為CK_TIMER。但要注意的是,所有定時(shí)器(包括基本、通用和高級(jí))使用內(nèi)部時(shí)鐘時(shí),定時(shí)器的時(shí)鐘源都被稱為CK_TIMER,但CK_TIMER的時(shí)鐘來源并不是完全一樣的,見下圖。
基本定時(shí)器和部分通用定時(shí)器的時(shí)鐘來源是 APB1 預(yù)分頻器的輸出。當(dāng) APB1 的分頻系數(shù)為 1 時(shí),則CK_TIMER直接等于該APB1 預(yù)分頻器的輸出,而 APB1 的分頻系數(shù) 不 為 1 時(shí),CK_TIMER則為APB1 預(yù)分頻器輸出的 2 倍。
如在常見的配置中,AHB=120MHz,而 APB1 預(yù)分頻器的分頻系數(shù)被配置為2,則PCLK1 剛好達(dá)到最大值60MHz,而此時(shí)APB1的分頻系數(shù)不為 1,則CK_TIMER = (AHB/2) x 2 = 120MHz。
而對(duì)于部分通用定時(shí)器和高級(jí)定時(shí)器的時(shí)鐘來源則是 APB2 預(yù)分頻器的輸出,同樣它也根據(jù)分頻系數(shù)分為兩種情況。
常見的配置中 AHB=120MHz,APB2 預(yù)分頻器的分頻系數(shù)被配置為1,此時(shí)PCLK2剛好達(dá)到最大值120MHz,而CK_TIMER則直接等于APB2分頻器的輸出,即CK_TIMER的時(shí)鐘 CK_TIMER =AHB=120MHz。
雖然這種配置下最終CK_TIMER的時(shí)鐘頻率相等,但必須清楚實(shí)質(zhì)上它們的時(shí)鐘來源是有區(qū)別的。還要強(qiáng)調(diào)的是:CK_TIMER是定時(shí)器內(nèi)部的時(shí)鐘源,但在時(shí)鐘輸出到脈沖計(jì)數(shù)器 TIMERx_CNT 前,還經(jīng)過一個(gè)預(yù)分頻器TIMERx_PSC,最終用于驅(qū)動(dòng)脈沖計(jì)數(shù)器 TIMERx_CNT 的時(shí)鐘頻率根據(jù)預(yù)分頻器 TIMERx_PSC 的配置而定。
1.3 高級(jí)定時(shí)器
TIMER0和TIMER7定時(shí)器的功能包括:
● 16位向上、向下、向上/下自動(dòng)裝載計(jì)數(shù)器;
● 16位可編程(可以實(shí)時(shí)修改)預(yù)分頻器,計(jì)數(shù)器時(shí)鐘頻率的分頻系數(shù)為1 ~ 65535之間的任意數(shù)值;
● 多達(dá)4個(gè)獨(dú)立通道:輸入捕獲,輸出比較,PWM生成(邊緣或中間對(duì)齊模式),單脈沖模式輸出;
● 死區(qū)時(shí)間可編程的互補(bǔ)輸出;
● 使用外部信號(hào)控制定時(shí)器和定時(shí)器互聯(lián)的同步電路;
● 允許在指定數(shù)目的計(jì)數(shù)器周期之后更新定時(shí)器寄存器的重復(fù)計(jì)數(shù)器;
● 剎車輸入信號(hào)可以將定時(shí)器輸出信號(hào)置于復(fù)位狀態(tài)或者一個(gè)已知狀態(tài);
● 如下事件發(fā)生時(shí)產(chǎn)生中斷/DMA:更新:計(jì)數(shù)器向上溢出/向下溢出,計(jì)數(shù)器初始化(通過軟件或者內(nèi)部/外部觸發(fā)),觸發(fā)事件(計(jì)數(shù)器啟動(dòng)、停止、初始化或者由內(nèi)部/外部觸發(fā)計(jì)數(shù)),輸入捕獲,輸出比較,剎車信號(hào)輸入;
● 支持針對(duì)定位的增量(正交)編碼器和霍爾傳感器電路;
● 時(shí)鐘源可選:內(nèi)部時(shí)鐘,內(nèi)部觸發(fā),外部輸入,外部觸發(fā);
總的來說,TIMER0和TIMER7是兩個(gè)高級(jí)定時(shí)器,它們具有基本、通用定時(shí)器的所有功能,還具有三相 6 步電機(jī)的接口、剎車功能(break function)及用于 PWM 驅(qū)動(dòng)電路的死區(qū)時(shí)間控制等,使得它非常適合于電機(jī)的控制。如圖4 所示為高級(jí)定時(shí)器結(jié)構(gòu)。
相比于通用定時(shí)器,主要多出了BRK、DTG 兩個(gè)結(jié)構(gòu),因而具有了死區(qū)時(shí)間的控制功能。首先,死區(qū)時(shí)間是什么呢?在 H 橋、三相橋的 PWM 驅(qū)動(dòng)電路中,上下兩個(gè)橋臂的PWM 驅(qū)動(dòng)信號(hào)是互補(bǔ)的,即上下橋臂輪流導(dǎo)通,但實(shí)際上為了防止出現(xiàn)上下兩個(gè)臂同時(shí)導(dǎo)通(會(huì)造成短路),在上下兩臂切換時(shí)留一小段時(shí)間,上下臂都施加關(guān)斷信號(hào),這個(gè)上下臂都關(guān)斷的時(shí)間稱為死區(qū)時(shí)間。
高級(jí)定時(shí)器可以配置出輸出互補(bǔ)的 PWM 信號(hào),并且在這個(gè) PWM 信號(hào)中加入死區(qū)時(shí)間,為電機(jī)的控制提供了極大的便利。下圖中的 OCxREF 為參考信號(hào)(可理解為原信號(hào)),OCx_O和 OCx_ON 為定時(shí)器通過 GPIO 引腳輸出的 PWM 互補(bǔ)信號(hào)。
若不加入死區(qū)時(shí)間,當(dāng)OxCPRE出現(xiàn)下降沿,OCx_O同時(shí)輸出下降沿,OCx_ON 則同時(shí)輸出相反的上升沿,即這三個(gè)信號(hào)的跳變是同時(shí)的。
加入死區(qū)時(shí)間后,當(dāng) OxCPRE出現(xiàn)下降沿,OCx_O同時(shí)輸出下降沿,但 OCx_ON 則過了一小段延遲再輸出上升沿,OxCPRE出現(xiàn)上升沿后,OCx_O要經(jīng)過一段延時(shí)再輸出上升沿。假如 OCx_O、 OCx_ON 分別控制上、下橋臂,有了延遲后,就不容易出現(xiàn)上、下橋臂同時(shí)導(dǎo)通的情況。這個(gè)延遲時(shí)間與 PWM 信號(hào)驅(qū)動(dòng)的電子器件特性相關(guān),從事工控領(lǐng)域的朋友對(duì)此應(yīng)該比較熟悉。
2 定時(shí)器計(jì)數(shù)模式
定時(shí)器可以向上計(jì)數(shù)、向下計(jì)數(shù)、向上向下雙向計(jì)數(shù)模式。
- 向上計(jì)數(shù)模式:計(jì)數(shù)器從0計(jì)數(shù)到自動(dòng)加載值(TIMERx_CAR),然后重新從0開始計(jì)數(shù)并且產(chǎn)生一個(gè)計(jì)數(shù)器溢出事件。
- 向下計(jì)數(shù)模式:計(jì)數(shù)器從自動(dòng)裝入的值(TIMERx_CAR)開始向下計(jì)數(shù)到0,然后從自動(dòng)裝入的值重新開始,并產(chǎn)生一個(gè)計(jì)數(shù)器向下溢出事件。
- 中央對(duì)齊模式(向上/向下計(jì)數(shù)):計(jì)數(shù)器從0開始計(jì)數(shù)到自動(dòng)裝入的值-1,產(chǎn)生一個(gè)計(jì)數(shù)器溢出事件,然后向下計(jì)數(shù)到1并且產(chǎn)生一個(gè)計(jì)數(shù)器溢出事件;然后再?gòu)?開始重新計(jì)數(shù)。
簡(jiǎn)單地理解三種計(jì)數(shù)模式,可以通過下面的圖形:
圖6定時(shí)器計(jì)數(shù)模式
計(jì)數(shù)器時(shí)鐘可由下列時(shí)鐘源提供:
- 內(nèi)部時(shí)鐘(CK_TIMER);
- 外部時(shí)鐘模式0:定時(shí)器選擇外部輸入引腳作為時(shí)鐘源;
- 外部時(shí)鐘模式1:定時(shí)器選擇外部輸入引腳ETI作為時(shí)鐘源;
3 定時(shí)器的寄存器分析
為了深入了解 GD32 的通用寄存器,下面我們先介紹一下與我們這章的實(shí)驗(yàn)密切相關(guān)的幾個(gè)通用定時(shí)器的寄存器。首先是控制寄存器0(TIMERx_CTL0),該寄存器的各位描述如下圖。
首先我們來看看TIMERx_CTL0的最低位,也就是計(jì)數(shù)器使能位,該位必須置1,才能讓定時(shí)器開始計(jì)數(shù)。 從第 4 位 DIR 可以看出默認(rèn)的計(jì)數(shù)方式是向上計(jì)數(shù), 同時(shí)也可以向下計(jì)數(shù),第 5,6位是設(shè)置計(jì)數(shù)對(duì)齊方式的。從第 8 和第 9 位可以看出,我們還可以設(shè)置定時(shí)器的時(shí)鐘分頻因子為1,2,4。
接下來介紹第二個(gè)與我們這章密切相關(guān)的寄存器:DMA/中斷使能寄存器(TIMERx_DMAINTEN)。該寄存器是一個(gè) 16 位的寄存器,其各位描述如下圖所示。
這里我們同樣僅關(guān)心它的第 0 位,該位是更新中斷允許位,本章用到的是定時(shí)器的更新中斷,所以該位要設(shè)置為1。
接下來我們看第三個(gè)與我們這章有關(guān)的寄存器:預(yù)分頻寄存器(TIMERx_PSC)。該寄存器用設(shè)置對(duì)時(shí)鐘進(jìn)行分頻,然后提供給計(jì)數(shù)器,作為計(jì)數(shù)器的時(shí)鐘。該寄存器的各位描述如下圖。
這里順帶介紹一下TIMERx_CNT 寄存器,該寄存器是定時(shí)器的計(jì)數(shù)器,該寄存器存儲(chǔ)了當(dāng)前定時(shí)器的計(jì)數(shù)值。
接著我們介紹計(jì)數(shù)器自動(dòng)重載寄存器(TIMERx_CAR),該寄存器在物理上實(shí)際對(duì)應(yīng)著 2 個(gè)寄存器。一個(gè)是程序員可以直接操作的,另外一個(gè)是程序員看不到的,這個(gè)看不到的寄存器在《GD32F20x_User_Manual_EN_Rev2.4》里面被叫做影子寄存器。事實(shí)上真正起作用的是影子寄存器。根據(jù)TIMERx_CTL0寄存器中ARSE位的設(shè)置:ARSE=0 時(shí),預(yù)裝載寄存器的內(nèi)容可以隨時(shí)傳送到影子寄存器,此時(shí)二者是連通的;而 ARSE=1 時(shí),在每一次更新事件時(shí),才把預(yù)裝在寄存器的內(nèi)容傳送到影子寄存器。自動(dòng)重裝載寄存器的各位描述如下圖。
最后,我們要介紹的寄存器是:中斷標(biāo)志寄存器(TIMERx_INTF)。該寄存器用來標(biāo)記當(dāng)前與定時(shí)器相關(guān)的各種事件/中斷是否發(fā)生。該寄存器的各位描述如下圖。
關(guān)于這些位的詳細(xì)描述,請(qǐng)參考《GD32F20x_User_Manual_EN_Rev2.4》。只要對(duì)以上幾個(gè)寄存器進(jìn)行簡(jiǎn)單的設(shè)置,我們就可以使用通用定時(shí)器了,并且可以產(chǎn)生中斷。這一章,我們將使用定時(shí)器產(chǎn)生中斷,然后在中斷服務(wù)函數(shù)里面翻轉(zhuǎn) DS1 上的電平,來指示定時(shí)器中斷的產(chǎn)生。
4 定時(shí)器代碼實(shí)現(xiàn)
接下來我們以通用定時(shí)器TIMER1為實(shí)例,來說明要經(jīng)過哪些步驟,才能達(dá)到這個(gè)要求,并產(chǎn)生中斷。
4.1 定時(shí)器配置步驟
這里我們就對(duì)每個(gè)步驟通過庫(kù)函數(shù)的實(shí)現(xiàn)方式來描述。首先要提到的是,定時(shí)器相關(guān)的庫(kù)函數(shù)主要集中在固件庫(kù)文件 gd32f20x_timer.h 和 gd32f20x_timer.c 文件中。
1) TIMER1時(shí)鐘使能。
TIMER1是掛載在 APB1 之下,所以我們通過 APB1 總線下的使能使能函數(shù)來使能 TIMER1。調(diào)用的函數(shù)是:
rcu_periph_clock_enable(RCU_TIMER1);
2) 初始化定時(shí)器參數(shù),設(shè)置自動(dòng)重裝值,分頻系數(shù),計(jì)數(shù)方式等。
在庫(kù)函數(shù)中,定時(shí)器的初始化參數(shù)是通過初始化函數(shù) timer_init實(shí)現(xiàn)的:
void timer_init(uint32_t timer_periph, timer_parameter_struct *initpara)
第一個(gè)參數(shù)是確定是哪個(gè)定時(shí)器,這個(gè)比較容易理解。
第二個(gè)參數(shù)是定時(shí)器初始化參數(shù)結(jié)構(gòu)體指針,結(jié)構(gòu)體類型為 timer_parameter_struct,下面我們看看這個(gè)結(jié)構(gòu)體的定義:
/* TIMER init parameter structure definitions */
typedef struct {
uint16_t prescaler; /*!< prescaler value */
uint16_t alignedmode; /*!< aligned mode */
uint16_t counterdirection; /*!< counter direction */
uint32_t period; /*!< period value */
uint16_t clockdivision; /*!< clock division value */
uint8_t repetitioncounter; /*!< the counter repetition value */
} timer_parameter_struct;
這個(gè)結(jié)構(gòu)體一共有6個(gè)成員變量。
第一個(gè)參數(shù) prescaler 是用來設(shè)置分頻系數(shù)的,剛才上面有講解。
第二個(gè)參數(shù)alignedmode是對(duì)齊模式,分為邊沿對(duì)齊模式,中央對(duì)齊向下計(jì)數(shù)置1模式,中央對(duì)齊向上計(jì)數(shù)置1模式,中央對(duì)齊上下計(jì)數(shù)置1模式。
第三個(gè)參數(shù)counterdirection是計(jì)數(shù)方向,向上計(jì)數(shù)和向下計(jì)數(shù)。
第四個(gè)參數(shù)period是設(shè)置自動(dòng)重載計(jì)數(shù)周期值,這在前面也已經(jīng)講解過。
第五個(gè)參數(shù)clockdivision是用來設(shè)置時(shí)鐘分頻因子。
第六個(gè)參數(shù)repetitioncounter是重復(fù)計(jì)數(shù)器。
針對(duì) TIMER1初始化范例代碼格式:
timer_parameter_struct timer_initpara;
/* TIMER1 configuration */
timer_initpara.prescaler = 119;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 999;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER1, &timer_initpara);
3) 設(shè)置TIMERx_DMAINTEN允許更新中斷。
因?yàn)槲覀円褂?TIMER1的更新中斷, 寄存器的相應(yīng)位便可使能更新中斷。 在庫(kù)函數(shù)里面定時(shí)器中斷使能是通過 timer_interrupt_enable()函數(shù)來實(shí)現(xiàn)的:
void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt)
第一個(gè)參數(shù)是選擇定時(shí)器號(hào),這個(gè)容易理解,取值為TIMERx(x=0..13)。
第二個(gè)參數(shù)非常關(guān)鍵,是用來指明我們使能的定時(shí)器中斷的類型,定時(shí)器中斷的類型有很多種,包括TIMER_INT_UP, TIMER_INT_TRG等等。
例如我們要使能 TIMER1 的更新中斷,格式為:
timer_interrupt_enable(TIMER1,TIMER_INT_UP);
4) TIMER1中斷優(yōu)先級(jí)設(shè)置。
在定時(shí)器中斷使能之后,因?yàn)橐a(chǎn)生中斷,必不可少的要設(shè)置 NVIC 相關(guān)寄存器,設(shè)置中斷優(yōu)先級(jí)。中斷優(yōu)先級(jí)配置代碼如下:
//TIMER1 interrupt setting, preemptive priority 0, sub-priority 3
nvic_irq_enable(TIMER1_IRQn, 0, 3);
5) 允許 TIMER1工作,也就是使能 TIMER1。
光配置好定時(shí)器還不行,沒有開啟定時(shí)器,照樣不能用。我們?cè)谂渲猛旰笠_啟定時(shí)器,通過 TIMER_CTL0的TIMER_CTL0_CEN位來設(shè)置。在固件庫(kù)里面使能定時(shí)器的函數(shù)是通過 timer_enable()函數(shù)來實(shí)現(xiàn)的:
void timer_enable(uint32_t timer_periph)
這個(gè)函數(shù)非常簡(jiǎn)單,比如我們要使能定時(shí)器1,方法為:
/* TIMER1 enable */
timer_enable(TIMER1);
6) 編寫中斷服務(wù)函數(shù)。
在最后,還是要編寫定時(shí)器中斷服務(wù)函數(shù),通過該函數(shù)來處理定時(shí)器產(chǎn)生的相關(guān)中斷。在中斷產(chǎn)生后,通過狀態(tài)寄存器的值來判斷此次產(chǎn)生的中斷屬于什么類型。然后執(zhí)行相關(guān)的操作,我們這里使用的是更新(溢出)中斷,所以在中斷標(biāo)志寄存器TIMERx_INTF的最低位。在處理完中斷之后應(yīng)該向TIMERx_INTF最低位寫 0,來清除該中斷標(biāo)志。
在固件庫(kù)函數(shù)里面,用來讀取中斷狀態(tài)寄存器的值判斷中斷類型的函數(shù)是:
FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t int_flag)
該函數(shù)的作用是,判斷定時(shí)器 TIMERx 的中斷類型是否發(fā)生中斷。比如,我們要判斷定時(shí)器1是否發(fā)生更新(溢出)中斷,方法為:
if ( timer_interrupt_flag_get(TIMER1 , TIMER_INT_UP) != RESET ) {}
固件庫(kù)中清除中斷標(biāo)志位的函數(shù)是:
該函數(shù)的作用是,清除定時(shí)器 TIMERx 的中斷標(biāo)志位。 使用起來非常簡(jiǎn)單,比如我們?cè)赥IMER1 的溢出中斷發(fā)生后,我們要清除中斷標(biāo)志位,方法是:
timer_interrupt_flag_clear(TIMER1 , TIMER_INT_UP);
這里需要說明一下,固件庫(kù)還提供了兩個(gè)函數(shù)用來判斷定時(shí)器狀態(tài)以及清除定時(shí)器狀態(tài)標(biāo)志位的函數(shù) timer_flag_get()和timer_flag_clear(),他們的作用和前面兩個(gè)函數(shù)的作用類似。只是在 timer_interrupt_flag_get()函數(shù)中會(huì)先判斷這種中斷是否使能,使能了才去判斷中斷標(biāo)志位,而timer_flag_get()直接用來判斷狀態(tài)標(biāo)志位。
通過以上幾個(gè)步驟,我們就可以達(dá)到我們的目的了,使用通用定時(shí)器的更新中斷,來控制LED的亮滅。
最后定時(shí)器核心配置代碼如下:
/*
brief configure the TIMER peripheral
param[in] tim_typedef_enum TIM_id, uint16_t prescaler, uint32_t period, uint8_t prePriority, uint8_t subPriority
param[out] none
retval none
*/
void timx_init(tim_typedef_enum TIM_id, uint16_t prescaler, uint32_t period, uint8_t prePriority, uint8_t subPriority)
{
/* TIMER configuration: generate PWM signals with different duty cycles */
timer_parameter_struct timer_initpara;
//Enable TIMER clock
rcu_periph_clock_enable(TIM_CLK[TIM_id]);
timer_deinit(TIM[TIM_id]);
/* TIMER configuration */
timer_initpara.prescaler = prescaler;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = period;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIM[TIM_id], &timer_initpara);
timer_interrupt_enable(TIM[TIM_id], TIMER_INT_UP); /* Enable timer interrupt */
//TIMER interrupt setting, preemptive priority 0, sub-priority 3
nvic_irq_enable(TIM_IRQn[TIM_id], prePriority, subPriority);
/* TIMER enable */
timer_enable(TIM[TIM_id]);
rcu_periph_clock_disable(TIM_CLK[TIM_id]);/* disable timer clock*/
}
中斷代碼如下:
/**
* @brief This function handles TIMER1 interrupt request.
* @param None
* @retval None
*/
void TIMER1_IRQHandler(void)
{
if ( timer_interrupt_flag_get(TIMER1 , TIMER_INT_UP) != RESET )
{
time++;
timer_interrupt_flag_clear(TIMER1 , TIMER_INT_UP);
}
}
主函數(shù)如下:
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
//systick init
sysTick_init();
/* configure the TIMER peripheral */
timx_init(T1, 119, 999, 0, 1);
/* configure LED1 GPIO port */
led_init(LED1);
/* configure LED2 GPIO port */
led_init(LED2);
/* configure LED3 GPIO port */
led_init(LED3);
/* configure LED4 GPIO port */
led_init(LED4);
//Enable TIMER1 clock
rcu_periph_clock_enable(RCU_TIMER1);
while(1)
{
if ( time == 1000 ) /* 1000 * 1 ms = 1s 時(shí)間到 */
{
time = 0;
/* LED 取反 */
led_toggle(LED1);
led_toggle(LED2);
led_toggle(LED3);
led_toggle(LED4);
}
}
}
接下來分析下定時(shí)器溢出時(shí)間。
4.2 定時(shí)器溢出時(shí)間計(jì)算
1.定時(shí)器的時(shí)鐘源
定時(shí)器時(shí)鐘CK_TIMER經(jīng) APB1 預(yù)分頻器后分頻提供,如果 APB1 預(yù)分頻系數(shù)等于 1,則頻率不變,否則頻率乘以 2,庫(kù)函數(shù)中 APB1 預(yù)分頻的系數(shù)是 2,即 PCLK1=60M,所以定時(shí)器時(shí)鐘 CK_TIMER=60*2=120M。
其時(shí)鐘初始化代碼在system_stm32f20x.c定義的,這里使用的默認(rèn)配置,具體時(shí)鐘設(shè)置函數(shù)是system_clock_120m_hxtal(),代碼如下:
/*!
\\brief configure the system clock to 120M by PLL which selects HXTAL(8M) as its clock source
\\param[in] none
\\param[out] none
\\retval none
*/
static void system_clock_120m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {
while(1) {
}
}
/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB/1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB/2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* CK_PLL = (CK_PREDIV0) * 10 = 120 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLSEL);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL10);
/* CK_PREDIV0 = (CK_HXTAL) / 5 * 12 /5 = 12 MHz */
RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL12 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV5);
/* enable PLL1 */
RCU_CTL |= RCU_CTL_PLL1EN;
/* wait till PLL1 is ready */
while((RCU_CTL & RCU_CTL_PLL1STB) == 0U) {
}
/* enable PLL */
RCU_CTL |= RCU_CTL_PLLEN;
/* wait until PLL is stable */
while(0U == (RCU_CTL & RCU_CTL_PLLSTB)) {
}
/* select PLL as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL;
/* wait until PLL is selected as system clock */
while(0U == (RCU_CFG0 & RCU_SCSS_PLL)) {
}
}
重點(diǎn)關(guān)注以下代碼:
/* APB1 = AHB/2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
而RCU_APB1_CKAHB_DIV2的定義如下:
#define RCU_APB1_CKAHB_DIV2 CFG0_APB1PSC(4) /*!< APB1 prescaler select CK_AHB/2 */
因此最終到TIMER1上的時(shí)鐘為120Mhz。
2.定時(shí)器頻率
TIMER1上的時(shí)鐘為120Mhz,定時(shí)器分頻系數(shù)為119,因此TIMER1的頻率為
CK_CNT=TIMERxCLK/(PSC+1)=1MHz
3.自動(dòng)重裝載值
自動(dòng)重裝載寄存器 TIMERx_CAR是一個(gè) 16 位的寄存器,這里面裝著計(jì)數(shù)器能計(jì)數(shù)的最大數(shù)值。當(dāng)計(jì)數(shù)到這個(gè)值的時(shí)候,如果使能了中斷的話,定時(shí)器就產(chǎn)生溢出中斷,這里設(shè)置的是999。
完整配置參數(shù)如下:
Prtscaler (定時(shí)器分頻系數(shù)) : 119
Counter Mode(計(jì)數(shù)模式) :Up(向上計(jì)數(shù)模式)
Counter Period(自動(dòng)重裝載值) : 999
CKD(時(shí)鐘分頻因子) : No Division 不分頻
定時(shí)器溢出時(shí)間:
Tout=1/(Tclk/psc) *(arr+1)
本文設(shè)置參數(shù)為: arr=999 psc=119 Tclk=120Mhz ,因此最終的溢出時(shí)間如下:
Tout=1/(120MHz /(119+1)) *(999+1)=1ms
值得注意的是,自動(dòng)重裝載值計(jì)算溢出時(shí)間要加1,這是因?yàn)樽詣?dòng)重裝載寄存器 TIMERx_CAR是從0開始計(jì)數(shù)的。
5 實(shí)現(xiàn)現(xiàn)象
將編譯好的程序下載到看板子中,可以看到LED不停閃爍。
-
mcu
+關(guān)注
關(guān)注
146文章
17123瀏覽量
350975 -
定時(shí)器
+關(guān)注
關(guān)注
23文章
3246瀏覽量
114715 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
5032瀏覽量
97371 -
Cortex-M
+關(guān)注
關(guān)注
2文章
229瀏覽量
29752 -
GD32
+關(guān)注
關(guān)注
7文章
403瀏覽量
24326
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論