外部中断
由外部设备发起的中断请求,会使得设备暂停当前的主程序,保存标志位并把当前指令压入堆栈,转而去执行中断的子程序。执行完毕后再弹出执行堆栈,恢复标志位,继续执行主程序。
STM32 的外部中断线
STM32的每个IO都可以作为外部中断输入。
STM32的中断控制器支持19个外部中断/事件请求:
线0~15:对应外部IO口的输入中断 PAx~PIx。
线16:连接到PVD输出。
线17:连接到RTC闹钟事件。
线18:连接到USB唤醒事件。
每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。
外部中断线与IO引脚对应关系
这时候想必读者可能要问了,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),STM32F103RCT6(51),那么中断线怎么跟IO口对应上呢?下图就是STM32的外部中断线和IO口的对应关系:
对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。
中断向量与服务函数
是不是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
中断初始化配置
//中断初始化配置函数
void EXTI_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
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);
//中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级为2,子优先级为0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线0; //使能中断线13
}
在 HAL 库中,中断处理初始化和 GPIO 初始化使用同一个结构体,如果想把他当作一个初始化中断结构体,只需为 GPIO_Initure.Mode= 赋值,HAL库即会把他当作一个中断来看待。在此时我们设置的是上升沿触发,也就是从低电平转到高电平时刻触发这个中断。
中断服务函数与服务回调
//中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);//调用中断处理公用函数
}
//中断服务回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//这里写中断程序
switch(GPIO_Pin){
case GPIO_PIN_0:
//GPIO0的中断程序
break;
case GPIO_PIN_13:
//GPIO13中断程序
break;
}
}
首先,当 EXTI0 有中断事件后,HAL 库会帮我们调用 EXTI0_IRQHandler() 这个函数,如果是 EXTIx 就以此类推。
之后我们需要在这个函数中调用 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0),他会帮我们调用
void HAL_GPIO_EXTI_Callback() 这个函数,在这个函数中就可以写我们的中断子程序了。这个流程是较为规范的,请不要省略其中的步骤。
示例代码
#include "sys.h"
#include "delay.h"
#include "led.h"
//中断初始化配置函数
void EXTI_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
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);
//中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); //抢占优先级为2,子优先级为0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线0; //使能中断线13
}
//中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);//调用中断处理公用函数
}
//中断服务回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
//这里写中断程序
switch(GPIO_Pin){
case GPIO_PIN_0:
//GPIO0的中断程序
break;
case GPIO_PIN_13:
//GPIO13中断程序
break;
}
}
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
LED_Init(); //初始化LED
EXTI_Init(); //外部中断初始化
while(1)
{
delay_ms(1000); //务必始终循环延迟,否则执行完程序就不相应中断了
}
}