EXTI
EXTI,全称External Interrupt/Event Controller,即外部中断/事件控制器,是微控制器中的一个重要组成部分。它主要用于管理来自外部设备的中断和事件请求。以下是关于EXTI的详细介绍:
- 功能概述:
- EXTI管理了控制器的多个中断/事件线,通常这些线路数量在STM32等微控制器中可以达到20个或更多。
- 每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿检测。这意味着当外部信号的状态从低电平变为高电平(上升沿)或从高电平变为低电平(下降沿)时,EXTI可以检测到这种变化并产生相应的中断或事件。
- EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性(如上升沿触发、下降沿触发或双边沿触发)。
- 两大功能:
- 产生中断:当外部设备触发中断条件时,它会向EXTI发送中断请求。EXTI会接收并处理这些中断请求,并将信号传递给NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)进行处理。NVIC会根据中断的优先级来决定是否响应中断,并调用相应的中断服务程序。
- 产生事件:除了产生中断外,EXTI还可以产生事件。这些事件可以用于触发某些特定的操作或行为,而无需进入中断服务程序。例如,可以使用EXTI来检测按钮的按下事件,并在检测到该事件时执行某个特定的任务或操作。
- 典型应用:
- 在物联网应用中,NVIC和EXTI是常用的中断处理器和中断控制器。外部设备可以是各种传感器、开关、按钮等。当这些设备触发中断条件时,它们会向EXTI发送中断请求,EXTI会接收并处理这些请求,并触发相应的中断服务程序或事件处理程序。
- 在嵌入式系统中,EXTI也常用于处理各种外设的中断请求,例如传感器数据的到达、网络通信的中断等。
- 结构特点:
- EXTI的结构通常包括多个中断/事件线、边沿检测器、配置寄存器等部分。每个中断/事件线都可以单独进行配置,以选择请求类型和相应的触发事件。此外,EXTI还提供了丰富的库函数和API,方便开发人员进行编程和配置。
总的来说,EXTI是微控制器中一个重要的外部中断/事件控制器,它可以实现对外部设备的中断和事件请求的管理和处理。在物联网和嵌入式系统等领域中,EXTI具有广泛的应用前景和重要的价值。
以上介绍来自文心一言。
GD32中的EXTI
很常规的EXTI,与STM32不一样的是,GD32这边的优先级是只有2位配置中断优先级的。
再看看中断的流程。
那根据上面的框图,我们要实现外部中断,要配置的东西就是边沿检测,极性控制,中断屏蔽控制(NVIC)。
然后和STM32一样的是,我们是GPIO几,那么中断线就是几号。并且同一时间同一个引脚号只能使用一种端口(A,B,C)
固件库函数
首先我们除了需要打开拿来外部中断的GPIO的外设时钟之外。由于我们还需要将GPIO口映射到中断线上,因此还需要配置系统时钟。
rcu_periph_clock_enable(RCU_CFGCMP);
exti_init
初始化EXTI。
如果我们用的是GPIO的0号引脚,那么选择0号中断线(EXTI_0),一号引脚就选择1号中断线,以此类推。
模式一般选择中断模式EXTI_INTERRUPT。
exti_interrupt_enable
中断使能,输入的参数指定中断线。
exti_interrupt_flag_get
获取指定中断线的标志位,当我们有多条中断线共用一个中断处理函数的时候可以帮助我们判断具体是哪一个中断线触发的中断。
exti_interrupt_flag_clear
清除指定中断线的中断标志位。
关于EXTI的函数就上面几个就够了。但是根据框图,我们还需要将GPIO映射到中断线上,以及配置NVIC的优先级。
syscfg_exti_line_config
根据上面表格的参数来填写,将我们使用的端口以及对应的中断线选上。因为中断线的序号和GPIO的引脚号是一致的,所以只需要说是EXTI的哪个引脚(中断线)即可。
nvic_irq_enable
使能NVIC中断并且配置优先级。
可以自行去官方提供的固件库使用指南去寻找可以使用的枚举类型。
比如说我使用GPIO的0号引脚,那么我就使用红框框中的参数。
一切准备就绪,我们就可以开始中断了,最后一个问题就是中断处理函数,和STM32一样,我们去启动文件中的汇编文件里寻找。
示例代码
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "OLED.h"
int count=0;
void EXTI0_1_IRQHandler(void){
if(exti_interrupt_flag_get(EXTI_0) == SET){ //获取中断线0的标志位
if(!gpio_input_bit_get(GPIOA,GPIO_PIN_0)){ //消抖处理
delay_ms(15);
gpio_bit_toggle(GPIOA, GPIO_PIN_5);
count++;
}
exti_interrupt_flag_clear(EXTI_0); //清除标志位
}
}
int main(void){
systick_config(); //延时函数初始化
OLED_Init();
rcu_periph_clock_enable(RCU_GPIOA); //打开GPIOA的外设时钟
rcu_periph_clock_enable(RCU_CFGCMP); //配置系统时钟
gpio_mode_set(GPIOA,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO_PIN_5); //GPIOA5输出模式
gpio_output_options_set(GPIOA,GPIO_OTYPE_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_5);
gpio_mode_set(GPIOA,GPIO_MODE_INPUT,GPIO_PUPD_PULLDOWN,GPIO_PIN_0); //GPIOA0下拉输入模式
nvic_irq_enable(EXTI0_1_IRQn, 1); //NVIC分配优先级
syscfg_exti_line_config(EXTI_SOURCE_GPIOA,EXTI_SOURCE_PIN0); //将GPIO口映射到中断线上
exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_FALLING); //EXTI初始化
exti_interrupt_enable(EXTI_0); //中断使能
while(1){
OLED_ShowNum(1,1,count,2);
delay_ms(1000);
}
}