1.0 中断的概念
中断:简单来说就是打断的意思,在计算机系统中CPU在执行一个操作的时候,有一个比当前任务更为紧急的任务需要执行,cpu暂停当前任务转而去执行更为紧急任务的操作,执行完更为紧急任务之后再返回来执行原来未执行完的任务:这里还涉及到任务切换的概念【以上是对于中断的理解】
2.0 中断的硬件结构
以上的结构大概是GD32单片机有7个端口,每一个端口大概有8个引脚,【同一时间只能有一个引脚接入到中断引脚选择】,中断引脚选择中可以选择触发中断的模式,有上升沿触发,下降沿触发选择完成后设置中断标志位,今个NVIC嵌入式中断向量控制器,然后进入对应的片上外设。
3.0 中断优先级
中断的优先级分为4组,每一组设置有不同的抢占式优先级和响应式优先级,可以参考以下的例子作为了解。
CPU 正在执行任务A,这个时候任务B来了 ,假设目前任务A的抢占式优先级是2 响应式优先级是3,任务B的抢占式优先级是1,响应式优先级是3 , 这是时候,GPU或展停任务A的执行转而去执行任务B的任务B任务执行完毕之后再去执行A任务。
第一种场景
抢占式优先级:如下图所示,任务a的抢占式优先级是3,响应式优先级是1,任务B的抢占式优先级是2,响应式优先级是2,然后任务B会打断抢占任务A的先执行
以下可以类比得出结果
4.0 代码实现
相关库函数参考
项目架构
中断配置步骤
初始化GPIO时钟
以上初始化GPIO的时钟使用定义结构体的方式进行初始化,使用结构体更方便后续程序的移植与使用,也可以选择不使用结构体初始化的方式。
中断EXTI初始化
使用结构体的方式初始化中断,在使用NVIC嵌套中断向量控制器使能中断的时候,首先要清除指定的中断线路,否则可能会出现程序频繁的进入中断与中断卡死的情况。
中断函数编写
以上主要使用到3个中断线,第一个是EXTI_0的中断,第二个是EXTI_13的中断,第三个是EXTI_14的中断。
KEY.C程序源码
#include "gd32f30x.h" // Device header
#include <stdint.h>
#include "LED.h"
// 创建结构体数组
typedef struct{
rcu_periph_enum rcu;
uint32_t gpio;
uint32_t mode;
uint32_t speed;
uint32_t pin;
}Exti_Gpio_t;
static Exti_Gpio_t Exti_List[] = {
{RCU_GPIOA,GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_10MHZ,GPIO_PIN_0},
{RCU_GPIOG,GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_10MHZ,GPIO_PIN_13},
{RCU_GPIOG,GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_10MHZ,GPIO_PIN_14},
};
#define Max_List_Exti sizeof(Exti_List)/sizeof(Exti_List[0])
static void GPIO_Init(void){
uint16_t i = 0;
for(i = 0; i < Max_List_Exti; i++){
rcu_periph_clock_enable(Exti_List.rcu);
gpio_init(Exti_List.gpio,Exti_List.mode,Exti_List.speed,Exti_List.pin);
}
}
static void EXTI_Init(void){
// 开启中断时钟
rcu_periph_clock_enable(RCU_AF);
// 开启中断引脚选择
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA,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,1);
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);
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_4);
nvic_irq_enable(EXTI10_15_IRQn, 0, 1);
}
void KeyDrvInit(void){
GPIO_Init();
EXTI_Init();
}
void EXTI0_IRQHandler(void){
// 获取中断标志位
if(exti_interrupt_flag_get(EXTI_0) != RESET){
Toggle_Led_Turn(LED1);
// 清除中断线路,防止该中断被反复的处理
exti_interrupt_flag_clear(EXTI_0);
while(1);
}
}
void EXTI10_15_IRQHandler(void){
if(exti_interrupt_flag_get(EXTI_13) != RESET){
Toggle_Led_Turn(LED2);
exti_interrupt_flag_clear(EXTI_13);
}
if(exti_interrupt_flag_get(EXTI_14) != RESET){
Toggle_Led_Turn(LED3);
exti_interrupt_flag_clear(EXTI_14);
}
}
KEY.H程序源码
#ifndef __KEY_H_
#define __KEY_H_
#include <stdint.h>
void KeyDrvInit(void);
#endif
LED.C 程序源码
#include "gd32f30x.h" // Device header
#include "Delay.h"
#include <stdint.h>
// 初始化结构体结构体数据类型
typedef struct Led_gpio_t{
rcu_periph_enum rcu;
uint32_t gpio;
uint32_t pin;
}LED_GPIO_T;
// 定义一个静态全局变量保存GPIO口的资源信息
static LED_GPIO_T Gpio_List[] = {
{RCU_GPIOA,GPIOA,GPIO_PIN_8},
{RCU_GPIOE,GPIOE,GPIO_PIN_6},
{RCU_GPIOF,GPIOF,GPIO_PIN_6}
};
// 宏定义确定数组的大小
#define LED_NUM_MAX (sizeof(Gpio_List) / sizeof(Gpio_List[0]))
void LED_Init_Drive(void){
for(uint8_t i = 0; i < LED_NUM_MAX; i++){
// 开启GPIO时钟
rcu_periph_clock_enable(Gpio_List.rcu);
// 初始化GPIO
gpio_init(
Gpio_List.gpio,
GPIO_MODE_OUT_PP,
GPIO_OSPEED_10MHZ,
Gpio_List.pin);
// GPIO 初始化调用的方式
gpio_bit_reset(Gpio_List.gpio,Gpio_List.pin);
}
}
void Turn_LedOn(uint8_t LedNo){
if(LedNo >= LED_NUM_MAX){
return;
}else{
gpio_bit_set(Gpio_List[LedNo].gpio,Gpio_List[LedNo].pin);
}
}
void Turn_OffLed(uint8_t LedOff){
if(LedOff >= LED_NUM_MAX){
return;
}else{
gpio_bit_reset(Gpio_List[LedOff].gpio,Gpio_List[LedOff].pin);
}
}
// 进入中断函数之后实现LED翻转功能
void Toggle_Led_Turn(uint8_t LedToggle){
// 设置中断标志位
FlagStatus bit_state;
// 获取输出数据寄存器的值
bit_state = gpio_input_bit_get(Gpio_List[LedToggle].gpio,Gpio_List[LedToggle].pin);
// 取反功能
bit_state = (FlagStatus)(1 - bit_state);
// 写入数据寄存器的值
gpio_bit_write(Gpio_List[LedToggle].gpio,Gpio_List[LedToggle].pin,bit_state);
}
/*
注:下面的这段代码可以忽略
*/
// 初始化LED灯
void LED_Init(void){
// 使能RCU时钟
rcu_periph_clock_enable(RCU_GPIOA);
// 配置引脚输出频率
gpio_init( GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_8);
// 初始化GPIOE的引脚
rcu_periph_clock_enable(RCU_GPIOE);
// 配置引脚输出频率
gpio_init( GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_6);
// 初始化GPIOE的引脚
rcu_periph_clock_enable(RCU_GPIOF);
// 配置引脚输出频率
gpio_init( GPIOF, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_6);
}
// 实现循环流水灯的功能
void LED_Cycle(void){
DelayInit();
while(1){
gpio_bit_set(GPIOA, GPIO_PIN_8);
DelayNms(1000);
gpio_bit_reset(GPIOA, GPIO_PIN_8);
DelayNms(1000);
gpio_bit_set(GPIOE, GPIO_PIN_6);
DelayNms(1000);
gpio_bit_reset(GPIOE, GPIO_PIN_6);
DelayNms(1000);
gpio_bit_set(GPIOF, GPIO_PIN_6);
DelayNms(1000);
gpio_bit_reset(GPIOF, GPIO_PIN_6);
DelayNms(1000);
}
}
LED.H程序源码
#ifndef _LED_H_
#define _LED_H_
#include <stdint.h>
//宏定义LED灯的引脚
#define LED1 0
#define LED2 1
#define LED3 2
void LED_Init(void);
void LED_Cycle(void);
void LED_Init_Drive(void);
void Turn_LedOn(uint8_t LedNo);
void Turn_OffLed(uint8_t LedOff);
void Toggle_Led_Turn(uint8_t LedToggle);
#endif
MAIN.C程序源码
#include <stdio.h>
#include "gd32f30x.h"
#include "Delay.h"
#include "LED.h"
#include "Key.h"
int main(void)
{
// 初始化LED
LED_Init();
KeyDrvInit();
while(1){
}
}
注:以上程序实现的效果是由KEY1,KEY2,KEY3,控制三个LED灯分别为LED1,LED2,LED3当第一个按键按下时LED1点亮,同时控制LED1的按键优先级低于控制KEY2,KEY3按键的优先级这个时候LED1灯会被抢占,也就是点亮LED1后可能无法再次被熄灭。【抢占式优先级】。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_45973003/article/details/140263321
|
|