STM32标准库自学笔记:中断系统详解
目录
一、中断的概念
二、中断优先级
三、串口中断例程
非常感谢“B站UP铁头山羊”老师对STM32的入门讲解,建议大家在看我整理的笔记时,配合山羊老师的视频一起食用!!
一、中断的概念
中断发生,常规程序暂停,单片机调用中断响应函数,中断响应函数处理完继续执行常规程序。
实例:串口调试助手控制板载LED的闪烁速度,分为两步实现
- 闪灯程序,即点亮LED一段时间,再熄灭LED一段时间(点亮+延迟+熄灭+延迟)
- 串口数据接收程序,先检测RxNE标志位是否接收到数据,调用函数读取数据,根据数据改变闪烁速度
int main(void)
{
while(1)
{
GPIO_WriteBit(GPIOx, GPIO_Pin_xx, Bit_SET); //点亮LED
delay(...); //延时一段时间
GPIO_WriteBit(GPIOx, GPIO_Pin_xx, Bit_RESET); //熄灭LED
delay(...); //延时一段时间
}
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) //判断是否接收到数据
{
uint8_t byteRcvd = USART_ReceiveData(USART1); //读取接收到的数据
}
}
×:需要等到亮灯和灭灯(一个周期)后才读取数据,假设读取数据的串口为115200,一个字节为起始位+数据位+停止位=1+8+1=10bit,115200/10=11520(每秒传输1个位就会产生11520个字节),每个字节大概接收0.1ms远小于延迟的周期时间(假设为亮100ms,灭100ms,总200ms),中间字节会丢失。所以合并程序不可行。
加入中断:将数据读取写在调用中断中,这样可以输入数据就读取数据,保证没有遗漏,如下
二、中断优先级
优先处理紧急的程序。
中断优先级由四个位表示,有五个分组,其中由抢占优先级和子优先级组成,如下图,以1011为例来表示不同分组的中断。
中断嵌套:更高优先级的中断打断正在执行的中断,例如普通病人在看病时突然来了急症病人,优先处理急症病人再处理普通病人。
条件:抢占优先级更高,数字越小中断,优先级越高。下图为以中断优先级为分组2来判断1与2、3、4的执行顺序。
中断排队: 优先级相仿,等待前一个中断执行完再处理新中断,不打断当前中断执行,优先级高排前面,优先级相同则先来后到。
三、串口中断例程
配置RxNE标志位来产生中断,其中黑色月牙型是逻辑或门,标志位共用中断,只要其中一个为1则触发了中断,标志位和中断直接的开关用于中断屏蔽,断开则不会触发中断,这里需要把RxNE这的开关也打开。
闭合该开关需要使用到的函数:(中断配置)
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
USARTx |
串口名称 |
USART_IT |
标志位名称,例如USART_IT_TXE,USART_IT_RxNE |
NewState | 开关状态,ENABLE—打开,DISABLE—关闭 |
NVIC模块(无需配置时钟,因为内核的时钟始终开启):
- 配置中断优先级分组,配置抢占优先级和子优先级的位
- 配置四个比特位位的中断优先级,代表优先级的紧急程度
- 闭合开关,中断才能使能
配置分组需要用到的编程接口,一般写在程序的一开始,即main后:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //设置NVIC中断优先级分组函数
中断初始化是串口初始化的一部分,即写在串口初始化中。
其中在设置抢占优先级时,由于例程所给为分组2,即抢占优先级占了2bit,有00~11可设置,即对应十进制的0~3,所以所设值(对NVIC_IRQChannelPreemptionPriority的设置)不可超出这个范围,子优先级同理。参考代码如下:
NVIC_InitTypeDef NVIC_InitStruct; //定义NVIC初始化结构体变量
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //USART1中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; //子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStruct); //初始化NVIC
全部代码参考如下,编写思路:
- 编写闪灯代码
- 初始化串口(串口和串口引脚初始化)
- RxNE标志位触发中断
- 配置NVIC模块
- 编写中断响应函数
#include "Delay.h" //需要注意添加的头文件
void App_OnBoardLED_Init(void);
void App_USART1_Init(void);
uint32_t blinkInterval = 1000; //定义LED闪烁的时间间隔
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断优先级分组
App_OnBoardLED_Init(); //初始化板载LED的调用
App_USART1_Init(); //初始化串口1的调用
while(1)
{
GPIO_WriteBit(GPIOx, GPIO_Pin_xx, Bit_SET); //点亮LED
Delay(blinkInterval); //延时一段时间
GPIO_WriteBit(GPIOx, GPIO_Pin_xx, Bit_RESET); //熄灭LED
Delay(blinkInterval); //延时一段时间
}
}
void USART1_IRQHandler(void) //串口1中断函数,在startup文件中查找名称
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是RxNE触发的中断
{
uint8_t byteRcvd = USART_ReceiveData(USART1); //定义接收到的数据
if (byteRcvd == '0') //如果接收到的数据为0
{
blinkInterval = 1000; //LED闪烁的时间间隔为1s
}
else if (byteRcvd == '1') //如果接收到的数据为1
{
blinkInterval = 200; //LED闪烁的时间间隔为200ms
}
else if (byteRcvd == '2') //如果接收到的数据为2
{
blinkInterval = 50; //LED闪烁的时间间隔为50ms
}
}
}
void App_OnBoardLED_Init(void) //初始化板载LED
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE); //开启GPIOx的时钟
GPIO_InitTypeDef GPIO_InitStruct; //定义GPIO初始化结构体变量
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_xx; //板载LED对应的引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; //2MHz最大速度
GPIO_Init(GPIOx, &GPIO_InitStruct); //初始化GPIOx
}
void App_USART1_Init(void) //初始化串口1
{
GPIO_InitTypeDef GPIO_InitStruct; //定义GPIO初始化结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE); //开启GPIOx的时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_xx; //USART1的引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; //10MHz最大速度
GPIO_Init(GPIOx, &GPIO_InitStruct); //初始化GPIOx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE); //开启GPIOx的时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_xx; //USART1的引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //输入上拉模式
GPIO_Init(GPIOx, &GPIO_InitStruct); //初始化GPIOx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟
USART_InitTypeDef USART_InitStruct; //定义USART初始化结构体变量
USART_InitStruct.USART_BaudRate = 115200; //波特率115200
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_InitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStruct.USART_Parity = USART_Parity_No; //无校验
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送模式
USART_Init(USART1, &USART_InitStruct); //初始化USART1
USART_Cmd(USART1, ENABLE); //使能USART1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //配置中断
NVIC_InitTypeDef NVIC_InitStruct; //定义NVIC初始化结构体变量
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //USART1中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; //子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStruct); //初始化NVIC
}
作者:coconaraine