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

#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

void KeyDrvInit(void);

#endif

LED.C 程序源码

#include "gd32f30x.h" // Device header

#include "Delay.h"

#include

// 初始化结构体结构体数据类型

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);

// 初始化GPIO

gpio_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

//宏定义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

#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后可能无法再次被熄灭。【抢占式优先级】。

下面的内容是关于STM32的可以不看【------用于类比学习---------】

5.0 STM32外部中断

初始化外部中断的步骤

1: RCC 开启外部中断的时钟【中断引脚选择时钟AFIO选择哪一组引脚进入中断】

2: RCC 开启GPIO时钟

3:EXTI 中断引脚选择与中断的触发方式

4:NVIC 嵌套中断向量控制器【配置中断优先级分组和中断的优先级同时使能中断】

5:编写中断函数执行中断操作

中断按照这个步骤一一打通即可

外部中断实现旋转编码器计算次数的功能

CountSensor.c 代码

#include "stm32f10x.h" // Device header

#include

uint16_t g_CountSensor_Count;

/*

*****************************************

* @brief : 初始化旋转编码器计算次数,使能rcc时钟

* @param : 无参数

* @param : 无参数

* @retval: 无返回值

*****************************************

*/

void CountSensor_Init(void)

{

// 时钟初始化

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

// GPIO初始化

GPIO_InitTypeDef GPIO_InitStructre;

GPIO_InitStructre.GPIO_Mode = GPIO_Mode_IPU;

GPIO_InitStructre.GPIO_Speed = GPIO_Pin_14;

GPIO_InitStructre.GPIO_Pin = GPIO_Speed_50MHz;

GPIO_Init(GPIOB,&GPIO_InitStructre);

// 中断引脚选择与配置方式

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);

EXTI_InitTypeDef EXTI_InitStructure;

EXTI_InitStructure.EXTI_Line = EXTI_Line14;

EXTI_InitStructure.EXTI_LineCmd = ENABLE;

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

EXTI_Init(&EXTI_InitStructure);

// 使能中断

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;

NVIC_Init(&NVIC_InitStructure);

}

uint16_t CountSensor_Cet(void)

{

return g_CountSensor_Count;

}

void EXTI15_10_IRQHandler(void)

{

if(EXTI_GetITStatus(EXTI_Line14) == SET)

{

if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14) == 0)

{

g_CountSensor_Count ++;

}

// 进一次中断清除一次中断标志位

EXTI_ClearITPendingBit(EXTI_Line14);

}

}

CountSensor.h

#ifndef __COUNTSENSOR_H_

#define __COUNTSENSOR_H_

#include

void CountSensor_Init(void);

uint16_t CountSensor_Cet(void);

#endif

MAIN.C

#include "stm32f10x.h"

#include

#include "Delay.h"

#include "Buzzer.h"

#include "LightSensor.h"

#include "CountSensor.h"

#include "OLED.h"

int main(void)

{

OLED_Init();

CountSensor_Init();

/*显示静态字符串*/

OLED_ShowString(1, 1, "Count:"); //1行1列显示字符串Count:

while (1)

{

OLED_ShowNum(1, 7, CountSensor_Cet(), 5);

}

}

OLED.H文件

#include "stm32f10x.h"

#include "OLED_Font.h"

/*引脚配置*/

#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))

#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))

/*引脚初始化*/

void OLED_I2C_Init(void)

{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

GPIO_Init(GPIOB, &GPIO_InitStructure);

OLED_W_SCL(1);

OLED_W_SDA(1);

}

/**

* @brief I2C开始

* @param 无

* @retval 无

*/

void OLED_I2C_Start(void)

{

OLED_W_SDA(1);

OLED_W_SCL(1);

OLED_W_SDA(0);

OLED_W_SCL(0);

}

/**

* @brief I2C停止

* @param 无

* @retval 无

*/

void OLED_I2C_Stop(void)

{

OLED_W_SDA(0);

OLED_W_SCL(1);

OLED_W_SDA(1);

}

/**

* @brief I2C发送一个字节

* @param Byte 要发送的一个字节

* @retval 无

*/

void OLED_I2C_SendByte(uint8_t Byte)

{

uint8_t i;

for (i = 0; i < 8; i++)

{

OLED_W_SDA(Byte & (0x80 >> i));

OLED_W_SCL(1);

OLED_W_SCL(0);

}

OLED_W_SCL(1); //额外的一个时钟,不处理应答信号

OLED_W_SCL(0);

}

/**

* @brief OLED写命令

* @param Command 要写入的命令

* @retval 无

*/

void OLED_WriteCommand(uint8_t Command)

{

OLED_I2C_Start();

OLED_I2C_SendByte(0x78); //从机地址

OLED_I2C_SendByte(0x00); //写命令

OLED_I2C_SendByte(Command);

OLED_I2C_Stop();

}

/**

* @brief OLED写数据

* @param Data 要写入的数据

* @retval 无

*/

void OLED_WriteData(uint8_t Data)

{

OLED_I2C_Start();

OLED_I2C_SendByte(0x78); //从机地址

OLED_I2C_SendByte(0x40); //写数据

OLED_I2C_SendByte(Data);

OLED_I2C_Stop();

}

/**

* @brief OLED设置光标位置

* @param Y 以左上角为原点,向下方向的坐标,范围:0~7

* @param X 以左上角为原点,向右方向的坐标,范围:0~127

* @retval 无

*/

void OLED_SetCursor(uint8_t Y, uint8_t X)

{

OLED_WriteCommand(0xB0 | Y); //设置Y位置

OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位

OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位

}

/**

* @brief OLED清屏

* @param 无

* @retval 无

*/

void OLED_Clear(void)

{

uint8_t i, j;

for (j = 0; j < 8; j++)

{

OLED_SetCursor(j, 0);

for(i = 0; i < 128; i++)

{

OLED_WriteData(0x00);

}

}

}

/**

* @brief OLED显示一个字符

* @param Line 行位置,范围:1~4

* @param Column 列位置,范围:1~16

* @param Char 要显示的一个字符,范围:ASCII可见字符

* @retval 无

*/

void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)

{

uint8_t i;

OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分

for (i = 0; i < 8; i++)

{

OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容

}

OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分

for (i = 0; i < 8; i++)

{

OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容

}

}

/**

* @brief OLED显示字符串

* @param Line 起始行位置,范围:1~4

* @param Column 起始列位置,范围:1~16

* @param String 要显示的字符串,范围:ASCII可见字符

* @retval 无

*/

void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)

{

uint8_t i;

for (i = 0; String[i] != '\0'; i++)

{

OLED_ShowChar(Line, Column + i, String[i]);

}

}

/**

* @brief OLED次方函数

* @retval 返回值等于X的Y次方

*/

uint32_t OLED_Pow(uint32_t X, uint32_t Y)

{

uint32_t Result = 1;

while (Y--)

{

Result *= X;

}

return Result;

}

/**

* @brief OLED显示数字(十进制,正数)

* @param Line 起始行位置,范围:1~4

* @param Column 起始列位置,范围:1~16

* @param Number 要显示的数字,范围:0~4294967295

* @param Length 要显示数字的长度,范围:1~10

* @retval 无

*/

void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)

{

uint8_t i;

for (i = 0; i < Length; i++)

{

OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');

}

}

/**

* @brief OLED显示数字(十进制,带符号数)

* @param Line 起始行位置,范围:1~4

* @param Column 起始列位置,范围:1~16

* @param Number 要显示的数字,范围:-2147483648~2147483647

* @param Length 要显示数字的长度,范围:1~10

* @retval 无

*/

void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)

{

uint8_t i;

uint32_t Number1;

if (Number >= 0)

{

OLED_ShowChar(Line, Column, '+');

Number1 = Number;

}

else

{

OLED_ShowChar(Line, Column, '-');

Number1 = -Number;

}

for (i = 0; i < Length; i++)

{

OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');

}

}

/**

* @brief OLED显示数字(十六进制,正数)

* @param Line 起始行位置,范围:1~4

* @param Column 起始列位置,范围:1~16

* @param Number 要显示的数字,范围:0~0xFFFFFFFF

* @param Length 要显示数字的长度,范围:1~8

* @retval 无

*/

void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)

{

uint8_t i, SingleNumber;

for (i = 0; i < Length; i++)

{

SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;

if (SingleNumber < 10)

{

OLED_ShowChar(Line, Column + i, SingleNumber + '0');

}

else

{

OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');

}

}

}

/**

* @brief OLED显示数字(二进制,正数)

* @param Line 起始行位置,范围:1~4

* @param Column 起始列位置,范围:1~16

* @param Number 要显示的数字,范围:0~1111 1111 1111 1111

* @param Length 要显示数字的长度,范围:1~16

* @retval 无

*/

void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)

{

uint8_t i;

for (i = 0; i < Length; i++)

{

OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');

}

}

/**

* @brief OLED初始化

* @param 无

* @retval 无

*/

void OLED_Init(void)

{

uint32_t i, j;

for (i = 0; i < 1000; i++) //上电延时

{

for (j = 0; j < 1000; j++);

}

OLED_I2C_Init(); //端口初始化

OLED_WriteCommand(0xAE); //关闭显示

OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率

OLED_WriteCommand(0x80);

OLED_WriteCommand(0xA8); //设置多路复用率

OLED_WriteCommand(0x3F);

OLED_WriteCommand(0xD3); //设置显示偏移

OLED_WriteCommand(0x00);

OLED_WriteCommand(0x40); //设置显示开始行

OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置

OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置

OLED_WriteCommand(0xDA); //设置COM引脚硬件配置

OLED_WriteCommand(0x12);

OLED_WriteCommand(0x81); //设置对比度控制

OLED_WriteCommand(0xCF);

OLED_WriteCommand(0xD9); //设置预充电周期

OLED_WriteCommand(0xF1);

OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别

OLED_WriteCommand(0x30);

OLED_WriteCommand(0xA4); //设置整个显示打开/关闭

OLED_WriteCommand(0xA6); //设置正常/倒转显示

OLED_WriteCommand(0x8D); //设置充电泵

OLED_WriteCommand(0x14);

OLED_WriteCommand(0xAF); //开启显示

OLED_Clear(); //OLED清屏

}

OLED.H

#ifndef __OLED_H

#define __OLED_H

#include

void OLED_Init(void);

void OLED_Clear(void);

void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);

void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);

void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);

void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);

#endif

Copyright © 2088 2017乒乓球世界杯_世界杯体彩 - uzhiqu.com All Rights Reserved.
友情链接