NVIC:嵌套向量中断控制器,全称Nested vectored interrupt controller。
EXTI:外部中断和事件控制器。
一般情况下中断分为5组,如下图1。当优先级分组为3时,该组所有的中断有三位设置抢占优先级,范围为0~7,有一位设置子优先级,范围0~1,数值越小,优先级越高。中断分组在HAL库初始化中被HAL_Init()设置,一般情况下不需要去修改。
图1 中断分组设置表
中断分组不建议看的原因与抢占优先级与子优先级有关,例如上面提到的分组3,在实际使用中中断优先级、子优先级均超过对应范围,在这种情况下不会报错也不会出现警告,也就是说中断分组对我使用外部中断造成了困扰,因此我不建议了解本部分内容。
中断优先级可以分为抢占式优先级和响应优先级(子优先级),每个中断源都需要被指定这两种优先级。抢占优先级高的中断可以打断正在执行的抢占优先级低的中断;抢占优先级相同情况下,子优先级高的中断不能打断子优先级低的中断。
EXTI:外部中断和事件控制器,是由20个产生事件/中断请求的边沿检测器组成。每一条输入线都可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
EXTI支持23个外部中断/事件请求,这些都是信息输入端,也就是上面提及到了输入线,具体如下:
EXTI线0~15:对应外部IO口的输入中断
EXTI线16:连接到PVD输出
EXTI线17:连接到RTC闹钟事件
EXTI线18:连接到USB唤醒事件
EXTI线19:连接到以太网唤醒事件
EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件
EXTI线21:连接到RTC入侵和时间戳事件
EXTI线22:连接到RTC唤醒事件
从上面可以看出,GD32供给IO口使用的中断线只有16个,但是STM32F407的IO口却远远不止16个,所以STM32把GPIO管脚GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线0~15。这样子每个中断线对应了最多7个IO口,而中断线每次只能连接到1个IO口上,这样就需要通过配置决定对应的中断线配置到哪个GPIO上了。
例:PA0对应的中断线是0,对应的中断服务函数是EXTI0_IRQHandler();PE4对应的中断线是4, 对应的中断服务函数是EXTI0_IRQHandler();
/********************************************************
* 函数描述:设置中断的优先级分组。
* 函数形参:PriorityGroup,中断优先级分组号,NVIC_PRIORITYGROUP_0到
NVIC_PRIORITYGROUP_4(共5组)。
* 返回值:无
*********************************************************/
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
/********************************************************
* 函数描述:用于设置中断的抢占优先级和响应优先级(子优先级)。
* 函数形参:IRQn,中断号,可以选择范围在IRQn_Type定义的枚举类型中
* PreemptPriority:是抢占优先级,0~15
* SubPriority:响应优先级,0~15
* 返回值:无
*********************************************************/
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
/********************************************************
* 函数描述:用于使能中断。
* 函数形参:IRQn,中断号,可以选择范围在IRQn_Type定义的枚举类型中
* 返回值:无
*********************************************************/
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOE时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_4; //PE4
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOE,&GPIO_Initure);
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY_UP按下 即WK_UP
//注意此函数有响应优先级,KEY0>KEY_UP!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1; //按键松开标志
if(mode==1)key_up=1; //支持连按
if(key_up&&(KEY0==1||WK_UP==1))
{
delay_ms(10); //去抖动
key_up=0;
if(KEY0==1) return KEY0_PRES;
else if(WK_UP==1) return WKUP_PRES;
}else if(KEY0==0&&WK_UP==0)key_up=1;
return 0; //无按键按下
}
//外部中断初始化
void EXTI_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOE_CLK_ENABLE(); //开启GPIOE时钟
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);
GPIO_Initure.Pin=GPIO_PIN_4; //PE4
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOE,&GPIO_Initure);
//中断线0-PA0
HAL_NVIC_SetPriority(EXTI0_IRQn,0,2); //抢占优先级为0,子优先级为2
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线0
//中断线4-PE4
HAL_NVIC_SetPriority(EXTI4_IRQn,1,2); //抢占优先级为1,子优先级为2
HAL_NVIC_EnableIRQ(EXTI4_IRQn); //使能中断线4
}
//中断服务函数,PA0用线0
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //调用中断处理公用函数
}
//PE4用线4
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); //调用中断处理公用函数
}
//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(100); //消抖
switch(GPIO_Pin)
{
case GPIO_PIN_0:
if(WK_UP==1)
{
LED1=!LED1;//控制LED1翻转
}
break;
case GPIO_PIN_4:
if(KEY0==1)
{
LED0=!LED0;//控制LED0翻转
}
break;
}
}