【stm32f407】外部中斷實現按鍵中斷方式
一.外部中斷
STM32F4的每個IO都可以作為外部中斷的中斷輸入口,這點也是STM32F4的強大之處。STM32F407的中斷控制器支援22個外部中斷/事件請求。每個中斷設有狀態位,每個中斷/事件都有獨立的觸發和遮蔽設定。
STM32F407的22個外部中斷為:
EXTI線0~15:對應外部IO口的輸入中斷。
EXTI線16:連線到PVD輸出。
EXTI線17:連線到RTC鬧鐘事件。
EXTI線18:連線到USB OTG FS喚醒事件。
EXTI線19:連線到乙太網喚醒事件。
EXTI線20:連線到USB OTG HS(在FS中配置)喚醒事件。
EXTI線21:連線到RTC入侵和時間戳事件。
EXTI
從上面可以看出,STM32F4供IO口使用的中斷線只有16個,但是STM32F4的IO口卻遠遠不止16個,那麼STM32F4是怎麼把16箇中斷線和IO口一一對應起來的呢?於是STM32就這樣設計,GPIO的管教GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G,H,I)分別對應中斷線0~15。這樣每個中斷線對應了最多9個IO口,以線0為例:它對應了GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0。而中斷線每次只能連線到1個IO口上,這樣就需要通過配置來決定對應的中斷線配置到哪個GPIO上了。下面我們看看GPIO跟中斷線的對映關係圖
二.外部中斷應用
操作中斷需要幾個步驟:
1)使能IO口時鐘,初始化IO口為輸入
首先,我們要使用IO口作為中斷輸入,所以我們要使能相應的IO口時鐘,以及初始化相應的IO口為輸入模式。
2)開啟SYSCFG時鐘,設定IO口與中斷線的對映關係。
接下來,我們要配置GPIO與中斷線的對映關係,那麼我們首先需要開啟SYSCFG時鐘。RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//使能SYSCFG時鐘這裡大家一定要注意,只要我們使用到外部中斷,就必須開啟SYSCFG時鐘。
接下來,我們配置GPIO與中斷線的對映關係。在庫函式中,配置GPIO與中斷線的對映關係的函式
voidSYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);
該函式將GPIO埠與中斷線對映起來,使用範例是:
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
將中斷線0與GPIOA對映起來,那麼很顯然是GPIOA.0與EXTI1中斷線連線了。設定好中斷線對映之後,那麼到底來自這個IO 口的中斷是通過什麼方式觸發的呢?接下來我們就要設定該中斷線上中斷的初始化引數了。
3)初始化線上中斷,設定觸發條件等。
中斷線上中斷的初始化是通過函式EXTI_Init()實現的。EXTI_Init()函式的定義是
voidEXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
下面我們用一個使用範例來說明這個函式的使用:
EXTI_InitTypeDefEXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=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);//初始化外設EXTI暫存器
上面的例子設定中斷線4上的中斷為下降沿觸發。STM32的外設的初始化都是通過結構體來設定初始值的,這裡就不再講解結構體初始化的過程了。我們來看看結構體EXTI_InitTypeDef的成員變數:
typedefstruct
{uint32_t EXTI_Line;
EXTIMode_TypeDefEXTI_Mode;
EXTITrigger_TypeDefEXTI_Trigger;
FunctionalStateEXTI_LineCmd;
}EXTI_InitTypeDef;
從定義可以看出,有4個引數需要設定。第一個引數是中斷線的標號,對於我們的外部中斷,取值範圍為EXTI_Line0~EXTI_Line15。這個在上面已經講過中斷線的概念。也就是說,這個函式配置的是某個中斷線上的中斷引數。第二個引數是中斷模式,可選值為中斷EXTI_Mode_Interrupt和事件EXTI_Mode_Event。第三個引數是觸發方式,可以是下降沿觸發EXTI_Trigger_Falling,上升沿觸發EXTI_Trigger_Rising,或者任意電平(上升沿和下降沿)觸發EXTI_Trigger_Rising_Falling
4)配置中斷分組(NVIC),並使能中斷。
我們設定好中斷線和GPIO對映關係,然後又設定好了中斷的觸發模式等初始化引數。既然是外部中斷,涉及到中斷我們當然還要設定NVIC中斷優先順序。這個在前面已經講解過,這裡我們就接著上面的範例, 設定中斷線2的中斷優先順序。
NVIC_InitTypeDefNVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel= EXTI2_IRQn;//使能按鍵外部中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x02;//搶佔優先順序2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0x02;//響應優先順序2
NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;//使能外部中斷通道
NVIC_Init(&NVIC_InitStructure);//中斷優先順序分組初始化
5)編寫中斷服務函式。
我們配置完中斷優先順序之後,接著要做的就是編寫中斷服務函式。中斷服務函式的名字是在MDK中事先有定義的。這裡需要說明一下,STM32F4的IO口外部中斷函式只有7個,分別為:
EXPORTEXTI0_IRQHandler
EXPORTEXTI1_IRQHandler
EXPORTEXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORTEXTI4_IRQHandler
EXPORTEXTI9_5_IRQHandler
EXPORTEXTI15_10_IRQHandler
中斷線0-4每個中斷線對應一箇中斷函式,中斷線5-9共用中斷函式EXTI9_5_IRQHandler,中斷線10-15共用中斷函式EXTI15_10_IRQHandler。在編寫中斷服務函式的時候會經常使用到兩個函式,第一個函式是判斷某個中斷線上的中斷是否發生(標誌位是否置位):
ITStatusEXTI_GetITStatus(uint32_t EXTI_Line);
這個函式一般使用在中斷服務函式的開頭判斷中斷是否發生。另一個函式是清除某個中斷線上的中斷標誌位:
voidEXTI_ClearITPendingBit(uint32_t EXTI_Line);
這個函式一般應用在中斷服務函式結束之前,清除中斷標誌位。
常用的中斷服務函式格式為:
voidEXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判斷某個線上的中斷是否發生
{ …中斷邏輯…
EXTI_ClearITPendingBit(EXTI_Line3);//清除LINE上的中斷標誌位
}
}
在這裡需要說明一下,韌體庫還提供了兩個函式用來判斷外部中斷狀態以及清除外部狀態標誌位的函式EXTI_GetFlagStatus和EXTI_ClearFlag,他們的作用和前面兩個函式的作用類似。只是在EXTI_GetITStatus函式中會先判斷這種中斷是否使能,使能了才去判斷中斷標誌位,而EXTI_GetFlagStatus直接用來判斷狀態標誌位。
講到這裡,相信大家對STM32的IO口外部中斷已經有了一定的瞭解。下面我們再總結一下使用IO口外部中斷的一般步驟:
1)使能IO口時鐘,初始化IO口為輸入。
2)使能SYSCFG時鐘,設定IO口與中斷線的對映關係。
3)初始化線上中斷,設定觸發條件等。
4)配置中斷分組(NVIC),並使能中斷。
5)編寫中斷服務函式。
通過以上幾個步驟的設定,我們就可以正常使用外部中斷了。
三.原碼解析
此原始碼是實現user PA0按鍵觸發中斷的實驗,檢測到終端會把LED常亮起來,並且列印monitor button
Exit.h
#ifndef_EXIT_H_H_H
#define_EXIT_H_H_H
voidEXTIX_Init(void);
#endif
Exit.c
#include"exit.h"
#include"key.h"
#include"delay.h"
#include"uart.h"
#include"led.h"
voidEXTI0_IRQHandler(void)
{
delay_ms(10);
printf("monitor button\r\n");
LED_Operate(LED_ORANGE,LED_ON);
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中斷標誌位
}
voidEXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode =EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel =EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
Main,c
#include"led.h"
#include "key.h"
#include "delay.h"
#include "uart.h"
#include "exit.h"
void User_Delay(__IO uint32_t nCount)
{
while(nCount--)
{
}
}
static int count = 0;
int main(void)
{
#if 1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
My_USART2_Init();
delay_init(168); //初始化延時函式
LED_Init();
printf("http://blog.csdn.net/XiaoXiaoPengBo\r\n");
EXTIX_Init();
while(1);
#endif
}
四.程式執行圖
通過原理圖來看,PA0正常狀態是低電平
按鍵觸發中斷後變為高電平
此處還有一個需要注意的地方:
如果是下降沿觸發中斷,即這種方式:EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
那麼會在擡起按鍵的瞬間進入中斷
如果是在上升沿觸發中斷,即這種方式:EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Rising;
那麼會在按下的瞬間進入中斷