1. 程式人生 > >STM32F4開發板 外部中斷實驗

STM32F4開發板 外部中斷實驗

STM32的中斷分為核心中斷外部中斷,“核心中斷”在《ARM CM3&CM4權威指南》中也被稱作“系統異常”,如下所示:

今天主要討論外部中斷,即 CMSIS-Core中列舉值為正的異常(見表7.3)。

首先看關鍵詞“中斷線”,即“EXTI線”。STM32F407有23個外部中斷,即23箇中斷線,從EXTI線0到EXTI線22。(此處書中似乎寫成了22箇中斷線,貌似有誤)

我們需要將I/O口與中斷線之間建立對映關係,由於I/O口數量多於23,因此這種對映是多對一的,多個I/O口對應一箇中斷線。以EXTI線0為例,STM32按照GPIOA.0,GPIOB.0,GPIOC.0,GPIOD.0,GPIOE.0,GPIOF.0,GPIOG.0,GPIOH.0,GPIOI.0對應EXTI線0(即PA0,PB0,PC0,PD0,PE0,PF0,PG0,PH0,PI0均可以對應EXTI線0)。實際用到某個I/O引腳時,再通過配置決定具體哪個引腳對應EXTI線0。

接下來看關鍵詞“步驟”,即配置外部中斷的步驟。可以參考STM32的韌體庫函式stm32f4xx_exti.c程式碼和《精通STM32F4(庫函式版)》。

程式碼中的註釋部分可以看到:

                       ##### How to use this driver #####  ==================================================    [..] In order to use an I/O pin as an external interrupt source, follow steps        below:    (#) Configure the I/O in input mode using GPIO_Init()

   (#) Select the input source pin for the EXTI line using SYSCFG_EXTILineConfig()    (#) Select the mode(interrupt, event) and configure the trigger         selection (Rising, falling or both) using EXTI_Init()    (#) Configure NVIC IRQ channel mapped to the EXTI line using NVIC_Init()

 [..]         (@) SYSCFG APB clock must be enabled to get write access to SYSCFG_EXTICRx        registers using RCC_APB2PeriphClockCmd

(RCC_APB2Periph_SYSCFG, ENABLE);

書中的步驟也基本類似:

1.使能I/O口時鐘,初始化I/O口為輸入。—— GPIO_Init(),RCC_AHB1PeriphClockCmd

2.使能SYSCFG時鐘,設定I/O口與中斷線的對映關係。—— RCC_APB2PeriphClockCmd(),SYSCFG_EXTILineConfig()

3.初始化中斷線(哪條EXTI線,中斷模式是中斷還是事件,觸發方式是下降沿觸發、上升沿觸發還是任意電平觸發,是否使能中斷線)。—— EXTI_Init()

4.配置巢狀中斷向量控制器NVIC。—— NVIC_Init()

5.編寫中斷服務函式。—— EXTI0_IRQHandler(),EXTI1_IRQHandler(),EXTI2_IRQHandler(),EXTI3_IRQHandler(),EXTI4_IRQHandler(),EXTI9_5_IRQHandler(),EXTI15_10_IRQHandler()。這些函式名稱是在.s啟動檔案中定義的。

其實庫函式是個殼,實際操作的還是暫存器,庫函式只是相當於把對暫存器的操作“包起來”。如下所示:

EXTI_Init()操作的暫存器實際是下面這幾個: 

這幾個暫存器。。。再說吧

stm32中斷初識與實踐(下)

這一部分我們將使用按鍵作為觸發源,在產生中斷時,實現控制LED燈的亮滅狀態切換。

在具體應用前,我們還需先認識認識EXTI。

EXTI

全稱為External interrupt/event controller,即外部中斷/事件控制器。其管理了20箇中斷/事件線,每條線都有對應的一個邊沿檢測器,用於輸入訊號上升沿和下降沿的檢測。如圖6-1為stm32參考手冊裡的EXTI框圖。

圖6-1

圖中有兩條走向的線路,藍色線路用於產生中斷,綠色線路產生事件。我們從右往左看圖。

查閱按鍵原理圖可知,按鍵按下時,電平狀態由低變高,會在輸入線呈現出一個上升沿訊號,這個訊號到達邊沿檢測電路後,會被上升沿觸發選擇暫存器(EXTI_RTSR)檢測並觸發,輸出有效訊號1給編號3電路,否則輸出無效訊號0。如果電平由高變低,則會被下降沿觸發選擇暫存器(EXTI_FTSR)檢測觸發。

觸發的訊號到達編號3電路,這是一個或閘電路,它的輸入訊號除了來源於邊沿檢測電路,還有來自軟體中斷事件暫存器(EXTI_SWIER)。無論是來自EXTI_SWIER或邊沿檢測電路的訊號,只要有一個是有效訊號1,那麼便可以輸出有效訊號1給編號3電路。編號3電路之後,分為兩條線,一條產生中斷,一條產生事件。

訊號沿著藍色線路產生中斷,編號3輸出訊號到編號4。編號4是一個與閘電路,它的另一個訊號來源是中斷遮蔽暫存器(EXTI_IMR)。眾所周知,與閘電路要求輸入訊號都為1才能輸出1,換言之,如果EXTI_IMR置為0,那麼編號4電路輸出的訊號都為0,只有EXTI_IMR置1時,編號4輸出的訊號才由編號3決定。這樣一來我們可以通過EXTI_IMR來控制是否產生中斷。隨後,編號4電路輸出的訊號會被儲存到掛起暫存器(EXTI_PR)內。最後將EXTI_PR裡的值輸出到NVIC,實現中斷控制。

訊號沿著綠色線路產生事件,最終會輸出一個脈衝訊號。編號3輸出訊號到編號5,編號5也是一個與閘電路,訊號來源於編號3電路和事件遮蔽暫存器(EXTI_EMR)。和編號4的與閘電路一樣,我們可以通過EXTI_EMR來控制是否產生事件。當編號5輸出有效訊號1時會在脈衝發生器(Pulse generator)輸出一個脈衝訊號(無效訊號不會輸出脈衝)。這個脈衝訊號可以給其他外設電路使用,如TIM、ADC等等,一般用來觸發TIM或ADC開始轉換。

EXTI的中斷/事件線

EXTI有20條中斷/事件線,其中有16條用於GPIO線上的外部中斷/事件,佔用EXTI0~EXTI15,其他4條用於特定外設的外部中斷/事件。如圖6-2。

圖6-2

可以通過操作AFIO的四個外部中斷配置暫存器(AFIO_EXTICR1~AFIO_EXTICR4)的EXTIx[3:0]位選擇配置PAx、PBx、PCx...PGx等引腳。如圖6-3為AFIO_EXTICR1暫存器描述。

圖6-3

EXTI_InitTypeDef

EXTI_InitTypeDef是EXTI初始化結構體,其定義在stm32f10x.h檔案中,如圖6-4所示。

圖6-4

有四個結構體成員,

  • EXTI_Line:中斷/事件線,可選擇EXTI0~EXTI19,如圖6-2所示;
  • EXTI_Mode:EXTI模式,可設定產生中斷(EXTI_Mode_Interrupt)或產生事件(EXTI_Mode_Event);
  • EXTI_Trigger:EXTI邊沿觸發,可選擇上升沿觸發(EXTI_Trigger_Rising)、下降沿觸發(EXTI_Trigger_Falling)或者上升沿下降沿都觸發(EXTI_Trigger_Rising_Falling);
  • EXTI_LineCmd:使能(ENABLE)/失能(DISABLE)EXTI線。

開始實驗

按鍵按下時,產生電平變化,EXTI檢測到上升沿訊號,觸發中斷,執行中斷服務函式,實現LED燈的亮滅切換。

簡要分析下程式設計要點:

  1. 初始化產生中斷的外設(GPIO);
  2. 配置NVIC;
  3. 初始化EXTI;
  4. 中斷服務函式;
  5. main函式

NVIC配置

我們先對NVIC進行配置,將其封裝為函式NVIC_Configuration(),供後續呼叫。

/**
  * @brief  NVIC配置
  * @param  無
  * @retval 無
  */
static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;               
	
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);           // 配置優先順序分組
	
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;          // 配置按鍵中斷源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 搶佔優先順序為1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        // 子優先順序為1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // 使能中斷暫存器

    NVIC_Init(&NVIC_InitStructure);
}

NVIC配置部分,需要配置優先順序分組、中斷源、搶佔優先順序、子優先順序以及使能中斷暫存器等。關於優先順序分組配置以及NVIC_InitTypeDef結構體分析,已在上篇文章裡詳細說明,讀者可點選進入閱讀。

EXTI中斷配置

/**
  * @brief  EXTI按鍵中斷配置
  * @param  無
  * @retval 無
  */
void EXTI_Key_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
	
    /* 呼叫函式配置NVIC */
    NVIC_Configuration();
	
    /* 初始化GPIO */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空輸入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
	
    /* 初始化EXTI */
    // 配置中斷線的輸入源
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); 

    EXTI_InitStructure.EXTI_Line = EXTI_Line0;            // 配置中斷線為EXTI0
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;   // 配置為中斷模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;// 上升沿觸發中斷	     
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;             // 使能中斷
    EXTI_Init(&EXTI_InitStructure);
}

這個配置函式裡用到了GPIO和EXTI兩個初始化結構體,對其分別進行初始化配置,同時呼叫NVIC_Configuration()函式配置NVIC。

其中,

  • GPIO_EXTILineConfig()韌體函式是對AFIO_EXTICR1的操作,所以我們需要開啟AFIO時鐘;
  • 需要把GPIO配置為輸入模式(浮空輸入),由外部電路決定引腳狀態;
  • 通過查閱按鍵原理圖可得,按鍵引腳為PA0,可得GPIO埠源和引腳源,並將其中斷線配置為EXTI0;
  • 由按鍵原理圖可得,按鍵按下時為高電平,所以使用上升沿觸發中斷。

中斷服務函式

上篇文章裡已經說明中斷服務函式名應在啟動檔案裡找到,並統一寫在stm32f10x_it.c檔案中。

/**
  * @brief  EXTI0線中斷服務函式
  * @param  無
  * @retval 無
  */
void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET) // 確保產生了EXTI0線中斷
    {
        LED_TOGGLE;        // LED燈狀態切換 
        EXTI_ClearITPendingBit(EXTI_Line0);// 清除中斷標誌位
    }
}

需要先確保是否產生了中斷,這一步我們直接呼叫stm32f10x_exti.c檔案裡的庫函式EXTI_GetITStatus(),通過其返回值判斷。EXTI_GetITStatus()函式操作的是中斷遮蔽暫存器(EXTI_IMR)和掛起暫存器(EXTI_PR),通過兩個暫存器的值判斷是否產生中斷,。由圖6-1功能框圖可得,如果相應線的EXTI_IMR和EXTI_PR都置1,則返回“SET”,即產生中斷。具體的原始碼實現可檢視3.5版本的stm32f10x韌體庫。

LED_TOGGLE是一個巨集,在巨集裡實現LED狀態切換。具體的實現在專欄(stm32):GPIO輸入——按鍵檢測文章裡已經有過說明,讀者可移步閱讀。

中斷服務實現後,需要清除該中斷線的中斷標誌位,以免下次程式判斷失誤。

main函式

int main(void)
{	
    LED_GPIO_Config();  // LED埠初始化
    EXTI_Key_Config();  // EXTI按鍵中斷配置
    while(1){}          // 等待中斷產生
}

當按鍵按下時,即進入中斷,執行中斷服務函式,完成實驗。

至此,stm32的中斷總結完成了,文字有點多,耐心看完,肯定會有所收穫。