任務目的
使用EXTI(External Interrupt)外部中斷方式, 通過中斷服務函數對GPIO口進行控制, 使得LED燈可以進行亮滅翻轉.
原理圖分析
問題分析結束之後還是先來看原理圖的分析.
首先是LED燈部分:
由圖中可知, 若要控制紅燈(PB5), 則只需輸出引腳輸出爲0(低電平)即可.
然後再看下按鍵部分:
從圖中可知, 按鍵未按時爲低電平, 按下按鍵時爲高電平. 符合下拉輸入的邏輯.
EXTI外部中斷
簡介
外部中斷指的是通過GPIO檢測輸入的脈衝變化,從而引起中斷. 觸發方式爲邊沿觸發.
中斷
筆者使用的STM32(F103VET6)單片機使用的是Cortex-M3內核,中斷資源豐富.
外部中斷/事件控制器包含19個邊沿檢測器,用於產生中斷/事件請求。每個中斷線都可以獨立地配置它的觸發事件(上升沿或下降沿或雙邊沿),並能夠單獨地被屏蔽;有一個掛起寄存器維持所有中斷請求的狀態。 EXTI可以檢測到脈衝寬度小於內部APB2的時鐘週期。多達112個通用I/O口連接到16個外部中斷線.
具體中斷向量表不再列出, 詳情請參照STM32中文參考手冊(132/754).
這裏我們需要注意一下, 中斷線有EXTI_Line0~EXTI_Line15共16條(其實還有四條,不再詳細闡述,詳情參照參考手冊), 但是中斷向量只有EXTI0~EXTI4以及EXTI9_5, EXTI15_10共7箇中斷向量.使用時需注意這一點。
由上圖可以發現,按鍵所在的PA0引腳處於EXTI_Line0中斷線上,中斷向量爲EXTI0.
NVIC設置
說到中斷,就不得不說到NVIC(Nested Vectored Interrupt Controller)嵌套向量中斷控制器。因爲STM32的中斷十分豐富,所以NVIC的出現幾乎是必然的。那麼談到NVIC,利用庫函數開發的思想,就不難想到結構體的應用。NVIC的結構體定義如下:
typedef struct {
uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
This parameter can be a value of @ref IRQn_Type
(For the complete STM32 Devices IRQ Channels list, please
refer to stm32f10x.h file) */
uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
specified in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
will be enabled or disabled.
This parameter can be set either to ENABLE or DISABLE */
} NVIC_InitTypeDef;
NVIC的結構體共有三個成員,描述如下:
結構體成員名 | 介紹 |
---|---|
NVIC_IRQChannel | 需要配置的中斷向量名 |
NVIC_IRQChannelCmd | 使能/關閉中斷向量 |
NVIC_IRQChannelPreemptionPriority | 中斷搶佔優先級 |
NVIC_IRQChannelSubPriority | 響應優先級 |
Tip:什麼是搶佔優先級和響應優先級
中斷向量有兩個屬性:搶佔屬性和響應屬性,屬性編號越小,中斷優先級級別越高。
搶佔優先級運用在中斷嵌套之中,而響應優先級運用在中斷響應處理方面。下面給出一個例子(參考自Fire的《STM32庫開發實戰指南》):
以下是三個中斷向量:
中斷向量 | 搶佔優先級 | 響應優先級 |
---|---|---|
A | 0 | 0 |
B | 1 | 0 |
C | 1 | 1 |
當內核正在執行C的中斷服務函數時,則它可以被搶佔優先級更高的A打斷,但不能被B打斷。當B和C同時發生中斷,則優先處理響應優先級較高的B中斷的中斷服務函數。
NVIC的優先級組
在配置中斷優先級時,由於搶佔優先級和響應優先級共同分配一個4位的二進制數,則一共可以有
- 第 0 組: 所有 4 位用來配置響應優先級。即 16 種中斷向量具有都不相同的響應優先級。
- 第 1 組:最高 1 位用來配置搶佔優先級,低 3 位用來配置響應優先級。表示有 21=2 種級別的搶佔優先級(0 級,1 級),有 23=8 種響應優先級,即在 16 種中斷向量之中,有8 種中斷,其搶佔優先級都爲 0 級,而它們的響應優先級分別爲 0~7,其餘 8 種中斷向量的搶佔優先級則都爲 1 級,響應優先級別分別爲 0~7。
- 第 2 組:2 位用來配置搶佔優先級,2 位用來配置響應優先級。即 22=4 種搶佔優先級,22=4 種響應優先級。
- 第 3 組:高 3 位用來配置搶佔優先級,最低 1 位用來配置響應優先級。即有 8 種搶佔優先級,2 種響應 2 優先級。
- 第 4 組:所有 4 位用來配置搶佔優先級,即 NVIC 配置的
24=16 種中斷向量都是隻有搶佔屬性,沒有響應屬性。要配置這些優先級組,可以採用庫函數NVIC_PriorityGroupConfig()
,可輸入的參數爲NVIC_PriorityGroup_0 ~ NVIC_PriorityGroup_4
,分別爲以上介紹的 5 種分配組。
更直觀的圖像版描述如下(其中的佔先式優先級即搶佔式優先級,副優先級即爲響應優先級):
NVIC結構體程序設置
static void NVIC_Configuration(void) {
NVIC_InitTypeDef key_nvic;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
key_nvic.NVIC_IRQChannel = EXTI0_IRQn;
key_nvic.NVIC_IRQChannelCmd = ENABLE;
key_nvic.NVIC_IRQChannelPreemptionPriority = 0;
key_nvic.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&key_nvic);
}
EXTI設置
前面說了好多NVIC的內容,終於寫到了EXTI的處理內容。EXTI的設置核心也是結構體的設置。但是別忘了,由於外部中斷依託於GPIO,所以也要同時設置GPIO的結構體成員。
注意:外部中斷在同一時間內只能響應一個中斷.
結構體說明
EXTI的結構體定義如下:
typedef struct {
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination of @ref EXTI_Lines */
EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef */
EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef */
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
This parameter can be set either to ENABLE or DISABLE */
}EXTI_InitTypeDef;
從中可以看出其共有4個結構體成員,描述如下表:
結構體成員名 | 作用 |
---|---|
EXTI_Line | 明確外部中斷線 |
EXTI_LineCmd | 外部中斷線使能 |
EXTI_Mode | 外部中斷線的模式選擇,共有EXTI_Mode_Interrupt 和EXTI_Mode_Event 兩種模式,前者爲中斷觸發,後者不會立刻觸發中斷,而只是在 寄存器上把相應的事件標誌位置1,應用這個模式需要不停地查詢相應的寄存器 |
EXTI_Trigger | EXTI_Trigger_Rising :上升沿觸發; EXTI_Trigger_Falling :下降沿觸發;EXTI_Trigger_Rising_Falling :上升/下降沿觸發 |
結構體編程
void key_exti(void) {
GPIO_InitTypeDef key_struct;
EXTI_InitTypeDef key_exti_struct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
NVIC_Configuration();
key_struct.GPIO_Mode = GPIO_Mode_IPD; // pull down input
key_struct.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &key_struct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
key_exti_struct.EXTI_Line = EXTI_Line0;
key_exti_struct.EXTI_Mode = EXTI_Mode_Interrupt;
key_exti_struct.EXTI_Trigger = EXTI_Trigger_Falling;
key_exti_struct.EXTI_LineCmd = ENABLE;
EXTI_Init(&key_exti_struct);
}
中斷服務函數
中斷服務函數的定義與51系列單片機不同。必須與規定的中斷服務函數名相同。我們可以從啓動文件startup_stm32f10x_hd.s
中查詢:
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
DCD FSMC_IRQHandler ; FSMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
查得本次中斷服務函數的函數名爲:EXTI0_IRQHandler
。
編寫的中斷服務函數爲:
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
led_toggle(); // LED狀態翻轉
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
主函數
int main()
{
led_config();
key_exti();
while (1);
}