STM32外部中斷原理與配置


STM32-外部中斷原理與配置

IO口外部中斷原理概述

STM32控制器支持的外部中斷/事件請求

中斷線 M3 M4 M7
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線22:連接到RTC喚醒事件。  
EXSTI線23:連接到LPTIM1異步事件    

IO口外部中斷

STM32的每個IO都可以作為外部中斷輸入。

每個外部中斷線可以獨立的配置觸發方式(上升沿,下降沿或者雙邊沿觸發),觸發/屏蔽,專用的狀態位。

STM32供IO使用的中斷線只有16個,但是STM32F系列的IO口多達上百個,STM32F103ZGT6(112),那么中斷線怎么跟io口對應呢?

GPIO和中斷線映射關系

GPIOx.0映射到EXTI0

GPIOx.1映射到EXTI1

GPIOx.14映射到EXTI14

GPIOx.15映射到EXTI15

對於M4/M7,配置寄存器為SYSCFG_EXTIRx

對於M3,配置寄存器為AFIO_EXTICRx

如下圖所示,EXTI0[3:0]有4個位,可以配置16個,所以可以從PA0選擇到PI0。也就是說16個中斷線,最多可以處理16*16個外部引腳的中斷。

在這里插入圖片描述

可以在手冊中找到SYSCFG 外部中斷配置寄存器:

在這里插入圖片描述

16個中斷線就分配16個中斷服務函數?

IO口外部中斷在中斷向量表中只分配了7個中斷向量,也就是只能使用7個中斷服務函數。

在這里插入圖片描述

從表中可以看出,外部中斷線5~ 9分配一個中斷向量,共用一個服務函數外部中斷線10~15分配一個中斷向量,共用一個中斷服務函數。

中斷服務函數列表:

EXTI0_IRQHandler

EXTI1_IRQHandler

EXTI2_IRQHandler

EXTI3_IRQHandler

EXTI4_IRQHandler

EXTI9_5_IRQHandler

EXTI15_10_IRQHandler

IO口外部中斷HAL庫配置方法

外部中斷操作使用到的函數分布文件

stm32fxxx_hal_gpio.h

stm32fxxx_hal_gpio.c

外部中斷配置:

外部中斷的中斷線映射配置和觸發方式都是在GPIO初始化函數中完成:

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_0; //PA0

GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿觸發

GPIO_Initure.Pull=GPIO_PULLDOWN;

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) { uint32_t position; uint32_t ioposition = 0x00; uint32_t iocurrent = 0x00; uint32_t temp = 0x00; /* Check the parameters */ assert_param(IS_GPIO_ALL_INSTANCE(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Init->Pin)); assert_param(IS_GPIO_MODE(GPIO_Init->Mode)); assert_param(IS_GPIO_PULL(GPIO_Init->Pull)); /* Configure the port pins */ for(position = 0; position < GPIO_NUMBER; position++) { /* Get the IO position */ ioposition = ((uint32_t)0x01) << position; /* Get the current IO position */ iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition; if(iocurrent == ioposition) { /*--------------------- GPIO Mode Configuration ------------------------*/ /* In case of Alternate function mode selection */ if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) { /* Check the Alternate function parameter */ assert_param(IS_GPIO_AF(GPIO_Init->Alternate)); /* Configure Alternate function mapped with the current IO */ temp = GPIOx->AFR[position >> 3]; temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07) * 4)); GPIOx->AFR[position >> 3] = temp; } /* Configure IO Direction mode (Input, Output, Alternate or Analog) */ temp = GPIOx->MODER; temp &= ~(GPIO_MODER_MODER0 << (position * 2)); temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2)); GPIOx->MODER = temp; /* In case of Output or Alternate function mode selection */ if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) { /* Check the Speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); /* Configure the IO Speed */ temp = GPIOx->OSPEEDR; temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2)); temp |= (GPIO_Init->Speed << (position * 2)); GPIOx->OSPEEDR = temp; /* Configure the IO Output Type */ temp = GPIOx->OTYPER; temp &= ~(GPIO_OTYPER_OT_0 << position) ; temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4) << position); GPIOx->OTYPER = temp; } /* Activate the Pull-up or Pull down resistor for the current IO */ temp = GPIOx->PUPDR; temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2)); temp |= ((GPIO_Init->Pull) << (position * 2)); GPIOx->PUPDR = temp; /*--------------------- EXTI Mode Configuration ------------------------*/ /* Configure the External Interrupt or event for the current IO */ if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE) { /* Enable SYSCFG Clock */ __HAL_RCC_SYSCFG_CLK_ENABLE(); temp = SYSCFG->EXTICR[position >> 2]; temp &= ~(((uint32_t)0x0F) << (4 * (position & 0x03))); temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4 * (position & 0x03))); SYSCFG->EXTICR[position >> 2] = temp; /* Clear EXTI line configuration */ temp = EXTI->IMR; temp &= ~((uint32_t)iocurrent); if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT) { temp |= iocurrent; } EXTI->IMR = temp; temp = EXTI->EMR; temp &= ~((uint32_t)iocurrent); if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT) { temp |= iocurrent; } EXTI->EMR = temp; /* Clear Rising Falling edge configuration */ temp = EXTI->RTSR; temp &= ~((uint32_t)iocurrent); if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE) { temp |= iocurrent; } EXTI->RTSR = temp; temp = EXTI->FTSR; temp &= ~((uint32_t)iocurrent); if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE) { temp |= iocurrent; } EXTI->FTSR = temp; } } } } 

和串口中斷一樣,HAL庫同樣提供了外部中斷通用處理函數HAL_GPIO_EXTI_IRQHandler,我們在外部中斷服務函數中會調用該函數處理中斷。

//中斷服務函數 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);//調用中斷處理公用函數 } void EXTI2_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);//調用中斷處理公用函數 } 

HAL_GPIO_EXTI_IRQHandler函數內部通過判斷中斷來源引腳,最終調用外部中斷回調函數HAL_GPIO_EXTI_Callback來處理中斷。

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); HAL_GPIO_EXTI_Callback(GPIO_Pin); } } 

然后找到定義,可以看到HAL_GPIO_EXTI_Callback是一個弱函數

__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { /* Prevent unused argument(s) compilation warning */ __IO uint32_t tmpreg = 0x00; UNUSED(tmpreg); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_GPIO_EXTI_Callback could be implemented in the user file */ } 

用戶最終編寫中斷處理回調函數來編寫中斷處理邏輯

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { switch(GPIO_Pin) { case GPIO_PIN_0: //控制邏輯 break; case GPIO_PIN_2: //控制邏輯 break; } } 

外部中斷的一般配置步驟:

① 使能IO口時鍾。

② 初始化IO口,設置觸發方式:HAL_GPIO_Init();

③ 設置中斷優先級,並使能中斷通道。

④ 編寫中斷服務函數:函數中調用外部中斷通用處理函數HAL_GPIO_EXTI_IRQHandler。

⑤ 編寫外部中斷回調函數:HAL_GPIO_EXTI_Callback;

按鍵硬件連接

key0按下低電平,松開應該是高電平,所以key0設置為上拉輸入,松開的時候高,按下低,所以按下的時候是下降沿,因此就下降沿觸發。

在這里插入圖片描述

KEY0->PH3 上拉輸入,下降沿觸發

KEY1->PH2 上拉輸入,下降沿觸發

KEY2->PC13 上拉輸入,下降沿觸發

WK_UP->PA0 下拉輸入,上升沿觸發

IO口外部中斷實驗

按鍵KEY0按下: 同時控制LED0和LED1翻轉。

按鍵KEY1按下: LED1狀態翻轉。

按鍵KEY2按下: LED0翻轉。

按鍵WK_UP按下:控制LED0和LED1互斥點亮。

根據外部中斷的配置的五個步驟,可以寫出代碼:

① 使能IO口時鍾。

② 初始化IO口,設置觸發方式:HAL_GPIO_Init();

③ 設置中斷優先級,並使能中斷通道。

④ 編寫中斷服務函數:函數中調用外部中斷通用處理函數HAL_GPIO_EXTI_IRQHandler。

⑤ 編寫外部中斷回調函數:HAL_GPIO_EXTI_Callback;

首先編寫exti.h文件:

#ifndef _EXTI_H #define _EXTI_H #include "sys.h" void EXTI_Init(void); #endif 

然后編寫exti.c文件

寫出里面要用到的函數

#include "exti.h" void EXTI_Init(void) { } void EXTI0_IRQHandler(void) { } void EXTI2_IRQHandler(void) { } void EXTI3_IRQHandler(void) { } void EXTI15_10_IRQHandler(void) { } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { } 

然后進行函數編寫,首先是 使能IO口時鍾、初始化IO口,設置觸發方式,然后是設置中斷優先級,並使能中斷通道

void EXTI_Init(void) { //使能IO口時鍾、初始化IO口,設置觸發方式 GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_GPIOA_CLK_ENABLE(); //開啟GPIOA時鍾 __HAL_RCC_GPIOC_CLK_ENABLE(); //開啟GPIOC時鍾 __HAL_RCC_GPIOH_CLK_ENABLE(); //開啟GPIOH時鍾 GPIO_Initure.Pin=GPIO_PIN_0; //PA0 GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //IT是外部中斷 RISING上升沿觸發 GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 HAL_GPIO_Init(GPIOA,&GPIO_Initure); GPIO_Initure.Pin=GPIO_PIN_13; //PC13 GPIO_Initure.Mode=GPIO_MODE_IT_FALLING; //下降沿觸發 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 HAL_GPIO_Init(GPIOC,&GPIO_Initure); GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3 HAL_GPIO_Init(GPIOH,&GPIO_Initure); //設置中斷優先級,並使能中斷通道 HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); HAL_NVIC_EnableIRQ(EXTI2_IRQn); HAL_NVIC_SetPriority(EXTI2_IRQn,2,1); HAL_NVIC_EnableIRQ(EXTI3_IRQn); HAL_NVIC_SetPriority(EXTI3_IRQn,2,2); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3); } 

然后在后面編寫中斷服務函數:函數中調用外部中斷通用處理函數HAL_GPIO_EXTI_IRQHandler。和 編寫外部中斷回調函數:HAL_GPIO_EXTI_Callback;

void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } void EXTI2_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); } void EXTI3_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); } void EXTI15_10_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13); } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { delay_ms(100);//消抖 switch(GPIO_Pin) { case GPIO_PIN_0://WK_UP if(WK_UP==1) { LED1=!LED1; LED0=!LED1; } break; case GPIO_PIN_13://KEY2 if(KEY2==1) { LED0=!LED0; } break; case GPIO_PIN_2://KEY1 if(KEY1==1) { LED1=!LED1; } break; case GPIO_PIN_3://KEY0 if(KEY0==1) { LED0=!LED0; LED1=!LED1; } break; } } 

然后開始寫main函數:

#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "key.h" #include "exti.h" int main(void) { HAL_Init(); //初始化HAL庫 Stm32_Clock_Init(360,25,2,8); //設置時鍾,180Mhz delay_init(180); //初始化延時函數 uart_init(115200); //初始化USART LED_Init(); //初始化LED EXTI_Init(); while(1) { printf("OK\r\n"); delay_ms(1000) } } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM