中断源的级别Level 的数字值采取左对齐的方式进行解读,Level 的数字值越大,则表示其级别越高,高级别的中断可以打断低级别的中断处理,从而形成中断嵌套。
中断源的优先级Priority 的数字值也采取左对齐的方式进行解读,Priority 的数字值越大,则表示其优先级越高,中断优先级(Priority)不参与中断嵌套的判断,即中断能否嵌套与中断优先级(Priority)的数值大小没有关系,而是与中断级别(Level)的数值大小有关。多个中断同时 Pending 时,ECLIC 需要仲裁决定哪个中断被发送给内核进行处理,仲裁时需要参考每个中断源的 Priority 数字值。
Ø ECLIC的优先级组
在配置优先级的时候,还要注意一个很重要的问题,即中断种类的数量。ECLIC只可以配置 16 种中断向量的优先级,也就是说,中断源的级别Level和优先级Priority的数量由一个 4 位的数字来决定,把这个4位数字的位数分配成级别部分和优先级部分。有 5 组分配方式 :
l 第 0 组: 所有 4 位用来配置中断源优先级Priority。即 16 种中断向量具有都不相同的优先级Priority。
l 第 1 组:最高 1 位用来配置级别Level,低 3 位用来配置优先级Priority。表示有 21=2 种级别的级别Level (0 级,1 级),有 23=8 种优先级Priority,即在 16 种中断向量优先级Priority,
l 第 2 组:2 位用来配置中断源级别Level,2 位用来配置优先级Priority,即 22=4 种中断源级别Level,22=4 种优先级Priority。
l 第 3 组:高 3 位用来配置中断源级别Level,最低 1 位用来配置优先级Priority。即有 8 种中断源级别Level,2 种优先级Priority。
l 第 4 组:所有 4 位用来配置中断源级别Level,即 ECLIC配置的 24=16 种中断向量都是只有级别属性,没有优先级Priority属性。
要配置这些优先级组,可以采用库函数 eclic_set_nlbits(),可输入的参数为ECLIC_GROUP_LEVEL0_PRIO4~ ECLIC_GROUP_LEVEL4_PRIO0,分别为以上介绍的 5 种优先级组。
Ø EXTI 外部中断
EXTI (中断/事件控制器)包括19个相互独立的边沿检测电路并且能够向处理器内核产生中断请求或唤醒事件。 EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发。EXTI中的每一个边沿检测电路都可以独立配置和屏蔽。
GD32的所有 GPIO 都引入到 EXTI 外部中断线上,使得所有的 GPIO 都能作为外部中断的输入源。GPIO 与 EXTI 的连接方式见下表。
由下表可知,PA0 ~ PE0 连接到 EXTI0 、PA1 ~ PE1 连接到 EXTI1、……、PA15 ~ PG15 连接到 EXTI15。这里大家要注意的是 :PAx ~ PEx 端口的中断事件都连接到了 EXTIx,即同一时刻 EXTIx 只能响应一个端口的事件触发,不能够同一时间响应所有GPIO 端口的事件,但可以分时复用。它可以配置为上升沿触发、下降沿触发或双边沿触发。EXTI 最普通的应用就是接上一个按键,设置为下降沿触发,用中断来检测按键。
表2外部中断通用I/O映像
2 EXTI方式实现主函数代码如下:
/*Includes*********************************************************************/
#include "gd32vf103.h"
#include"gd32vf103r_led_start.h"
#include"gd32vf103r_systick_start.h"
#include"gd32vf103r_key_start.h"
int main(void)
{
/* configure LED1 GPIO port */
led_init(LED1);
/* configure LED2 GPIO port */
led_init(LED2);
/* configure LED3 GPIO port */
led_init(LED3);
/* configure LED4 GPIO port */
led_init(LED4);
// key init
key_init(KEY_USER, KEY_MODE_EXTI);
while(1)
{
delay_ms(100);
}
}
Ø 配置外部中断
现在我们重点分析 key_init()这个函数,它完成了配置一个 I/O 为 EXTI 中断的一般步骤,主要有以下功能 :
1)使能 EXTIx 线的时钟和第二功能 AFIO 时钟。
2)配置 EXTIx 线的中断优先级。
3)配置 EXTI 中断线 I/O。
4)选定要配置为 EXTI 的 I/O 口线和 I/O 口的工作模式。
5)EXTI 中断线工作模式配置。
6)开启全局中断以及优先级组 。
/*
brief configure key
param[in] key_num: specify the key to be configured
arg KEY_USER: user key
param[in] key_mode: specify button mode
arg KEY_MODE_GPIO: key will be used as simple IO
arg KEY_MODE_EXTI: key will be connected to EXTI line with interrupt
param[out] none
retval none
*/
void key_init(key_typedef_enumkey_num, keymode_typedef_enum key_mode)
{
/* enable the key clock */
rcu_periph_clock_enable(KEY_CLK[key_num]);
rcu_periph_clock_enable(RCU_AF);
/* configure button pin as input */
gpio_init(KEY_PORT[key_num],GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, KEY_PIN[key_num]);
if(key_mode == KEY_MODE_EXTI)
{
/* enable and set key EXTI interrupt tothe lowest priority */
eclic_global_interrupt_enable();
eclic_set_nlbits(ECLIC_GROUP_LEVEL3_PRIO1);
eclic_irq_enable(KEY_IRQn[key_num], 1U,1U);
/* connect key EXTI line to key GPIOpin */
gpio_exti_source_select(KEY_PORT_SOURCE[key_num],KEY_PIN_SOURCE[key_num]);
/* configure key EXTI line */
exti_init(KEY_EXTI_LINE[key_num],EXTI_INTERRUPT, EXTI_TRIG_FALLING);
exti_interrupt_flag_clear(KEY_EXTI_LINE[key_num]);
}
}
key_init()代码中,不仅配置了ECLIC ,还对按键的GPIO进行了初始化,这部分和按键轮询的设置类似。
接下来,调用 gpio_exti_source_select() 函数把 GPIOA、Pin0 与EXTI连接起来。
最后调用 exti_init()把 EXTI 初始化,函数如下:
/*!
brief initialize the EXTI
param[in] linex: EXTI line number, refer to exti_line_enum
only one parameter can beselected which is shown as below:
arg EXTI_x (x=0..18): EXTI line x
param[in] mode: interrupt or event mode, refer to exti_mode_enum
only one parameter can beselected which is shown as below:
arg EXTI_INTERRUPT: interrupt mode
arg EXTI_EVENT: event mode
param[in] trig_type: trigger type, refer to exti_trig_type_enum
only one parameter can beselected which is shown as below:
arg EXTI_TRIG_RISING: rising edge trigger
arg EXTI_TRIG_FALLING: fallingedge trigger
arg EXTI_TRIG_BOTH: rising edge and fallingedge trigger
arg EXTI_TRIG_NONE: without rising edge orfalling edge trigger
param[out] none
retval none
*/
void exti_init(exti_line_enumlinex, exti_mode_enum mode, exti_trig_type_enum trig_type)
{
/* reset the EXTI line x */
EXTI_INTEN &= ~(uint32_t) linex;
EXTI_EVEN &= ~(uint32_t) linex;
EXTI_RTEN &= ~(uint32_t) linex;
EXTI_FTEN &= ~(uint32_t) linex;
/*set the EXTI mode and enable the interrupts or events from EXTI line x */
switch (mode) {
case EXTI_INTERRUPT:
EXTI_INTEN |= (uint32_t) linex;
break;
case EXTI_EVENT:
EXTI_EVEN |= (uint32_t) linex;
break;
default:
break;
}
/* set the EXTI trigger type */
switch (trig_type) {
case EXTI_TRIG_RISING:
EXTI_RTEN |= (uint32_t) linex;
EXTI_FTEN &= ~(uint32_t) linex;
break;
case EXTI_TRIG_FALLING:
EXTI_RTEN &= ~(uint32_t) linex;
EXTI_FTEN |= (uint32_t) linex;
break;
case EXTI_TRIG_BOTH:
EXTI_RTEN |= (uint32_t) linex;
EXTI_FTEN |= (uint32_t) linex;
break;
case EXTI_TRIG_NONE:
default:
break;
}
}
Ø AFIO 时钟
代码中调用rcu_periph_clock_enable(RCU_AF)表示开启 AFIO的时钟。
AFIO (alternate-functionI/O),指 GPIO 端口的复用功能,GPIO 除了用作普通的输入输出(主功能),还可以作为片上外设的复用输入输出,如串口、ADC,这些就是复用功能。大多数 GPIO 都有一个默认复用功能,有的 GPIO 还有重映射功能。重映射功能是指把原来属于 A 引脚的默认复用功能,转移到B引脚进行使用,前提是 B 引脚具有这个重映射功能。
当把 GPIO 用作 EXTI 外部中断或使用重映射功能的时候,必须开启 AFIO 时钟,而在使用默认复用功能的时候,就不必开启 AFIO 时钟了。
Ø 编写中断服务函数
在这个 EXTI 设置中我们把 PA0 连接到内部的 EXTI0,GPIO 配置为上拉输入,工作在下降沿中断。在外围电路上我们将 PA0 接到了 key上。当按键没有按下时,PA0 始终为高,当按键按下时 PA0 变为低,从而 PA0 上产生一个下降沿跳变,EXTI0 会捕捉到这一跳变,并产生相应的中断,中断服务程序在 gd32vf103_it.c中实现。gd32vf103_it.c 文件是专门用来存放中断服务函数的。文件中默认只有几个关于系统异常的中断服务函数,而且都是空函数,在需要的时候自行编写。那么中断服务函数名是不是可以自己定义呢?不可以。中断服务函数的名字必须要与启动文件start.S 中的中断向量表定义一致。
EXTI0_IRQHandler 表示为 EXTI0 中断向量的服务函数名。于是,我们就可以在
STM32f10x_it.c 文件中加入名为 EXTI0_IRQHandler()的函数。
/*!
brief this function handles external lines 0 interrupt request
param[in] none
param[out] none
retval none
*/
void EXTI0_IRQHandler(void)
{
if(RESET !=exti_interrupt_flag_get(EXTI_0))
{
/* turntoggle LED */
led_toggle(LED1);
led_toggle(LED2);
led_toggle(LED3);
led_toggle(LED4);
exti_interrupt_flag_clear(EXTI_0);
}
}
其内容比较容易理解,进入中断后,调用exti_interrupt_flag_get()库函数来重新检查是否产生了 EXTI_Line 中断,接下来把 LED 取反,操作完毕后,调用 exti_interrupt_flag_clear()清除中断标志位再退出中断服务函数。
3实验现象编译好程序后,下载到板子上,当按在按键时,LED或亮或灭。