?
?
我們?cè)诨?/span>ST的標(biāo)準(zhǔn)庫(kù)或基于CubeMx建立工程,當(dāng)用到定時(shí)器并啟用其更新中斷時(shí),可能會(huì)遇到一使能定時(shí)器中斷且計(jì)算器還未開(kāi)始計(jì)數(shù)就立即進(jìn)入更新中斷服務(wù)程序的情況。
?
可能出現(xiàn)該現(xiàn)象的場(chǎng)合大概像下面樣子,即先使能定時(shí)器更新中斷,然后才去啟動(dòng)計(jì)數(shù)器。
?
?
我們會(huì)發(fā)現(xiàn),剛一使能更新中斷還未啟動(dòng)計(jì)數(shù)器,結(jié)果就跑到更新中斷服務(wù)程序里。
?
看看下面截圖,右邊TIMER控制寄存器CEN還未置1,SR寄存器里的UIF【更新事件標(biāo)志】已經(jīng)置1了。
?
?
也就是說(shuō),計(jì)數(shù)器還沒(méi)開(kāi)始啟動(dòng)就先進(jìn)了一次更新中斷。
?
這種情況很多時(shí)候?qū)ξ覀儜?yīng)用可能并無(wú)妨礙,但有時(shí)也可能帶來(lái)些問(wèn)題或麻煩。比方:
?
1、誤動(dòng)作。本來(lái)打算基于定時(shí)器運(yùn)作延時(shí)特定時(shí)間后再在更新中斷里完成的動(dòng)作,這樣一來(lái)就會(huì)出現(xiàn)計(jì)數(shù)器還未開(kāi)始工作就進(jìn)中斷執(zhí)行當(dāng)前本不該執(zhí)行的動(dòng)作了。
2、誤計(jì)算。當(dāng)我們基于定時(shí)器捕獲功能進(jìn)行信號(hào)周期、占空比測(cè)量過(guò)程中,如果需要統(tǒng)計(jì)更新事件時(shí),就有可能因?yàn)榻y(tǒng)計(jì)這個(gè)不該統(tǒng)計(jì)的更新事件而帶來(lái)誤差。
?
該問(wèn)題怎么產(chǎn)生的呢?
問(wèn)題是由于我們的TIMER初始化函數(shù)里,在對(duì)ARR/PSC等時(shí)基參數(shù)做好初始賦值后,軟件做了個(gè)手動(dòng)產(chǎn)生更新事件的操作,目的就是讓剛才設(shè)置的那些時(shí)基參數(shù)立即生效,并讓定時(shí)器基于這些新設(shè)置的參數(shù)開(kāi)始運(yùn)行。
?
代碼大致是下面的層次結(jié)構(gòu)及內(nèi)容:
?
MX_TIM_Inxit(); ==》
HAL_TIM_Base_Init(&htim)==》
TIM_Base_SetConfig()
?
void TIM_Base_SetConfig(TIM_TypeDef *TIMx,TIM_Base_InitTypeDef *Structure)
{
…..
/* Set the auto-reload preload */
MODIFY_REG(tmpcr1, TIM_CR1_ARPE, Structure->AutoReloadPreload);
TIMx->CR1 = tmpcr1;
/*Set the Autoreload value */
TIMx->ARR =(uint32_t)Structure->Period ;
/*Set the Prescaler value */
TIMx->PSC = Structure->Prescaler;
if(IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx))
{
/* Set the Repetition Counter value */
TIMx->RCR =Structure->RepetitionCounter;
}
/*Generate an update event to reload the Prescaler
and the repetition counter (only for advanced timer) value immediately*/
TIMx->EGR = TIM_EGR_UG;
}
?
其中,? TIMx->EGR = TIM_EGR_UG;這行代碼就是用來(lái)手動(dòng)產(chǎn)生更新事件的。
?
我們知道,STM32定時(shí)器中有幾個(gè)由預(yù)裝寄存器和影子寄存器組成的寄存器組,他們分別是TIMx_PSC,TIMx_ARR,TIMx_CCR,TIMx_RCR. 【注:基本定時(shí)器或通用定時(shí)器沒(méi)有RCR寄存器】
那么如何消除這個(gè)問(wèn)題呢?操作很簡(jiǎn)單,TIMER初始化完成之后,使能定時(shí)器更新中斷之前加一句清除更新中斷請(qǐng)求位的代碼即可。比方類(lèi)似下面操作。
或許有人問(wèn),我在TIMER初始化過(guò)程中自己組織代碼時(shí)沒(méi)有手動(dòng)產(chǎn)生更新事件似乎也沒(méi)啥問(wèn)題?定時(shí)器跑得好好的?即沒(méi)有類(lèi)似下面打叉的語(yǔ)句。
?
?
的確,沒(méi)有這句產(chǎn)生更新事件的代碼定時(shí)器也能跑。
?
芯片復(fù)位后,ARR的預(yù)裝功能默認(rèn)關(guān)閉,此時(shí)改寫(xiě)ARR預(yù)裝寄存器相當(dāng)于同時(shí)也更新了其影子寄存器【即實(shí)際起作用的寄存器】,但PSC和RCR預(yù)裝寄存器的內(nèi)容只能借助溢出產(chǎn)生更新事件更新到其影子寄存器而起作用。
?
我們以向上計(jì)數(shù)模式為例,更新事件基于當(dāng)前用戶給定的ARR值計(jì)數(shù)一個(gè)周期后發(fā)生溢出而產(chǎn)生,隨之新的PSC和RCR值才會(huì)生效。即二者的生效時(shí)間要延后一個(gè)周期。?【注:基本定時(shí)器或通用定時(shí)器沒(méi)有RCR寄存器】
?
如果說(shuō)有人自行組織代碼,先將ARR的預(yù)裝控制位使能打開(kāi),然后才給ARR賦值,同樣不手動(dòng)產(chǎn)生更新事件,那又會(huì)怎么樣呢?拋磚引玉,可以自行結(jié)合手冊(cè)琢磨和測(cè)試下。
?
?
ST的庫(kù)函數(shù)的寫(xiě)法是合理的,當(dāng)然,如果緊跟著做個(gè)更新事件標(biāo)志的清零就更佳了,或許未來(lái)這個(gè)地方可以再改善下。
?
順便提下,當(dāng)你自己嘗試在代碼里適時(shí)而巧妙地使用手動(dòng)定時(shí)器更新操作時(shí),或許會(huì)發(fā)現(xiàn)這還是個(gè)不可多得的一個(gè)小技巧。
?
?
好,這個(gè)話題就聊到這里。其實(shí),多年前也在這里分享過(guò)該話題,只是沒(méi)有單列出來(lái)。這次單列出來(lái)再分享下,以資提醒。
?
?
審核編輯:湯梓紅
評(píng)論