中断
目录
一、中断的概念
1.1.中断的基本概念
单片机应对突发事件的一种
生活中的突发事件:

单片机的突发事件:

常规程序:

中断相应函数:

1.2.中断编程举例
串口发送数据控制LED闪烁速率
闪灯程序:

串口数据接收程序:

如果直接合并程序:

假设亮灯需要100ms,灭灯需要100ms,一次完整的闪灯周期为200ms
但只有在每次执行判断串口RXNE标志位时才会检查串口是否有数据
如果数据在闪灯延迟期间送达,就会被延迟到下一次轮询时才能处理
串口波特率为115200bps,数据帧格式为1位停止位+8位数据位+1位校验位
115200 / 10 = 11520,串口每秒中最多可以接收11520个字节
如果使用中断:

当串口接收到数据时,会触发中断
CPU立即停止主循环的闪灯程序
转而去执行中断服务函数来读取并且处理数据
二、中断优先级
2.1.中断优先级的概念


在第一个事例中:
正在给病人1看病,病人2比病人3先到,但病人3需要赶火车,所以先看病人3
在第二个事例中:
正在给病人4看病,但病人5病情较急,所以先给病人5看病
**优先级:**优先处理重要紧急的事情,用数字表示中断的紧急程度
2.2.中断优先级的表示方法
2.2.1中断结构框图

**片上外设:**芯片内部模块,每一种模块都有对应的功能
**中断:**芯片上的片上外设可以产生中断
例:USART模块的全局中断(由标志位控制)

**嵌套中断向量控制器(NVIC):**中断的管理员,根据中断的优先级进行排队、嵌套
**中断时序图:**反映中断的时序流程
**中断向量表:**CPU响应中断时,从中断向量表中寻找中断响应函数
2.2.2.嵌套中断向量控制器(NVIC)
- 配置中断优先级分组
- 设置抢占优先级和子优先级
- 控制中断开关


2.3.抢占优先级与中断嵌套
**中断嵌套:**更高优先级的中断打断正在执行的中断

**中断嵌套的条件:**新中断的抢占优先级更高
示例:
假设中断优先级分组为2

|-----|------|-------|-------|
| 序号 | | 抢占优先级 | 子优先级 |
| 中断1 | 1001 | 10(2) | 01(1) |
| 中断2 | 1000 | 10(2) | 00(0) |
| 中断3 | 0011 | 00(0) | 11(3) |
| 中断4 | 1110 | 11(3) | 10(2) |
**注:**抢占优先级越小,优先级越高
判断:

中断1的抢占优先级与中断2的抢占优先级都为2,不会发生中断嵌套

中断3的抢占优先级比中断1的抢占优先级小,所以比中断1的优先级高,会发生中断嵌套

中断1的抢占优先级比中断4的抢占优先级小,所以中断1的优先级高,不会发生中断嵌套
2.4.子占优先级与中断排队
**中断排队:**优先级相仿,等待前一个中断执行完再处理新中断

- 因为抢占优先级相仿,不会打断当前中断的执行
- 子占优先级高的排在前面
- 子占优先级相同,则根据先来后到排队
2.5.练习
中断优先级分组2

三、串口中断编程实验
3.1.编写闪灯代码
cpp
#include "stm32f10x.h"
#include "delay.h"
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//创建闪灯间隔变量
uint32_t blinkInterval = 1000;
int main(void)
{
App_OnBoardLED_Init();
while(1)
{
/*点亮板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
/*延迟*/
Delay(blinkInterval);
/*熄灭板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
/*延迟*/
Delay(blinkInterval);
}
}
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA0引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
3.2.初始化串口

cpp
//创建USART1初始化函数
void App_USART1_Init(void)
{
/*GPIO结构前置声明*/
GPIO_InitTypeDef GPIO_InitStruct;
//配置发送端Tx对应的PA9引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置接收端Rx对应的PA10引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA10引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);
//初始化串口
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构前置声明*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*硬件流控设为无*/
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
}
3.3.让RxNE标志位触发中断
每当串口接收到数据时,就要产生一个中断
可以通过串口的RxNE标志位判断
每当RxNE从0变为1时,说明串口收到数据

逻辑或门:
只要有一个数据为1,就输出1
只有当所有数据为0,才输出0
要想使标志位触发中断
需要闭合中断屏蔽开关
cpp
void USART_ITConfig(USART_TypeDef* USARTx,uint16_t USART_IT,FunctionalState NewState);
解析:
**参数1:**串口的名称 USART1 USART2
**参数2:**标志位的名称
- USART_IT_TXE
- USART_IT_TC
- USART_IT_RXNE
- USART_IT_PE
- USART_IT_ERR
**参数3:**开关状态
- 闭合:ENABLE
- 断开:DISABLE
**作用:**配置USART的中断函数
cpp
//创建USART1初始化函数
void App_USART1_Init(void)
{
/*GPIO结构前置声明*/
GPIO_InitTypeDef GPIO_InitStruct;
//配置发送端Tx对应的PA9引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置接收端Rx对应的PA10引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA10引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);
//初始化串口
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构前置声明*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*硬件流控设为无*/
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
//配置中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
}
3.4.配置NVIC模块


3.4.1.配置中断优先级分组
cpp
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
解析:
**参数:**分组方式选择
- NVIC_PriorityGroup_0:0位抢占优先级,4位子优先级
- NVIC_PriorityGroup_1:1位抢占优先级,3位子优先级
- NVIC_PriorityGroup_2:2位抢占优先级,2位子优先级
- NVIC_PriorityGroup_3:3位抢占优先级,1位子优先级
- NVIC_PriorityGroup_4:4位抢占优先级,0位子优先级
3.4.2.设置中断优先级、闭合开关
cpp
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
解析:
**参数:**初始化的参数结构体地址
**作用:**初始化NVIC,配置NVIC的各种参数
**补充:**NVIC_InitTypeDef结构
cpp
struct NVIC_InitTypeDef
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctioalState NVIC_IRQChannelCmd;
};
分析:
**1.NVIC_IRQChannel:**中断的名称(见stm32f10.h IRQn)
**2.NVIC_IRQChannelPreemptionPriority:**抢占优先级
**3.NVIC_IRQChannelSubPriority:**子优先级
**4.NVIC_IRQChannelCmd:**开关状态

cpp
int main(void)
{
/*设置中断优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
App_OnBoardLED_Init();
App_USART1_Init();
while(1)
{
/*点亮板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
/*延迟*/
Delay(blinkInterval);
/*熄灭板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
/*延迟*/
Delay(blinkInterval);
}
}
//创建USART1初始化函数
void App_USART1_Init(void)
{
/*GPIO结构前置声明*/
GPIO_InitTypeDef GPIO_InitStruct;
//配置发送端Tx对应的PA9引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置接收端Rx对应的PA10引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA10引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);
//初始化串口
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构前置声明*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*硬件流控设为无*/
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
//配置中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//配置NVIC
/*声明NVIC结构变量*/
NVIC_InitTypeDef NVIC_InitStruct;
/*设置USART1中断名称*/
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
/*设置抢占优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
/*子优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
/*闭合中断开关*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/*初始化NVIC*/
NVIC_Init(&NVIC_InitStruct);
}
3.5.编写中断响应函数
cpp
//创建USART中断响应函数
void USART1_IRQHandler(void)
{
/*判断中断产生原因*/
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
{
uint8_t dataRcvd = USART_ReceiveData(USART1);
if(dataRcvd == '0')
{
blinkInterval = 1000;
}
else if(dataRcvd == '1')
{
blinkInterval = 200;
}
else if(dataRcvd == '2')
{
blinkInterval = 50;
}
}
}
3.6.总代码
cpp
#include "stm32f10x.h"
#include "delay.h"
//声明板载LED初始化函数
void App_OnBoardLED_Init(void);
//声明USART1初始化函数
void App_USART1_Init(void);
//声明USART中断响应函数
void USART1_IRQHandler(void);
//创建闪灯间隔变量
uint32_t blinkInterval = 1000;
int main(void)
{
/*设置中断优先级分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
App_OnBoardLED_Init();
App_USART1_Init();
while(1)
{
/*点亮板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
/*延迟*/
Delay(blinkInterval);
/*熄灭板载LED*/
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
/*延迟*/
Delay(blinkInterval);
}
}
//创建USART中断响应函数
void USART1_IRQHandler(void)
{
/*判断中断产生原因*/
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
{
uint8_t dataRcvd = USART_ReceiveData(USART1);
if(dataRcvd == '0')
{
blinkInterval = 1000;
}
else if(dataRcvd == '1')
{
blinkInterval = 200;
}
else if(dataRcvd == '2')
{
blinkInterval = 50;
}
}
}
//创建USART1初始化函数
void App_USART1_Init(void)
{
/*GPIO结构前置声明*/
GPIO_InitTypeDef GPIO_InitStruct;
//配置发送端Tx对应的PA9引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*初始化PA9引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置接收端Rx对应的PA10引脚
/*开启GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
/*初始化PA10引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);
//初始化串口
/*开启USART1模块时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*USART结构前置声明*/
USART_InitTypeDef USART_InitStruct;
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*硬件流控设为无*/
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
/*初始化USART1*/
USART_Init(USART1,&USART_InitStruct);
/*闭合串口总开关*/
USART_Cmd(USART1,ENABLE);
//配置中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//配置NVIC
/*声明NVIC结构变量*/
NVIC_InitTypeDef NVIC_InitStruct;
/*设置USART1中断名称*/
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
/*设置抢占优先级*/
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
/*子优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
/*闭合中断开关*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/*初始化NVIC*/
NVIC_Init(&NVIC_InitStruct);
}
//创建板载LED初始化函数
void App_OnBoardLED_Init(void)
{
/*启动GPIOC时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*声明GPIO结构变量*/
GPIO_InitTypeDef GPIO_InitStruct;
/*选择PC13引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
/*输出开漏模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
/*最大输出速率为2MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
/*初始化PA0引脚*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
