中断就是当优先级低的事件运行的时候高优先级的事情也要发生时,高优先级的事件会打断低优先级的事件,而低优先级事件被打断后当高优先级事件运行完毕低优先级会继续运行。
stm32里面中断的配备步骤:
上图为图1。
根据KEY的原理图可知KEY0和KEY1应设置为下降沿触发。
下面为EXTI初始化函数的代码实现(包括1到6步):
void exti_init(void)
{
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_3;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); //设置优先级分组
HAL_NVIC_SetPriority(EXTI4_IRQn,0,1); //设置优先级,此为抢占优先级为0,响应优先级为1
HAL_NVIC_EnableIRQ(EXTI4_IRQn); //使能中断
HAL_NVIC_SetPriority(EXTI3_IRQn,1,1); //设置优先级,此为抢占优先级为1,响应优先级为1
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中断
}
首先__HAL_RCC_GPIOE_CLK_ENABLE();进行GPIO使能
之后设置GPIO输入模式,此部分不多做赘述同KEY初始化里部分相同,只是将Mode改为下降沿触发。下图为相关设置宏定义,依次为:
1.带上升沿触发检测的外部中断模式。
2.带下降沿触发检测的外部中断模式。
3.具有上升沿/下降沿触发检测的外部中断模式。
根据图1可知exti的配备步骤已完成1到5接下来实现第6步设置NVIC,设置NVIC分为3步:
1.设置优先级分组
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
这里我设置的是分组2即形参为:NVIC_PRIORITYGROUP_2;
根据HAL库中设置优先级分组函数的介绍和实现。可知可以设置5种情况,即NVIC_PRIORITYGROUP_[0~4];这里解释一下每种情况代表着什么。
首先优先级分为抢占优先级和响应优先级,而我们判断一个事件的优先级先比较它的抢占优先级如果抢占优先级相等那么我们再比较响应优先级。此处以形参设置为NVIC_PRIORITYGROUP_1举例。首先优先级分组是由4位二进制数确定的。那么NVIC_PRIORITYGROUP_1就是将抢占优先级设置1位响应优先级设置3位。接下来结合设置优先级函数来看。
2.设置优先级函数
当设置优先级分组为NVIC_PRIORITYGROUP_1时下面的函数第二个形参就可以设置成0或1(因为抢占优先级设置成1位,而1位二进制数只有两种情况),最后一个形参可以设置0到7(因为响应优先级设置成3位,而3位二进制数只有八种情况)。所以设置优先级函数的后两个形参我们已经确定。根据下面HAL库中对此函数的解释能知道第一个形参是外部中断编号。
HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
下图为部分外部中断编号,可以通过转到定义自行查看,这里解释一下外部中断信号的划分。
PIN为0~15共16个。而针对PIN0~15HAL的中断编号只划分为7个,0到4各一个,9到5共用一个,15到10又共用一个。下图只截取了0到4的部分。我们可以知道KEY0对应是PIN_4所以这里就应该选择EXTI4_IRQn。到此设置优先级函数所有形参全部介绍完毕。
3.使能中断
使能中断函数:void HAL_NVIC_EnableIRQ(IRQn_Type IRQn),此函数形参为外部中断编号,具体设置和设置优先级函数的外部中断编号一样。
到此EXTI函数的初始化已经完成。
最后设计中断服务函数也就是整个配备步骤的最后一步。
以下为中断服务函数实现过程。
这里我们只需要编写中断服务函数和回调函数即可,而中断服务函数里只调用了HAL库中断处理公用函数,这是HAL封装好的不用我们编写。具体实现以KEY0的中断服务函数为例:
void EXTI4_IRQHandler(void) //key0的中断服务函数
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); //调用中断处理公用函数
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); //清中断
}
清中断操作是防止按键抖动因为中断处理共用函数里默认是先清中断再调用回调函数。
上图第一个就是HAL库中断处理公用函数,可以看到是先清中断再调用回调函数,而下面的回调函数设置为 __weak 即需要我们用户自行编写。
下面为我程序中回调函数的编写:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) //中断回调函数
{
switch(GPIO_Pin)
{
case GPIO_PIN_4:
if(readkey0() == 1)
{
led0_TogglePin(); //翻转LED0函数
delay_ms(5000);
led0_TogglePin(); //翻转LED0函数
}
break;
case GPIO_PIN_3:
if(readkey1() == 1)
{
led1_TogglePin(); //翻转LED1函数
delay_ms(5000);
led1_TogglePin(); //翻转LED1函数
}
break;
}
}
此处首先说明中断中尽量不能有延时!!!这里我是为了观察现象来设置了5秒的延时。而且这里根据我个人理解不应该再调用其他用户自己编写的函数如readkey0()和led0_TogglePin(),应该用HAL库里的函数来实现想要的功能(纯个人理解)。我回调函数实现的功能就是通过KEY来控制LED灯的亮灭。KEY0被按下LED0亮起5秒后再熄灭,KEY1被按下LED1亮起5秒后再熄灭。最后讲解一下怎么判断是否发生了中断,因为在EXTI初始化函数里我将EXIT4_IRQn的抢占优先级设置为0,EXIT3_IRQn的抢占优先级设置为1,所以如果当先按下KEY1后LED1会亮起5秒,但是在LED1亮起过程中我按下KEY0后LED0也会亮起5秒这时LED1的状态会暂停也就是停在了延时5秒的这个阶段(LED1并不会熄灭),当LED0灭了之后也就是LED0亮起5秒过后LED1会继续之前的过程也就是完成延时五秒的部分。最后的现象就是LED1亮起的时间会是10秒因为自身的5秒再加上LED0亮起的5秒。
下面是完整的程序代码:
1.exit.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
void exti_init(void)
{
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_3;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); //设置优先级分组
HAL_NVIC_SetPriority(EXTI4_IRQn,0,1); //设置优先级,此为抢占优先级为0,响应优先级为1
HAL_NVIC_EnableIRQ(EXTI4_IRQn); //使能中断
HAL_NVIC_SetPriority(EXTI3_IRQn,1,1); //设置优先级,此为抢占优先级为1,响应优先级为1
HAL_NVIC_EnableIRQ(EXTI3_IRQn); //使能中断
}
void EXTI4_IRQHandler(void) //key0的中断服务函数
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); //调用中断处理公用函数
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4); //清中断
}
void EXTI3_IRQHandler(void) //key0的中断服务函数
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); //调用中断处理公用函数
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清中断
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) //中断回调函数
{
switch(GPIO_Pin)
{
case GPIO_PIN_4:
if(readkey0() == 1)
{
led0_TogglePin(); //翻转LED0函数
delay_ms(5000);
led0_TogglePin(); //翻转LED0函数
}
break;
case GPIO_PIN_3:
if(readkey1() == 1)
{
led1_TogglePin(); //翻转LED1函数
delay_ms(5000);
led1_TogglePin(); //翻转LED1函数
}
break;
}
}
2.exit.h
#ifndef __EXTI_H
#define __EXTI_H
#include "./SYSTEM/sys/sys.h"
void exti_init(void); /* 外部中断初始化 */
#endif
3.main.h
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */
delay_init(72); /* 初始化延时函数 */
led_init(); /* 初始化LED */
key_init();
exti_init();
while(1)
{
}
}
此处的while循环不能删!