使用STM32F407
的EXTI
中断功能需要使用HAL
库中的stm32f4xx_hal_exti.h
和stm32f4xx_hal_exti.c
.
STM32F4
的每个GPIO
都可设置为 EXTI
中断 功能。STM32F4
有16
路EXTI
中断线EXTI0~EXTI15
,所有GPIOx
的GPIO_PIN_0
对应EXTI0
,所有GPIOx
的GPIO_PIN_1
对应EXTI1
… 所有GPIOx
的GPIO_PIN_15
对应EXTI15
.
NVIC
配置 关于NVIC
中断管理的函数定义在stm32f4xx_hal_cortex.c
中。
/* 设置全局中断优先级分组,此函数只需调用一次
参数:
NVIC_PRIORITYGROUP_0
NVIC_PRIORITYGROUP_1
NVIC_PRIORITYGROUP_2
NVIC_PRIORITYGROUP_3
NVIC_PRIORITYGROUP_4 */
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
PriorityGroup |
PreemptPriority |
SubPriority |
---|---|---|
NVIC_PRIORITYGROUP_0 |
none |
0~15 |
NVIC_PRIORITYGROUP_1 |
0~1 |
0~7 |
NVIC_PRIORITYGROUP_2 |
0~3 |
0~3 |
NVIC_PRIORITYGROUP_3 |
0~7 |
0~1 |
NVIC_PRIORITYGROUP_4 |
0~15 |
none |
/* 设置指定中断事件的 抢占优先级 和 响应优先级
参数:
(IRQ_Type)IRQn - 中断名
(uint32_t)PreemptPriority - 抢占优先级
(uint32_t)SubPriority - 响应优先级 */
void HAL_NVIC_SetPriority(IRQn_Type IRQn,uint32_t PreemptPriority,uint32_t SubPriority);
/* 开启指定中断 */
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
/* 关闭指定中断 */
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);
STM32
单片机的优先级有两个属性:抢占优先级 和 响应优先级(又称子优先级),优先级编号越小其优先级越高。
抢占优先级(PreemptPriority
):高抢占优先级的中断程序可以打断低抢占优先级的中断程序。即如果低抢占优先级的中断程序正在运行,此时发生高抢占优先级的中断事件,高抢占优先级的中断程序可以打断正在运行的低抢占优先级程序。
响应优先级(SubPriority
):当抢占优先级相同时,高响应优先级的中断程序 先于 低响应优先级的中断程序执行。响应优先级只能处理同时发生的中断事件,而不能打断正在处理的中断事件。
如果两个中断事件的抢占优先级和响应优先级都相同,先发生的中断事件先被处理。
总结:抢占优先级(PreemptPriority
)用于管理中断事件的处理权,未开始执行的中断程序(高抢占优先级)可以抢占正在执行的中断程序(低抢占优先级)的执行权。响应优先级(SubPriority
)用于管理同时发生的中断事件的处理顺序,未开始执行的中断程序不能打断正在执行的中断程序。
GPIO
配置GPIO
初始化GPIO_InitTypeDef GPIO_Init;
/* GPIOx时钟初始化。注意,IO中断仅需初始化对应GPIO的时钟即可 */
__HAL_RCC_GPIOx_CLK_ENABLE();
GPIO_Init.Pin = GPIO_PIN_0;
/* GPIO_MODE_IT_RISING (外部事件,上升沿触发);
GPIO_MODE_IT_FALLING (外部事件,下降沿触发);
GPIO_MODE_IT_RISING_FALLING (外部事件,上升沿 或 下降沿触发). */
// 上升沿触发
GPIO_Init.Mode = GPIO_MODE_IT_RISING;
// 上升沿触发,可选择下拉或者无拉
GPIO_Init.Pull = GPIO_PULLDOWN;
GPIO_Init.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_Init);
EXTI
中断事件名typedef enum {
...
EXTI0_IRQn = 6, //IO中断事件0, 对应 GPIOx 的 GPIO_PIN_0
EXTI1_IRQn = 7, //IO中断事件1, 对应 GPIOx 的 GPIO_PIN_1
EXTI2_IRQn = 8, //IO中断事件2, 对应 GPIOx 的 GPIO_PIN_2
EXTI3_IRQn = 9, //IO中断事件3, 对应 GPIOx 的 GPIO_PIN_3
EXTI4_IRQn = 10, //IO中断事件4, 对应 GPIOx 的 GPIO_PIN_4
EXTI9_5_IRQn = 23, //IO中断事件5~9, 对应 GPIOx 的 GPIO_PIN_5 ~ GPIO_PIN_9
EXTI15_10_IRQn = 40, //IO中断事件10~15, 对应 GPIOx 的 GPIO_PIN_10 ~ GPIO_PIN_15
...
} IRQn_Type
示例:
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);
HAL_NVIC_SetPriority(EXTI0_IRQn,0,0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
GPIO_Pin
对于GPIO_PIN_5 ~ GPIO_PIN_9
和GPIO_PIN_10 ~ GPIO_PIN_15
,中断服务函数仅有EXTI9_5_IRQHandler()
和EXTI15_10_IRQHandler()
,发生中断时,不能直接得到触发中断的GPIO
,需要借助具体函数判断:
/* 判断指定的GPIO_Pin是否触发了EXTI中断。
参数:GPIO_Pin - 指定的GPIO_Pin
返回:
(ITStatus)SET - 指定的GPIO_Pin触发了EXTI中断
(ITStatus)RESET - 指定的GPIO_Pin未触发EXTI中断
*/
ITStatus __HAL_GPIO_EXTI_GET_IT(uint16_t GPIO_Pin);
示例:
void EXTI9_5_IRQHandler(void) {
uint16_t GPIO_Pin;
// Get the GPIO_Pin which requsted EXTI handle
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_5) != RESET) {
GPIO_Pin = GPIO_PIN_5;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_6) != RESET) {
GPIO_Pin = GPIO_PIN_6;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_7) != RESET) {
GPIO_Pin = GPIO_PIN_7;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_8) != RESET) {
GPIO_Pin = GPIO_PIN_8;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_9) != RESET) {
GPIO_Pin = GPIO_PIN_9;
}
HAL_GPIO_EXTI_IRQHandler(GPIO_Pin);
}
void EXTI15_10_IRQHandler(void) {
uint16_t GPIO_Pin;
// Get the GPIO_Pin which requsted EXTI handle
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_10) != RESET) {
GPIO_Pin = GPIO_PIN_10;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_11) != RESET) {
GPIO_Pin = GPIO_PIN_11;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_12) != RESET) {
GPIO_Pin = GPIO_PIN_12;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_13) != RESET) {
GPIO_Pin = GPIO_PIN_13;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_14) != RESET) {
GPIO_Pin = GPIO_PIN_14;
} else if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_15) != RESET) {
GPIO_Pin = GPIO_PIN_15;
}
HAL_GPIO_EXTI_IRQHandler(GPIO_Pin);
}
/* EXTI0_IRQn */
void EXTI0_IRQHandler(void) {}
/* EXTI1_IRQn */
void EXTI1_IRQHandler(void) {}
/* EXTI2_IRQn */
void EXTI2_IRQHandler(void) {}
/* EXTI3_IRQn */
void EXTI3_IRQHandler(void) {}
/* EXTI4_IRQn */
void EXTI4_IRQHandler(void) {}
/* EXTI5_IRQn~EXTI9_IRQn */
void EXTI9_5_IRQHandler(void) {}
/* EXTI10_IRQn~EXTI15_IRQn */
void EXTI15_10_IRQHandler(void) {}
使用中断服务函数处理中断事件时,需要在中断服务函数中手动清除中断标志。HAL
库提供了更为简洁的中断处理方式:通用入口函数+中断回调函数。
通用入口函数HAL_GPIO_EXTI_IRQHandler()
定义在stm32f4xx_hal_gpio.c
中。
/* 通用入口函数的组成:
1.清除中断标志:__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
2.中断处理逻辑:HAL_GPIO_EXTI_Callback(GPIO_Pin); */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) {
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) {
// 清除中断标志
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
// 调用中断回调函数,实现中断处理逻辑
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
通用入口函数的目的是封装中断标志的清除处理。直接在中断服务函数中调用通用入口函数即可,传入对应的参数,不必手动清除中断标志。例:
void EXTI0_IRQHandler(void) {
// 调用通用入口函数
HAL_GPIO_EXTI_IRQHandler(GOIO_PIN_0);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
// 中断处理逻辑
}
中断回调函数HAL_GPIO_EXTI_Callback()
定义在stm32f4xx_hal_gpio.c
中。中断回调函数实现中断处理逻辑,被通用入口函数调用。只需正确声明定义中断回调函数,而无需显式调用。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
// 中断处理逻辑
}
使用HAL
库实现EXTI
中断的函数调用流程为:
/* 中断向量函数,在中断向量函数中调用
__HAL_GPIO_EXTI_GET_IT(GPIO_Pin)判断指定的GPIO_Pin是否触发中断 */
1. void EXTIx_IRQHandler(void);
/* 通用入口函数,清除中断标志,被 HAL_GPIO_EXTI_Callback() 调用 */
2. void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
/* 中断回调函数,用户定义中断处理逻辑 */
3. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
main.c
#include "stm32f4xx.h"
/* define */
#define LED_Port GPIOC
#define LED_Pin GPIO_PIN_0
#define LED_ON HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_RESET)
#define LED_OFF HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_SET)
#define LED_Toggle HAL_GPIO_TogglePin(LED_Port,LED_Pin)
#define Key_IT EXTI1_IRQn
#define Key_Port GPIOC
#define Key_Pin GPIO_PIN_1
#define Key_LED_CLK_ENABLE __HAL_RCC_GPIOC_CLK_ENABLE()
#define Key_LED_IT_ENABLE HAL_NVIC_EnableIRQ(Key_IT)
/* declare */
void Key_LED_Init(void);
HAL_StatusTypeDef SysClk_Init(uint32_t m,uint32_t n,uint32_t p,uint32_t q);
int main(void) {
HAL_Init();
SysClk_Init(8,336,2,7);
/* set system NVIC group */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);
Key_LED_Init();
while(1);
}
void Key_LED_Init(void) {
GPIO_InitTypeDef GPIO_Init;
/* #define Key_LED_CLK_ENABLE __HAL_RCC_GPIOC_CLK_ENABLE() */
Key_LED_CLK_ENABLE;
/* #define LED_Pin GPIO_PIN_0 */
GPIO_Init.Pin = LED_Pin;
GPIO_Init.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init.Pull = GPIO_NOPULL;
GPIO_Init.Speed = GPIO_SPEED_LOW;
/* #define LED_Port GPIOC */
HAL_GPIO_Init(LED_Port,&GPIO_Init);
// LED turn off when system reset
/* #define LED_ON HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_RESET)
#define LED_OFF HAL_GPIO_WritePin(LED_Port,LED_Pin,GPIO_PIN_SET) */
LED_OFF;
GPIO_Init.Pin = Key_Pin;
GPIO_Init.Mode = GPIO_MODE_IT_FALLING;
GPIO_Init.Pull = GPIO_PULLUP;
GPIO_Init.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(Key_Port,&GPIO_Init);
/* #define Key_IT EXTI1_IRQn */
HAL_NVIC_SetPriority(Key_IT,0,0);
/* #define Key_LED_IT_ENABLE HAL_NVIC_EnableIRQ(Key_IT) */
Key_LED_IT_ENABLE;
}
/* SYSCLK = PLLCLK = HSE*n/(m*p)
AHBCLK = HCLK = SYSCLK/1
APB1CLK = PCLK1 = HCLK/4
APB2CLK = PCLK2 = HCLK/2
return :
HAL_OK - Config succeed.
HAL_ERROR - Config failed. */
/* If HSE = 8MHz,call SysClk_Init(8,336,2,7) will set to :
SYSCLK = PLLCLK = 8*336/(8*2) MHz = 168MHz
AHBCLK = HCLK = SYSCLK/1 = 168/1 MHz = 168MHz
APB1CLK = PCLK1 = HCLK/4 = 168/4 MHz = 42MHz
APB2CLK = PCLK2 = HCLK/2 = 168/2 MHz = 84MHz */
HAL_StatusTypeDef SysClk_Init(uint32_t m,uint32_t n,uint32_t p,uint32_t q) {
HAL_StatusTypeDef status = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;
/* Enable PWR clock. */
__HAL_RCC_PWR_CLK_ENABLE();
/* Config voltage scaler to PWR_REGULATOR_VOLTAGE_SCALE1.
This configuration is for balancing performance and power consumpion. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Clock source choose HSE clock */
RCC_OscInitStructure.OscillatorType = RCC_OSCILLATORTYPE_HSE;
/* Open HSE clock */
RCC_OscInitStructure.HSEState = RCC_HSE_ON;
/* Open PLL clock */
RCC_OscInitStructure.PLL.PLLState = RCC_PLL_ON;
/* PLL clock source choose HSE clock */
RCC_OscInitStructure.PLL.PLLSource = RCC_PLLSOURCE_HSE;
/* Range from 2 to 63 */
RCC_OscInitStructure.PLL.PLLM = m;
/* Range from 64 to 432 */
RCC_OscInitStructure.PLL.PLLN = n;
/* Choose one from 2,4,6,8 */
RCC_OscInitStructure.PLL.PLLP = p;
/* Range from 2 to 15 */
RCC_OscInitStructure.PLL.PLLQ = q;
status = HAL_RCC_OscConfig(&RCC_OscInitStructure);
if(status != HAL_OK) {return HAL_ERROR;}
/* Choose PLL clock as system clock and config HCLK,PCLK1,PCLK2 */
RCC_ClkInitStructure.ClockType = (RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_HCLK
|RCC_CLOCKTYPE_PCLK1
|RCC_CLOCKTYPE_PCLK2);
/* HCLK = SYSCLK/1 */
RCC_ClkInitStructure.AHBCLKDivider = RCC_SYSCLK_DIV1;
/* PCLK1 = HCLK/4 */
RCC_ClkInitStructure.APB1CLKDivider = RCC_HCLK_DIV4;
/* PCLK2 = HCLK/2 */
RCC_ClkInitStructure.APB2CLKDivider = RCC_HCLK_DIV2;
status = HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_5);
if(status != HAL_OK) {return HAL_ERROR;}
if(HAL_GetREVID() == 0x1001) { __HAL_FLASH_PREFETCH_BUFFER_ENABLE();}
return HAL_OK;
}
/* 中断向量函数 */
void EXTI1_IRQHandler(void) {
/* 调用通用入口函数 */
HAL_GPIO_EXTI_IRQHandler(Key_Pin);
}
/* 中断回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
// 按下按键,LED明暗切换
if(GPIO_Pin == Key_Pin) {
/* #define LED_Toggle HAL_GPIO_TogglePin(LED_Port,LED_Pin) */
LED_Toggle;
}
}