STM32标准库自学笔记:中断系统详解

目录

一、中断的概念

二、中断优先级 

三、串口中断例程 


 非常感谢“B站UP铁头山羊”老师对STM32的入门讲解,建议大家在看我整理的笔记时,配合山羊老师的视频一起食用!!

一、中断的概念

中断发生,常规程序暂停,单片机调用中断响应函数,中断响应函数处理完继续执行常规程序。

实例:串口调试助手控制板载LED的闪烁速度,分为两步实现

  1. 闪灯程序,即点亮LED一段时间,再熄灭LED一段时间(点亮+延迟+熄灭+延迟)
  2. 串口数据接收程序,先检测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模块(无需配置时钟,因为内核的时钟始终开启):

  1. 配置中断优先级分组,配置抢占优先级和子优先级的位
  2. 配置四个比特位位的中断优先级,代表优先级的紧急程度
  3. 闭合开关,中断才能使能

 配置分组需要用到的编程接口,一般写在程序的一开始,即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

全部代码参考如下,编写思路:

  1.  编写闪灯代码
  2. 初始化串口(串口和串口引脚初始化)
  3. RxNE标志位触发中断
  4. 配置NVIC模块
  5. 编写中断响应函数
#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

物联沃分享整理
物联沃-IOTWORD物联网 » STM32标准库自学笔记:中断系统详解

发表回复