打印
[研电赛技术支持]

GD 32中断系统实现

[复制链接]
887|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-7-11 08:57 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
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

32656668f2d62a58b7.png (278.44 KB )

32656668f2d62a58b7.png

使用特权

评论回复
沙发
kzlzqi| | 2024-8-30 16:10 | 只看该作者
cpu暂停当前任务转而去执行更为紧急任务的操作

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1975

主题

15764

帖子

12

粉丝