STM32的定時(shí)器知識(shí)相當(dāng)復(fù)雜,這里列舉一些基礎(chǔ)知識(shí),在之后的文章我會(huì)寫一下它的各種應(yīng)用。
通用定時(shí)器是一個(gè)通過可編程預(yù)分頻器驅(qū)動(dòng)的16位自動(dòng)裝載計(jì)數(shù)器構(gòu)成。它適用于多種場(chǎng)合,包括定時(shí)中斷、測(cè)量輸入信號(hào)的脈沖長(zhǎng)度(輸入捕獲)或者產(chǎn)生輸出波形(輸出比較和PWM)。每個(gè)定時(shí)器都是完全獨(dú)立的,沒有互相共享任何資源,它們可以一起同步操作。
STM32F103RC系列有4個(gè)通用定時(shí)器,2個(gè)高級(jí)定時(shí)器和兩個(gè)基本定時(shí)器:
我們最常使用通用定時(shí)器,功能包括:
1、16位向上 、向下、向上/向下自動(dòng)裝載計(jì)數(shù)器(TIMx_CNT),例如向上是指:計(jì)數(shù)器從0計(jì)數(shù)到自動(dòng)加載值(TIMx_ARR),然后重新從0開始計(jì)數(shù)并且產(chǎn)生一個(gè)計(jì)數(shù)器溢出事件。
2、16 位可編程(可以實(shí)時(shí)修改)預(yù)分頻器(TIMx_PSC),計(jì)數(shù)器時(shí)鐘頻率的分頻系數(shù) 為 1~65535 之間的任意數(shù)值。
3、4 個(gè)獨(dú)立通道(TIMx_CH1~4)。
4、支持多種中斷,如溢出中斷等。
這里我們講述使用通用定時(shí)器中斷實(shí)驗(yàn):
一、定時(shí)器時(shí)鐘選擇
我們常使用內(nèi)部時(shí)鐘(CK_INT),通過配置TIMx_SMCR的SMS[2:0],配置為000。該時(shí)鐘是ABP1時(shí)鐘的的1倍(APB1不分頻)或2倍(APB1分頻)。
二、計(jì)數(shù)器模式
可選擇向上 、向下或者向上/向下。
三、定時(shí)時(shí)間計(jì)算
溢出時(shí)間 = ( 自動(dòng)加載值(ARR)+ 1 )( 預(yù)分頻系數(shù)(PSC)+ 1 ) / 定時(shí)器時(shí)鐘(Tclk)
四、庫(kù)函數(shù)配置
1、使能能定時(shí)器時(shí)鐘。
2、初始化定時(shí)器,配置ARR,PSC(在stm32f10x_tim.c)
TIM_TimeBaseInit();
其中的結(jié)構(gòu)體:typedef struct
{
uint16_t TIM_Prescaler; //設(shè)置分頻系數(shù)
uint16_t TIM_CounterMode; //計(jì)數(shù)方式
uint16_t TIM_Period; //自動(dòng)重裝載值
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
3、開啟定時(shí)器中斷,配置中斷優(yōu)先級(jí)分組NVIC。
void TIM_ITConfig();
NVIC_Init();
4、 使能定時(shí)器。
TIM_Cmd();
5、編寫中斷服務(wù)函數(shù)。
TIMx_IRQHandler();
stm32定時(shí)器誤區(qū)
在用到STM32定時(shí)器的更新中斷時(shí),發(fā)現(xiàn)有些情形下只要開啟定時(shí)器就立即進(jìn)入一次中斷。準(zhǔn)確說,只要使能更新中斷允許位就立即響應(yīng)一次更新中斷【當(dāng)然前提是相關(guān)NVIC也已經(jīng)配置好】。換言之,只要使能了相關(guān)定時(shí)器更新中斷,不管你定時(shí)間隔多長(zhǎng)甚至不在乎你是否啟動(dòng)了相關(guān)定時(shí)器,它都會(huì)立即進(jìn)入一次定時(shí)器更新中斷服務(wù)程序。
以STM32F051芯片為例,做了幾種不同順序的組合測(cè)試。根據(jù)測(cè)試發(fā)現(xiàn),的確有些情況下一運(yùn)行TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); 【即使能更新中斷】就立即進(jìn)入更新中斷服務(wù)程序。當(dāng)然后面的中斷都是正常的。
老實(shí)說,這個(gè)問題比較容易忽視,有些情況下也無關(guān)緊要,但有些情況可能會(huì)給應(yīng)用帶來困擾。從ST MCU相關(guān)技術(shù)手冊(cè)似乎并不能明顯地找到關(guān)于這個(gè)問題的很合適或者邏輯性很強(qiáng)的前因后果。
經(jīng)過驗(yàn)證測(cè)試,如果注意一下相關(guān)指令代碼順序是可以回避這個(gè)問題的。
先做更新中斷標(biāo)志的清除操作,即清除TIMx-》SR寄存器里的UIF標(biāo)志,然后做定時(shí)器更新中斷的使能操作。至于開啟相關(guān)定時(shí)器的指令擺放位置并不嚴(yán)格。下面是相關(guān)動(dòng)作的操作順序及結(jié)果,可以參考、驗(yàn)證之。這里共羅列了6種寫法,其中有3種情形是會(huì)立即進(jìn)入中斷的,另外3種不會(huì)。
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除更新中斷請(qǐng)求位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //使能定時(shí)器1更新中斷
TIM_Cmd(TIM1, ENABLE); //啟動(dòng)定時(shí)器
(1)。。。。。。不會(huì)立即進(jìn)入更新中斷程序。
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中斷請(qǐng)求位
TIM_Cmd(TIM1, ENABLE);
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定時(shí)器1更新中斷
(2)。。。。。。不會(huì)立即進(jìn)入更新中斷程序。
TIM_Cmd(TIM1, ENABLE);
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中斷請(qǐng)求位
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//使能定時(shí)器1更新中斷
(3)。。。。。。不會(huì)立即進(jìn)入更新中斷程序。
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清除更新中斷請(qǐng)求位
TIM_Cmd(TIM1, ENABLE);
(5)。。。。。。立即進(jìn)入更新中斷程序。
(6)。。。。。。立即進(jìn)入更新中斷程序。
順便提下關(guān)于定時(shí)器里UG位和URS位的使用,分別在TIMx-》EGR和TIMx-》CR1寄存器里。對(duì)UG位置1可以產(chǎn)生更新事件并對(duì)相關(guān)計(jì)數(shù)器和寄存器重新初始化,如果URS位為0的話,同時(shí)會(huì)產(chǎn)生更新中斷。如果不希望對(duì)UG位置1的同時(shí)產(chǎn)生更新中斷,得置URS位為1,否則會(huì)立即進(jìn)入更新中斷。
另外
我們平時(shí)使用定時(shí)器的時(shí)候多數(shù)都是處于開啟狀態(tài),平時(shí)的定時(shí)中斷書寫格式一般是:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//要處理的事件內(nèi)容。。。。
}
}
但是,項(xiàng)目的實(shí)驗(yàn)過程中,我使用的定時(shí)器處理事件稍微有點(diǎn)特殊,即,定時(shí)器不是一直處于開啟狀態(tài), 而且關(guān)閉時(shí)候也是在中斷里關(guān)閉。大概形式這樣:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//要處理的事件內(nèi)容。。。。
TIM_Cmd(TIM3, DISABLE); //失能(函數(shù)外使能)
}
}
看似沒錯(cuò),而且也看似正常。但是,處理的事件內(nèi)容出現(xiàn)了很多未知錯(cuò)誤(由于我的這個(gè)處理事件有很強(qiáng)的時(shí)序性,開始和結(jié)束都比較嚴(yán)格),無法正常執(zhí)行。通過后來的調(diào)試中發(fā)現(xiàn)(把處理時(shí)間改為點(diǎn)燈或者打印輸出方式),發(fā)現(xiàn)是:TIM_Cmd(TIM3, DISABLE); 擾亂了時(shí)序關(guān)系。當(dāng)失能后,其實(shí)中斷并沒有真正失能,還會(huì)再進(jìn)入一次中斷,因此事件又被執(zhí)行了一次,對(duì)于時(shí)序比較嚴(yán)格的事件,就產(chǎn)生了問題!
找到了原因,因此,我猜測(cè)雖然定時(shí)器失能并且關(guān)閉了定時(shí)器,但是可能中斷標(biāo)志位并沒真正清除,雖然中斷開始已經(jīng)清除過一次,但估計(jì)因?yàn)槭苁沟脴?biāo)志位又被置位了,因此,我在失能前面加了句清除中斷更新標(biāo)志位,如下:
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//要處理的事件內(nèi)容。。。。
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);//再清除標(biāo)志位
TIM_Cmd(TIM3, DISABLE); //失能(函數(shù)外使能)
}
}
果然,程序可以正常的時(shí)序運(yùn)行。
比較納悶關(guān)定時(shí)器前又得清下標(biāo)志位,因此引起了另一個(gè)好奇心,是不是在其他地方關(guān)閉定時(shí)器(如主函數(shù)),也得這樣做才可以。所以對(duì)這個(gè)好奇心進(jìn)行了下測(cè)試。發(fā)現(xiàn):如果把關(guān)閉定時(shí)器放到了主函數(shù)后,不用再清中斷標(biāo)志位。能正常把定時(shí)器關(guān)閉,并不會(huì)進(jìn)入中斷。
通過這次的問題,浪費(fèi)了很多時(shí)間解決,不過也吸取到了點(diǎn)經(jīng)驗(yàn),但對(duì)于內(nèi)在真正原因:在中斷里失能和中斷外失能效果為什么不一樣,暫時(shí)還沒搞清楚。。。但這個(gè)可以作為以后的前車之鑒,以及大家的前車之鑒,少走彎路。
評(píng)論
查看更多