4.1 实验内容
通过本实验主要学习以下内容:
• EXTI中断原理;
• 按键中断检测原理;
4.2 实验原理
4.2.1 NVIC中断向量控制器
介绍EXTI之前,首先为各位读者介绍NVIC中断向量控制器,NVIC为M4内核组件,用于实现高效的异常和中断处理。NVIC可以支持抢占以及咬尾中断,具有多达68种外设中断以及4位中断优先等级配置(最多支持16个中断有限等级),当中断或异常产生时,系统自动将当前处理器工作状态压栈,在执行完中断服务子程序(ISR) 后自动将其出栈。
GD32F303系列MCU的中断向量表如下表所示(包含异常中断)。
有关NVIC中断相关配置函数可参考gd32f30x_misc.c,其中主要有以下几个函数,其功能简介如下表 所示。
4.2.2 EXTI中断原理
EXTI为外部中断/事件控制器,GD32F303系列MCU EXTI可以最多支持20个相互独立的边沿检测电路并且能够向处理器内核产生中断请求或唤醒事件。 EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发。 EXTI中的每一个边沿检测电路都可以独立配置和屏蔽。
EXTI框图如下图所示,极性控制用于控制边沿检测,可实现对外部EXTI信号线进行检测判断,当符合相关极性配置的EXTI信号出现后,将会发出EXTI请求,硬件EXTI请求与内部软件触发信号相或,然后输出给NVIC中断向量控制器产生中断以及输出至唤醒单元进行唤醒,也即是内部软件也可以触发相关请求。
EXTI相关触发源如下表所示,所有的GPIO均可以触发EXTI,另外LVD、RTC闹钟、USB唤醒以及以太网唤醒也可以触发EXTI中断或事件,EXTI可用于唤醒深度睡眠模式下的MCU。
• 注意:相同编号的引脚共用中断入口,比如PA0、PB0、PC0均使用EXTI0中断线,读者在使用时需注意不同的EXTI中断需要使用不同编号的GPIO引脚。
•软件触发EXTI中断请求可通过设置EXTI_SWIEV软件中断事件寄存器实现,如下图所示,设置相应控制位为1,即可实现软件触发EXTI中断请求。
4.3 硬件设计
本例程所使用的电路也为按键电路,具体可参考3.3章节描述。
4.4 代码解析
4.4.1 主函数代码解析
主函数代码如下所示,主要包括延迟初始化、LED初始化、key按键结构体初始化(此处将KEY0按键配置为中断模式,并将中断回调函数注册为ROCKER_KEY_IRQHandler)、串口初始化以及NVIC配置,KEY0使用的是PE2引脚,因而使能EXTI2_IRQn中断号,延迟1S后,打印Example of key interrupt detection,之后进入主循环,在主循环中查询ROCKER_KEY.press_timerms标志位,当ROCKER_KEY被按键触发中断后,该标志将会被设置为PRESS_DOWN,然后被主循环检测到后,将会打印ROCKER_KEY is pressed to trigger an interrupt。
C
int main(void)
{
driver_init();
bsp_led_group_init();
bsp_led_on(&LED0);
bsp_led_off(&LED1);
/* 配置按键为中断模式,并注册按键回调函数 */
ROCKER_KEY.key_gpio->gpio_mode = INT_LOW;
ROCKER_KEY.key_gpio->int_callback = ROCKER_KEY_IRQHandler;
bsp_key_init(&ROCKER_KEY);
nvic_irq_enable(EXTI2_IRQn,0,0);
bsp_uart_init(&BOARD_UART);
delay_ms(1000);
printf_log("Example of key interrupt detection.\r\n");
while (1)
{
if(ROCKER_KEY.press_timerms == PRESS_DOWN)
{
/* 检测到按键被按下 */
ROCKER_KEY.press_timerms = PRESS_NONE;
printf_log("ROCKER_KEY is pressed to trigger an interrupt.\r\n");
}
}
}
4.4.2 按键中断回调函数
按键中断回调函数如下所示,该函数在dvire_gpio_exti_handle中被调用,dvire_gpio_exti_handle在EXTI2_IRQHandler中被调用,其中EXTI2_IRQHandler为EXTI2的中断服务程序入口。
C
void ROCKER_KEY_IRQHandler(typdef_gpio_general *KEYx_IO)
{
if(SET==bsp_key_state_get(&ROCKER_KEY))
{
ROCKER_KEY.press_timerms=PRESS_DOWN;
bsp_led_toggle(&LED0);
bsp_led_toggle(&LED1);
}
}
void dvire_gpio_exti_handle(typdef_gpio_general *gpio)
{
bit_status int_input_bit=RESET;
if(exti_flag_get(gpio->extix)==SET)
{
exti_flag_clear(gpio->extix);
int_input_bit=dvire_gpio_pin_filter_get(gpio);
if( (gpio->gpio_mode==INT_LOW && int_input_bit==RESET) || (gpio->gpio_mode==INT_HIGH && int_input_bit==SET) )
{
if(gpio->int_callback!=NULL)
{
gpio->int_callback((typdef_gpio_general *)gpio);
}
}
}
}
void EXTI2_IRQHandler(void)
{
dvire_gpio_exti_handle(ROCKER_KEY.key_gpio);
}
4.5 实验结果
将本例程烧录到红枫派开发板中,通过Type C数据线连接USB串口和PC,打开串口调试助手,上电复位后,首先将会打印Example of key interrupt detection.,之后按下ROCKER_KEY按键后,将会打印:ROCKER_KEYis pressed to trigger an interrupt.。
红枫派开发板使用手册:GD32F303红枫派使用手册 - 飞书云文档 (feishu.cn)