中断的概念
中断硬件结构/软件结构
EXTI中断
EXTI硬件结构
注:EXTI线在同一时刻只能连接一个GPIO口,如果我们先连接了PA0,然后又连接了PB0那么此时PA0这个IO口就失去作用。
中断触发函数
中断优先级
中断优先级
数值越小优先级越高,抢占优先级可以实现中断嵌套的效果,不同的分组有不同的抢占优先级
EXTI外部中断配置
配置步骤
根据学习库函数原理配置步骤一般为如下所示(类比学习hal库函数原理)
1:配置AFIO,中断引脚寻找
2:配资EXTI,边沿检测以及控制
3:配置NVIC,嵌套中断向量控制器(用于统一分配和管理优先级)
注:按照步骤将下图打通即可
配置EXTI外部中断
static void KeyExtiInit(void)
{
/*使能EXTI时钟*/
rcu_periph_clock_enable(RCU_AF);
/*I/O连接到EXTI线*/
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_0);
// gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOC, GPIO_PIN_SOURCE_0);
//gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOC, GPIO_PIN_SOURCE_0);
/*配置上升/下降沿*/
exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/*清除标志*/
exti_interrupt_flag_clear(EXTI_0);
/*使能中断*/
nvic_irq_enable(EXTI0_IRQn, 1, 0);
/*
key2
*/
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOG, GPIO_PIN_SOURCE_13);
/*配置上升/下降沿*/
exti_init(EXTI_13, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/*清除标志*/
exti_interrupt_flag_clear(EXTI_13);
/*
KEY3
*/
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOG, GPIO_PIN_SOURCE_14);
/*配置上升/下降沿*/
exti_init(EXTI_14, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/*清除标志*/
exti_interrupt_flag_clear(EXTI_14);
// 使能中断
nvic_irq_enable(EXTI10_15_IRQn, 1, 0);
}
中断服务函数代码
// EXTI0中断服务函数,对应PA0口
void EXTI0_IRQHandler(void){
// 判断中断标志位,产生中断时标志位会设置为1
if( exti_interrupt_flag_get(EXTI_0) == SET){
// 此时说明进入中断
ToggleLed(LED1);
// 清除标志位,如果标志位没有清除为0,
//会不断地进入中断服务函数当中,所以要清除中断标志位
exti_interrupt_flag_clear(EXTI_0);
}
}
uint8_t flag = 0;
// EXTI_10_15中断服务函数,对应PA0口
void EXTI10_15_IRQHandler(void){
// 判断中断标志位,产生中断时标志位会设置为1
if( exti_interrupt_flag_get(EXTI_13) == SET){
// 此时说明进入中断
ToggleLed(LED2);
// 清除标志位,如果标志位没有清除为0,会不断地进入中断服务函数当中,
// 所以要清除中断标志位
exti_interrupt_flag_clear(EXTI_13);
}
// 判断中断标志位,产生中断时标志位会设置为1
if( exti_interrupt_flag_get(EXTI_14) == SET){
// 此时说明进入中断
ToggleLed(LED3);
// 清除标志位,如果标志位没有清除为0,会不断地进入中断服务函数当中,
// 所以要清除中断标志位
exti_interrupt_flag_clear(EXTI_14);
flag = 1;
}
}
整个程序的完整结构
KEY_DRV.c代码
#include <stdint.h>
#include "gd32f30x.h"
#include "KEY_DRV.h"
#include <stdint.h>
#include "LED.h"
static void KeyGpioInit(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_2MHZ, GPIO_PIN_0);
// 初始化剩下的两个按键
rcu_periph_clock_enable(RCU_GPIOG);
gpio_init(GPIOG, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_2MHZ, GPIO_PIN_13);
gpio_init(GPIOG, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_2MHZ, GPIO_PIN_14);
}
static void KeyExtiInit(void)
{
/*使能EXTI时钟*/
rcu_periph_clock_enable(RCU_AF);
/*I/O连接到EXTI线*/
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_0);
// gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOC, GPIO_PIN_SOURCE_0);
//gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOC, GPIO_PIN_SOURCE_0);
/*配置上升/下降沿*/
exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/*清除标志*/
exti_interrupt_flag_clear(EXTI_0);
/*使能中断*/
nvic_irq_enable(EXTI0_IRQn, 1, 0);
/*
key2
*/
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOG, GPIO_PIN_SOURCE_13);
/*配置上升/下降沿*/
exti_init(EXTI_13, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/*清除标志*/
exti_interrupt_flag_clear(EXTI_13);
/*
KEY3
*/
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOG, GPIO_PIN_SOURCE_14);
/*配置上升/下降沿*/
exti_init(EXTI_14, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/*清除标志*/
exti_interrupt_flag_clear(EXTI_14);
// 使能中断
nvic_irq_enable(EXTI10_15_IRQn, 1, 0);
}
/**
***********************************************************
* @brief 按键硬件初始化
* @param
* @return
***********************************************************
*/
void KeyDrvInit(void)
{
KeyGpioInit();
KeyExtiInit();
}
// EXTI0中断服务函数,对应PA0口
void EXTI0_IRQHandler(void){
// 判断中断标志位,产生中断时标志位会设置为1
if( exti_interrupt_flag_get(EXTI_0) == SET){
// 此时说明进入中断
ToggleLed(LED1);
// 清除标志位,如果标志位没有清除为0,
//会不断地进入中断服务函数当中,所以要清除中断标志位
exti_interrupt_flag_clear(EXTI_0);
}
}
uint8_t flag = 0;
// EXTI_10_15中断服务函数,对应PA0口
void EXTI10_15_IRQHandler(void){
// 判断中断标志位,产生中断时标志位会设置为1
if( exti_interrupt_flag_get(EXTI_13) == SET){
// 此时说明进入中断
ToggleLed(LED2);
// 清除标志位,如果标志位没有清除为0,会不断地进入中断服务函数当中,
// 所以要清除中断标志位
exti_interrupt_flag_clear(EXTI_13);
}
// 判断中断标志位,产生中断时标志位会设置为1
if( exti_interrupt_flag_get(EXTI_14) == SET){
// 此时说明进入中断
ToggleLed(LED3);
// 清除标志位,如果标志位没有清除为0,会不断地进入中断服务函数当中,
// 所以要清除中断标志位
exti_interrupt_flag_clear(EXTI_14);
flag = 1;
}
}
KEY_DRV.h代码
#ifndef _KEY_DRIVE_H_
#define _KEY_DRIVE_H_
#include <stdint.h>
void KeyDrvInit(void);
#endif
LED.C
#include "gd32f30x.h" // Device header
#include "Delay.h"
// 结构体初始化
typedef struct{
rcu_periph_enum rcu;
uint32_t gpio;
uint32_t pin;
}Led_GPIO_t;
// 保存GPIO口的资源信息
static Led_GPIO_t g_gpioList[] = {
{RCU_GPIOA,GPIOA,GPIO_PIN_8},
{RCU_GPIOE,GPIOE,GPIO_PIN_6},
{RCU_GPIOF,GPIOF,GPIO_PIN_6},
};
// 获取内存空间的大小
#define LED_NUM_MAX (sizeof(g_gpioList)/sizeof(g_gpioList[0]))
void LedDrv_Init(void){
uint8_t i = 0;
for(i = 0; i < LED_NUM_MAX;i++){
rcu_periph_clock_enable(g_gpioList[i].rcu);
gpio_init(
g_gpioList[i].gpio,
GPIO_MODE_OUT_PP,
GPIO_OSPEED_2MHZ,
g_gpioList[i].pin
);
// 熄灭LED灯
gpio_bit_reset(g_gpioList[i].gpio,g_gpioList[i].pin);
}
}
void TurnOnLed(uint8_t ledNO){
if(ledNO >= LED_NUM_MAX){
return;
}
gpio_bit_set(g_gpioList[ledNO].gpio,g_gpioList[ledNO].pin);
};
void TrunOffLed(uint8_t LedOff){
if(LedOff >= LED_NUM_MAX){
return;
}
gpio_bit_reset(g_gpioList[LedOff].gpio,g_gpioList[LedOff].pin);
};
// led状态取反
void ToggleLed(uint8_t ledNO){
FlagStatus bit_state;
bit_state = gpio_input_bit_get(g_gpioList[ledNO].gpio,g_gpioList[ledNO].pin);
// 当获取到的数据为0的时候 1 -0 等于 0 ,当获取到的数据为1的时候 1 - 1 = 0;
bit_state = (FlagStatus)(1 - bit_state);
gpio_bit_write(g_gpioList[ledNO].gpio,g_gpioList[ledNO].pin,bit_state);
};
LED.H
#ifndef _LED_H_
#define _LED_H_
#include <stdint.h>
#define LED1 0
#define LED2 1
#define LED3 2
void LedDrv_Init(void);
// 点亮LED ledNO 标号0-2
void TurnOnLed(uint8_t ledNO);
void TrunOffLed(uint8_t LedOff);
void ToggleLed(uint8_t ledNO);
#endif
main.c
#include <stdint.h>
#include "gd32f30x.h"
#include "Delay.h"
#include "LED.h"
#include "KEY_DRV.h"
extern uint8_t flag;
int main(void)
{
LedDrv_Init();
KeyDrvInit();
while(1){
if(flag == 1){
/*执行led是否被按下*/
flag = 0;
}
}
}
仿真调试
按键运算补充
抢占优先级的作用
注:高优先级的任务会抢占低优先级的任务,数值越小表示优先级别越高,当抢占式优先级相同时
如果有一个任务响应式优先级更高,但是实际上抢占还是不会发生,仍然是执行前面优先级部分。