上一节简单介绍并实现点亮LED灯,本小节在此基础上加入中断事件控制器(EXTI),Cortex®-M33集成了嵌套式矢量型中断控制器(Nested Vectored Interrupt Controller(NVIC))来实现高效的异常和中断处理。中断系统实现了低延迟的异常和中断处理,且包含外部中断、定时器中断、DMA中断和串口中断等。
本小节采用外部中断(外部信号发生高低电位变化触发中断),则选择GD32E502C-START 开发板上的按键KEY功能,当K2按键没按下时,GPIO引脚PA0被下拉电阻R29为低电平;反之,当K2按键按下后,GPIO引脚PA0为高电平。
因此,通过检测GPIO引脚PA0的电平来触发中断,中断信号在传送到中断处理器(NVIC),中断处理器(NVIC)在中断向量表找到对应的中断服务函数地址,传送到CPU内核,CPU就会把当前运行的程序暂停,跳转到中断服务函数,等中断服务函数运行完后,再跳回到之前暂停的函数,理解文字不易理解,下图逻辑差不多就是这个概念,以及简单函数框架。
比如:面前的你正在读我的文章,这时你的门被敲响(你点的外卖到了),你不得以被打断爱学习的你,马上起身开门拿外卖,并且吃了外卖后,再来继续读文章。
所以,简单理解上面外部中断,比如LED灯先一直亮着,突然你按下按键KEY2及GPIO引脚PA0(高电平)触发中断,中断服务函数里编写的功能是关闭LED灯,松手后GPIO引脚PA0又变为(低电平),LED灯再次点亮。
其次,中断事件还有优先级,在此就没具体介绍,每一个中断都有4位中断优先级配置位,可提供16个中断优先等级。其实简单理解就是多个中断/事件来临,根据这些中断/事件中优先级高低,依次运行函数,好比你吃外卖过程中女朋友电话打过来了,接女朋友电话事件比吃饭优先级高。
接下里,简单说下中断/事件控制器,中断/事件可以分为硬件中断和软件中断,硬件中断可以理解为外部或者内部信号的电位变化;软件中断可以理解为定时器计时、串口中断等,当达到了所设计的定时器时间来触发中断服务函数。
上图很清楚的表示了中断线和硬件引脚之间的关系,同一个EXTI线下的GPIO管脚不能同时触发,只能选择一个GPIO管脚作为该EXTI线的触发源,所有的0号引脚例如PA0/PB0/PC0等由0线代表,所有的1号引脚有1线表示以此类推。
接着上一节LED灯的功能扩展,当按键KEY按下时及4个LED灯点亮,按键KEY松开时及4个LED灯点熄灭。
1、开启时钟(GPIO时钟和系统配置时钟)
2、配置GPIO引脚模式及电平(LED灯、按键KEY)
3、配置中断EXTI(GPIO复用连接EXTI源、中断模式、边沿触发、清除对应中断标志位)
4、使能NVIC中断向量及优先级分组
由于使用的引脚PA0(按键KEY),故对应的EXTI0_IRQn中断。
#include "gd32e502.h"
#include "systick.h"
void EXTI0_IRQHandler(void); //中断函数定义声明
int key_count = 0; //定义按键计数变量
int main(void)
{
systick_config(); //初始化systick计数器
//********LED灯初始化**********//
rcu_periph_clock_enable(RCU_GPIOA); //打开GPIOA GPIOF GPIOC时钟
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOF);
//设置GPIO模式 输出模式 带上拉电阻 GPIO_PIN_x
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0 | GPIO_PIN_1);
//设置GPIO输出模式和速度 推完输出模式 最大输出速度50M GPIO_PIN_x
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_1);
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_0 |GPIO_PIN_1);
//电平置低 LED熄灭
gpio_bit_reset(GPIOA, GPIO_PIN_1);
gpio_bit_reset(GPIOF, GPIO_PIN_5);
gpio_bit_reset(GPIOC, GPIO_PIN_0 | GPIO_PIN_1);
//**************************//
//********按键KEY灯初始化**********//
//设置GPIO模式 输入模式 带上拉电阻 GPIO_PIN_x
gpio_mode_set(GPIOA,GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_0);
//设置AO口与中断线的映射关系
syscfg_exti_line_config(EXTI_SOURCE_GPIOA, EXTI_SOURCE_PIN0);
//设置中断向量0为下降沿触发 中断模式
exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_RISING);
//清除中断向量0对应的标志位
exti_interrupt_flag_clear(EXTI_0);
//**************************//
//********NVIC中断分组初始化**********//
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); //中断向量等级为组别2
nvic_irq_enable(EXTI0_IRQn, 2U, 2U); //使能中断向量0 响应优先级2 抢占优先级2
while(1)
{
}
}
void EXTI0_IRQHandler(void)
{
//判断是否进入中断 RESET:不进入 RESET;进入
if(RESET != exti_interrupt_flag_get(EXTI_0))
{
delay_1ms(10); //按键消抖
//判断按键KEY是否按下
if(SET == gpio_input_bit_get(GPIOA, GPIO_PIN_0))
{
key_count++; //按键计数加1
//按键长按进入while循环
while(SET == gpio_input_bit_get(GPIOA, GPIO_PIN_0));
}
//设置按键2种功能功能 点亮LED 关闭LED
if(key_count%2 == 1)
{
//点亮LED灯
gpio_bit_set(GPIOA, GPIO_PIN_1);
gpio_bit_set(GPIOF, GPIO_PIN_5);
gpio_bit_set(GPIOC, GPIO_PIN_0 | GPIO_PIN_1);
}
else
{
//关闭LED灯
gpio_bit_reset(GPIOA, GPIO_PIN_1);
gpio_bit_reset(GPIOF, GPIO_PIN_5);
gpio_bit_reset(GPIOC, GPIO_PIN_0 | GPIO_PIN_1);
}
//清除中断标志位 使下一次能够再次进入中断
exti_interrupt_flag_clear(EXTI_0);
}
}
间隔按下按键KEY2,LED灯点亮与熄灭切换,key_count按键计数累加,说明进入了EXTI0中断服务函数。
预告:下一小节来实现PWM发波功能