今天在寫利用中斷實現控制LED燈亮滅程序時,我遇到了一個尷尬的問題。
本來是想實現綠色標識的對應控制,即
K_UP控制LED1亮,K_DOWN控制LED2亮,
K_LEFT控制LED3亮,K_RIGHT控制LED4亮。
但結果很遺憾,出現了紅色標識對應的情況,各按鈕與LED燈的對應情況不符。我原始的代碼如下:(僅用於記錄錯誤代碼,若無興趣可以直接跳至中斷服務函數)
#include "exti.h"
void My_EXTI_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中斷優先級分組 分2組
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);//選擇GPIO管腳用作外部中斷線路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);//選擇GPIO管腳用作外部中斷線路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);//選擇GPIO管腳用作外部中斷線路
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//選擇GPIO管腳用作外部中斷線路
…………//NVIC配置的代碼略
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line=EXTI_Line2|EXTI_Line3|EXTI_Line4;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)==1)
{
delay_ms(10);
if(K_UP==1)
{
LED1_ON;
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)==1)
{
delay_ms(10);
if(K_DOWN==1)
{
LED2_ON;
}
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)==1)
{
delay_ms(10);
if(K_LEFT==1)
{
LED3_ON;
}
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4)==1)
{
delay_ms(10);
if(K_RIGHT==0)
{
LED4_ON;
}
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
既然從燈值表能看出K_UP和K_RIGHT與燈的對應關係是正確的,那麼問題是出在了K_DOWN和K_LEFT上。在仔細覆盤查找led、key、exti的問題之後,我最終把焦點鎖定在了中斷服務函數上。
原來,我的代碼的問題出現在了中斷服務函數與中斷線的對應上。什麼意思呢?我做了一張表格來表明我的中斷對應關係,如下:
乍一看其實沒有什麼問題的,也是符合中斷定義規則的。但聯繫到筆記開頭的燈值表的第一列,就能發現問題了。其中,綠色代表預想的配置關係,紅色代表錯誤配置關係。
顯然,在中斷複用IO後:
K_DOWN按鍵(PEin(3))應該對應EXTI3,卻被誤設置爲對應EXTI2;
K_LEFT按鍵(PEin(2))應該對應EXTI2,卻被誤設置爲對應EXTI3。
這也就是爲什麼出現了“按這個鍵,那個燈卻亮了”的原因。而爲什麼會出現明明應該是“K_DOWN== 0”和“K_LEFT== 0”(因爲按照我在key.h裏的宏定義,“==0”代表了低電平,而硬件設計則表示低電平代表開關打開),卻在設置“K_DOWN ==1”時燈纔會亮呢?
可以這麼理解:由於
EXTI_InitStructure.EXTI_Line=EXTI_Line2|EXTI_Line3|EXTI_Line4;
EXTI_EXTI_Trigger=EXTI_Trigger_Falling;
故EXTI2和EXTI3都被配置成了下降沿觸發(0到1時觸發)。
//以EXTI2爲例說明,EXTI3同
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)==1)
{
delay_ms(10); if(K_DOWN==1) { LED2_ON; }
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
當按下按鍵K_LEFT時,EXTI2的中斷服務函數啓動,此時:
若設置爲
if(K_DOWN==0)
(代表K_DOWN按下)那麼顯然if不成立,LED2不亮;
若設置爲if(K_DOWN==1)
(代表K_DOWN未按下)因爲我按下的是K_LEFT按鍵,那麼此時if條件是成立的,LED2亮。
按照此思路一一對應過去就會發現與文章最開頭的燈值表是情況一致的。
那麼其實要修改爲正確程序,只需要將兩個中斷服務函數的if條件對換位置即可:
//以EXTI2爲例說明,EXTI3同
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)==1)
{
delay_ms(10); if(K_LEFT==0) { LED3_ON; }
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
總結
由於對外部中斷的理解不夠深入,導致編程時出現了一些制杖的問題。
但從中我也學習到了:在中斷裏的if函數代表了先滿足中斷條件後,後進行判斷。也就是說如果要執行中斷函數裏的if函數的內容,要滿足兩個條件:1、能中斷;2、滿足1的前提下同時滿足if函數的條件。
中斷充當了一個連接硬件層面條件和軟件層面條件的橋樑,當硬件層面條件(上升沿、下降沿)被滿足時,中斷服務函數啓動,若中斷服務函數內還有條件判斷語句,則軟件層面條件(if語句等)開始執行判斷。
另外,這也告訴我:只有當在編寫軟件層面程序的同時,對硬件層面也要有深刻地理解,這樣才能實現精準而無bug的控制。
附言
完整程序文件見附件,我使用的開發板是普中PZ6806L型開發板,所以對應IO是以它的IO爲設置的。