STM32单片机中断及其应用
CubeMX新建工程,LED的IO口配置略,详细配置请参考:
STM32 CubeMX新建工程+GPIO的研究
中断概述
- 什么是中断?
中断是指计算机程序运行过程中,出现某些意外情况需要处理时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。 - 什么是EXTI?
外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。 - 什么是优先级?
1. 多个意外事情需要处理时,不同时间的优先程序
2. 先判断抢占优先级,数字越小,优先级越高;
3. 若抢占优先级相同,判断子优先级,同样,数字越小,优先级越高;
抢占优先级和响应优先级的区别:
1. 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
2. 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
3. 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
4. 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行 - 什么是优先级分组?
Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:
第0组:所有4位用于指定响应优先级
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级
- 什么是中断服务程序
每个中断源都有对应的处理程序,这个处理程序称为中断服务程序
外部中断配置
- 配置相关的RCC时钟及复用
- 初始化用来中断的IO口
- 初始化EXTI
- 配置NVIC(中断优先级)
- 编写中断服务函数
CubeMX相关配置及实现
-
时钟配置
- 采用外部高速时钟
- 时钟树配置
- 采用外部高速时钟
-
初始化EXTI及IO口
- EXTI及IO口初始化
2.相关参数配置
- EXTI及IO口初始化
-
配置NVIC中断优先级
STM32默认使用的规则是 NVIC_PriorityGroup_0 -
编写中断服务函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case KEY1_Pin: HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin); break;
case KEY2_Pin: HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin); break;
}
}
固件库相关程序配置及其实现
- 初始化相关的RCC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能APB2上挂载的GPIOA端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能复用功能时钟
- 初始化用来中断的IO口
GPIO_InitTypeDef GPIO_InitStructure; //GPIO结构体变量定义
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化
- 初始化EXTI
EXTI_InitTypeDef EXTI_InitStructure; //EXTI结构体变量定义
EXTI_InitStructure.EXTI_Line = EXTI_Line0; //触发线
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //外部中断
EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling; //下降沿触发
EXTI_Init(&EXTI_InitStructure); //初始化EXTI
- 配置NVIC(中断优先级)
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//外部中断源配置
// KEY NVIC中断优先级配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
- exti初始化函数定义
void exti_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //IO口结构体变量
EXTI_InitTypeDef EXTI_InitStructure; //EXTI结构体变量定义
NVIC_InitTypeDef NVIC1_InitStructure; //KEY1 NVIC 结构体定义
NVIC_InitTypeDef NVIC2_InitStructure; //KEY2 NVIC 结构体定义
//时钟相关配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //初始化GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能复用功能时钟
//中断引脚IO口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//EXTI 相关配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0 | GPIO_PinSource1);//外部中断源线配置
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling; //下降沿触发
EXTI_Init(&EXTI_InitStructure);
// KEY1 NVIC中断优先级配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2
NVIC1_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC1_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC1_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC1_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC1_InitStructure);
//KEY2 中断优先级配置
NVIC2_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC2_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC2_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC2_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC2_InitStructure);
}
- 编写中断服务函数
//外部中断0服务程序 KEY1
void EXTI0_IRQHandler(void)
{
delay_ms(10);
if( KEY1 == 0 )
LED1 = !LED1;
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
//外部中断1服务程序 KEY2
void EXTI1_IRQHandler(void)
{
delay_ms(10);
if(KEY2 == 0)
LED2 = !LED2;
EXTI_ClearITPendingBit(EXTI_Line1); //清除LINE1上的中断标志位
}