先看一段代碼:
while(1)
{
if(EXTI_Sign==1)
{
HAL_Delay(Period);
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);
EXTI_Sign=0;
。。。。。。
}
}
有人使用STM32G0系列的芯片開發(fā)產(chǎn)品,有段功能測(cè)試驗(yàn)證代碼如上所示,相同的函數(shù)必須調(diào)用2次才能正常運(yùn)行,調(diào)用2次倒也罷了,關(guān)鍵是必須!頗為納悶。
這里開啟了PA3的外部中斷功能,上下沿均可觸發(fā)。PA3接收外來報(bào)警信號(hào),類似于煙感報(bào)警器。報(bào)警信號(hào)是一串脈沖信號(hào),報(bào)警信號(hào)過來時(shí)存在多次抖動(dòng)問題??蛻粝肓藗€(gè)方法消抖,只要報(bào)警端口有電平變化就觸發(fā)中斷然后把中斷Disable,并設(shè)置報(bào)警標(biāo)志再回到主程序。
主程序里識(shí)別到報(bào)警有效標(biāo)志后延時(shí)幾分鐘再Enable剛才Disable掉的外部中斷。但是,他發(fā)現(xiàn)再次使能外部中斷時(shí)需要連續(xù)兩次調(diào)用使能中斷的代碼才可以響應(yīng)新的報(bào)警信號(hào)?!敬颂幬淖忠罁?jù)反饋者的文字描述組織而成】
下面MX_GPIO_Init(void)是經(jīng)CubeMx配置后自動(dòng)生成的,里面有EXTI相關(guān)NVIC配置。相關(guān)代碼如下:
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
。。。。。。
/*Configure GPIO pin : PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI2_3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);
}
基于上沿觸發(fā)的中斷服務(wù)程序如下[基于下沿觸發(fā)的此處省略】:
EXTI ISR():
{
__HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_PIN_3);//清中斷申請(qǐng)標(biāo)志;
HAL_NVIC_DisableIRQ(EXTI2_3_IRQn);//關(guān)閉中斷響應(yīng)
EXIT_Sign=1;//表示收到報(bào)警信號(hào)
}
主循環(huán)代碼像下面書寫才能讓程序正常運(yùn)行:【略去了其它代碼】
while (1)
{
if(EXTI_Sign ==1)
{
HAL_Delay(Period);
MX_GPIO_Init();//客戶無意中發(fā)現(xiàn)加這句有用
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);
EXTI_Sign =0;
。。。。。。
}
}
現(xiàn)在的疑問是在EXTI中斷服務(wù)程序運(yùn)行HAL_NVIC_DisableIRQ(EXTI2_3_IRQn)后,到主循環(huán)代碼里再次使能外部中斷時(shí),為何還要額外運(yùn)行一次MX_GPIO_Init()函數(shù)才能讓程序正常運(yùn)行。最終發(fā)現(xiàn)運(yùn)行該函數(shù)的實(shí)質(zhì)就是將HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)多運(yùn)行一次。
換句話說,上面的主循環(huán)代碼要改成下面樣子才可以讓程序正常運(yùn)行:
while(1)
{
if(EXTI_Sign==1)
{//報(bào)警有效,即發(fā)生過報(bào)警時(shí),代碼進(jìn)到這里。
HAL_Delay(Period);
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);//1
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);//2
EXTI_Sign=0; //清除報(bào)警標(biāo)志,準(zhǔn)備監(jiān)測(cè)新的警情
。。。。。。
}
}
說到底,問題就是主循環(huán)里為何要兩次重復(fù)運(yùn)行HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)函數(shù)后才能響應(yīng)新的報(bào)警信號(hào)呢?
可以肯定,理論上講,開啟某個(gè)中斷響應(yīng)無須2次運(yùn)行相關(guān)函數(shù)。我們來一起找找原因。為了便于查看代碼,我把中斷服務(wù)程序和主程序代碼截圖放在一起。


在中斷服務(wù)程序里就是清除中斷請(qǐng)求標(biāo)志,關(guān)閉PA3的外部中斷響應(yīng),并設(shè)置警情標(biāo)志EXTI_Sign為1。
這里有沒有問題呢?
他使用的HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)函數(shù),關(guān)閉的是內(nèi)核對(duì)該中斷請(qǐng)求的響應(yīng),盡管他剛才在進(jìn)中斷時(shí)做外部中斷請(qǐng)求標(biāo)志的清零,但并不能保證他這個(gè)清零操作之后不會(huì)再產(chǎn)生外部中斷請(qǐng)求。事實(shí)上,結(jié)合目前的使用場(chǎng)景,由于報(bào)警信號(hào)是一串跳變脈沖,即使一進(jìn)中斷就先做了個(gè)中斷請(qǐng)求標(biāo)志的清零,在中斷退出甚至還未完全退出時(shí)大概率還會(huì)產(chǎn)生新的中斷請(qǐng)求,但又由于他在中斷服務(wù)程序里把中斷響應(yīng)關(guān)閉了,中斷不能得到及時(shí)響應(yīng),請(qǐng)求只能懸著【Pending】跟隨程序來到主循環(huán)。
主循環(huán)代碼首先檢查報(bào)警標(biāo)志是否生效,生效則進(jìn)入循環(huán)體,先靜靜地歇會(huì)兒【HAL_Delay(Period)】,讓剛才的報(bào)警信號(hào)完全消停下來,然后再調(diào)用第一個(gè)HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)函數(shù)打開中斷響應(yīng)。這下可好,剛才候著的中斷請(qǐng)求得到響應(yīng)機(jī)會(huì)了,則馬上去執(zhí)行中斷服務(wù)程序。這次在中斷服務(wù)程序里的操作跟上次完全一樣,即在中斷服務(wù)程序里,又調(diào)用中斷響應(yīng)關(guān)閉函數(shù),做了跟剛才主循環(huán)里第一個(gè)HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)函數(shù)完全相反的功能。即到這個(gè)點(diǎn)的時(shí)候,中斷響應(yīng)被關(guān)閉了。
如果中斷返回后沒有使用第2句HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)函數(shù)打開中斷響應(yīng),而只是執(zhí)行那句清零報(bào)警標(biāo)志然后退出循環(huán)體。由于中斷響應(yīng)已經(jīng)關(guān)閉,不管外部怎么報(bào)警都不會(huì)得到響應(yīng),報(bào)警標(biāo)志也就永遠(yuǎn)不會(huì)被置1,這樣主循環(huán)體也進(jìn)不了內(nèi)循環(huán)來開啟中斷響應(yīng)。
如果有了第2句HAL_NVIC_EnableIRQ(EXTI2_3_IRQn)函數(shù)在循環(huán)體內(nèi),它就可以扭轉(zhuǎn)剛才在中斷服務(wù)程序里關(guān)閉外部中斷響應(yīng)的局面,即把它扳回來。這樣的話功能上至少能正常運(yùn)轉(zhuǎn)了。
原因基本就大致這么回事?;诂F(xiàn)有代碼寫法,如何破除這個(gè)連寫2次的搞法呢。其實(shí),我們只需要在主循環(huán)體內(nèi)開啟外部中斷響應(yīng)的函數(shù)前,延時(shí)等待函數(shù)之后加上對(duì)相關(guān)中斷請(qǐng)求標(biāo)志位的清零即可解決當(dāng)前困惑。
比如像下面這樣【其中DSB是個(gè)數(shù)據(jù)同步隔離指令,保障它前面的指令執(zhí)行完畢后才執(zhí)行它后面的】,在主循環(huán)內(nèi)開啟中斷響應(yīng)前,先做中斷請(qǐng)求標(biāo)志的清零。
while(1)
{
if(EXTI_Sign==1)
{
HAL_Delay(Period);
__HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_PIN_3); __HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_PIN_3);
__DSB();
HAL_NVIC_EnableIRQ(EXTI2_3_IRQn);
EXTI_Sign=0;
。。。。。。
}
}
OK,本話題就聊到這里,愿君有所獲。類似問題不論STM32新手還是老手都可能不期而遇,祝君好運(yùn)!
-
STM32
+關(guān)注
關(guān)注
2310文章
11171瀏覽量
373767 -
中斷
+關(guān)注
關(guān)注
5文章
918瀏覽量
43800 -
程序
+關(guān)注
關(guān)注
117文章
3846瀏覽量
85315 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4419瀏覽量
67627 -
代碼
+關(guān)注
關(guān)注
30文章
4972瀏覽量
74079
原文標(biāo)題:聊聊一個(gè)STM32中斷處理問題
文章出處:【微信號(hào):stmcu832,微信公眾號(hào):茶話MCU】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
中斷是什么?STM32中斷系統(tǒng)介紹和中斷配置步驟資料免費(fèi)下載
STM32的Cortex-M3中斷異常處理
STM32關(guān)全局中斷的方法 STM32中斷類型
STM32中斷與DMA通信編程
STM32中斷與DMA通信編程
STM32中斷與DMA通信編程
stm32中斷初識(shí)與實(shí)踐(上)
為什么有些STM32中斷沒有子優(yōu)先級(jí)?
聊聊一個(gè)STM32中斷處理問題
評(píng)論