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[i].rcu);gpio_init(Exti_List[i].gpio,Exti_List[i].mode,Exti_List[i].speed,Exti_List[i].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[i].rcu);// 初始化GPIOgpio_init(Gpio_List[i].gpio,GPIO_MODE_OUT_PP,GPIO_OSPEED_10MHZ,Gpio_List[i].pin);// GPIO 初始化調用的方式gpio_bit_reset(Gpio_List[i].gpio,Gpio_List[i].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 2void 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)
{ // 初始化LEDLED_Init();KeyDrvInit();while(1){} }
注:以上程序實現的效果是由KEY1,KEY2,KEY3,控制三個LED燈分別為LED1,LED2,LED3當第一個按鍵按下時LED1點亮,同時控制LED1的按鍵優先級低于控制KEY2,KEY3按鍵的優先級這個時候LED1燈會被搶占,也就是點亮LED1后可能無法再次被熄滅。【搶占式優先級】。
5.0 STM32類比學習
.....