STM32学习(MCU控制)(NVIC)

文章目录

    • 中断【重点,难点】
        • [1. 中断是什么](#1. 中断是什么)
        • [2. Cortex-M3 中断分类](#2. Cortex-M3 中断分类)
        • [3. 外部中断 / EXTI](#3. 外部中断 / EXTI)
          • [3.1 外部中断 / EXTI 概述](#3.1 外部中断 / EXTI 概述)
          • [3.2 外部中断判断逻辑框图](#3.2 外部中断判断逻辑框图)
          • [3.3 按键功能实现外部中断控制 EXTI](#3.3 按键功能实现外部中断控制 EXTI)
            • [3.3.1 按键原理图](#3.3.1 按键原理图)
            • [3.3.2 AFIO 时钟使能](#3.3.2 AFIO 时钟使能)
            • [3.3.3 外部中断线和按键对应关系](#3.3.3 外部中断线和按键对应关系)
            • [3.3.4 外部中断上升沿或者下降沿触发寄存器配置](#3.3.4 外部中断上升沿或者下降沿触发寄存器配置)
            • [3.3.5 外部中断屏蔽寄存器](#3.3.5 外部中断屏蔽寄存器)
            • [3.3.6 中断挂起寄存器](#3.3.6 中断挂起寄存器)
        • [4. IRQn 和 IRQHandler](#4. IRQn 和 IRQHandler)
          • [4.1 IRQn 中断请求编号](#4.1 IRQn 中断请求编号)
          • [4.2 IRQHandler 中断请求处理函数/句柄](#4.2 IRQHandler 中断请求处理函数/句柄)
        • [5. 外部中断控制实现](#5. 外部中断控制实现)
          • [5.1 外部中断配置函数使能函数](#5.1 外部中断配置函数使能函数)
          • [5.2 外部中断处理函数实现](#5.2 外部中断处理函数实现)
        • [6. 内部中断](#6. 内部中断)
          • [6.1 内部中断概述](#6.1 内部中断概述)
          • [6.2 内部中断对应 IRQn 和 IRQHandler](#6.2 内部中断对应 IRQn 和 IRQHandler)
        • [7. 中断优先级](#7. 中断优先级)
          • [7.1 中断优先级概述](#7.1 中断优先级概述)
          • [7.2 中断优先级配置](#7.2 中断优先级配置)
          • [7.3 涉及到的函数](#7.3 涉及到的函数)
        • [8. USART 操作中断优化](#8. USART 操作中断优化)
          • [8.1 USART 中断优化思路](#8.1 USART 中断优化思路)
          • [8.2 数据接收完成问题](#8.2 数据接收完成问题)

中断【重点,难点】

1. 中断是什么

中断 ==> Interrupt。STM32 寄存器开发中,标志为描述如果是 IE (Interrupt Enable) 结尾,大多数情况下都是针对于某个中断的开启/关闭控制

中断操作是 STM32 中针对于特定的事件/行为,进行特定的处理操作。需要【触发机制】和【注册行为】。

  • 【触发机制】程序执行过程中,中断出现,触发其他操作
  • 【注册行为】注册当前 STM32 执行过程中,关注的中断内容是哪一个。

常用概念

  • 断点 :注册的中断触发位置,一般对应电平跳变,寄存器标志位变化。
  • 中断源 : 当前中断注册的功能函数/中断服务函数
  • 压栈 : 中断触发,会将断点对应函数压栈操作,转换后台函数。
2. Cortex-M3 中断分类

Cortex-M3 内核中断分类

  • 内核中断/内部中断 :芯片内部中断,也可以称之为【异常 Exception】。可以任务是程序在正常情况下,出现不同于平常的数据情况,可以是错误,可以是标记。一般关注的是寄存器中的中断标志位
  • 外设中断:Cortex-M3 内核 预留了外部中断控制线,可以通过外部中断控制线高低电平切换**,出现上升沿和下降沿进行中断触发。**
3. 外部中断 / EXTI
3.1 外部中断 / EXTI 概述
  • 外部中断线一共 EXTI0 ~ EXTI19
  • 其中 EXTI0 ~ EXTI15 对应 GPIO 口,规则是 EXTI0 对应所有的 GPIO 组中 0 引脚,例如 PA0 ~ PG0
  • 其他 EXTI16 ~ EXTI19 暂时不使用。
3.2 外部中断判断逻辑框图
  • 外部中断线通过
    • 上升沿触发选择寄存器
    • 下降沿触发选择寄存器
  • 决定当前中断的触发方式。需要根据电路分析,找到默认的电平情况下,选择合理的触发方式。
3.3 按键功能实现外部中断控制 EXTI
3.3.1 按键原理图
  • PA0 ==> GPIO 工作模式为【下拉输入模式】
  • PE2 PE3 PE4 ==> GPIO 工作模式为【上拉拉输入模式】
  • 对应时钟配置寄存器是 APB2ENR 位2 IOA 位6 IOE
3.3.2 AFIO 时钟使能
  • 根据目录分析,当前 EXTI 外部中断线,控制中需要设计到 AFIO 相关寄存器。AFIO 是 IO 引脚复用控制寄存器。
  • 其中 AFIO_EXTICR1 ~ AFIO_EXTICR4 是用于控制外部中断线内容
  • AFIO 对应的时钟是 APB2,需要进行使能操作。
3.3.3 外部中断线和按键对应关系
  • 按键设计到引脚分别对应的外表中断线为

  • KEY_UP ==> PA0 ==> EXTI0

  • KEY_2 ==> PE2 ==> EXTI2

  • KEY_1 ==> PE3 ==> EXTI3

  • KEY_0 ==> PE4 ==> EXTI4

  • 【注意】在 AFIO 寄存器中,为了分别管理 EXTICR1 ~ EXTICR4 采用数组形式进行数据管理。底层代码

    c 复制代码

AFIO_EXTICR[0] <> 对应寄存器名称 AFIO_EXTICR1
AFIO_EXTICR[1] <> 对应寄存器名称 AFIO_EXTICR2

AFIO_EXTICR[2] <> 对应寄存器名称 AFIO_EXTICR3
AFIO_EXTICR[3] <> 对应寄存器名称 AFIO_EXTICR4

复制代码
- 底层原码内容

```c
/** 
  * @brief Alternate Function I/O
  */

typedef struct
  {
  __IO uint32_t EVCR;
  __IO uint32_t MAPR;
  __IO uint32_t EXTICR[4];
  uint32_t RESERVED0;
  __IO uint32_t MAPR2;  
} AFIO_TypeDef;
3.3.4 外部中断上升沿或者下降沿触发寄存器配置
  • 以下两个寄存器用于控制当前触发的中断方式
  • EXTI_RTSR 控制对应外部中断控制线触发方式为 上升沿触发
  • EXTI_FTSR 控制对应外部中断控制线触发方式为 下降沿触发
3.3.5 外部中断屏蔽寄存器

外部中断屏蔽寄存器是用于控制对应外部中断线,是否运行被 MCU 处理。如果需要使用对应的外部中断控制,必须开启中断允许。

3.3.6 中断挂起寄存器

利用 EXTI_PR 寄存器明确当前中断是否触发,并且在处理之后,给予 write 1操作,机械能挂起寄存器复位。

4. IRQn 和 IRQHandler
4.1 IRQn 中断请求编号

IRQn ==> I nterrupt R eQ uest number

  • 在 Cortex-M 内核中设计的中断请求编号,具备唯一性,同时根据当前设备的小容量,中容量,大容量 支持的 IRQn 数量不同。

  • IRQn 是用于在 NVIC 中注册当前对应中断服务,同时绑定对应的中断处理函数。

以上是 EXTI 外部中断线对应的 IRQn,这里需要使用的有

  • EXTI0_IRQn ==> EXTI0 ==> PA0 ==> KEY_UP
  • EXTI2_IRQn ==> EXTI2 ==> PE2 ==> KEY2
  • EXTI3_IRQn ==> EXTI3 ==> PE3 ==> KEY1
  • EXTI4_IRQn ==> EXTI4 ==> PE4 ==> KEY0
4.2 IRQHandler 中断请求处理函数/句柄

IRQHandler ==> I nterrupt R eQ uest Handler

  • 中断请求处理函数/句柄。
  • 在 Cortex-M 内核每一个中断请求,内核提供了对应的唯一处理函数,如果需要完成中断服务,必须实现对应的中断处理函数/句柄。

以上是 EXIT 外部中断 IRQn 对应的处理函数

  • EXTI0_IRQn ==> EXTI0_IRQHandler ==> EXTI0 ==> PA0 ==> KEY_UP
  • EXTI2_IRQn > EXTI2_IRQHandler> EXTI2 ==> PE2 ==> KEY2
  • EXTI3_IRQn > EXTI3_IRQHandler> EXTI3 ==> PE3 ==> KEY1
  • EXTI4_IRQn > EXTI4_IRQHandler> EXTI4 ==> PE4 ==> KEY0
5. 外部中断控制实现
5.1 外部中断配置函数使能函数
c 复制代码
void Key_EXTI_Interrupt_Enable(void)
{
	// 因为 Key_Init 已经完成了 KEY 对应 GPIO 配置。
	// 1. AFIO 时钟使能,开启当前 IO 引脚复用功能。
	RCC->APB2ENR |= 0x01; 
	
	/*
	2. 外部中断控制线配置
		EXTI0 ==> PA0 EXTI2 ==> PE2 EXTI3 ==> PE3 EXTI4 == PE4
		
		EXTI0 对应寄存器位置为 0000 ==> PA[0]
		EXTI2 对应寄存器位置为 0100 ==> PE[2]
		EXTI3 对应寄存器位置为 0100 ==> PE[3]
		EXTI4 对应寄存器位置为 0100 ==> PE[4]
		
		当前 AFIO 中的针对于 EXTICR 寄存器采用数组形式进行管理
			EXTI_CR1 ==> AFIO->EXTICR[0] ==> EXTI0 ~ EXIT3
			EXTI_CR2 ==> AFIO->EXTICR[1] ==> EXTI4 ~ EXIT7
			EXTI_CR3 ==> AFIO->EXTICR[2] ==> EXTI8 ~ EXIT11
			EXTI_CR4 ==> AFIO->EXTICR[3] ==> EXTI12 ~ EXIT15
		
		根据当前需求,需要配置 
			EXTI_CR1 ==> AFIO->EXTICR[0] ==> 0100 0100 0000 0000 ==> 0x44 << 8
				对应配置 EXTI0 EXTI2 EXTI3
			
			EXTI_CR2 ==> AFIO->EXTICR[1] ==> 0000 0000 0000 0100 ==> 0x04
				对应配置 EXTI4
	*/
	AFIO->EXTICR[0] |= 0x44 << 8;
	AFIO->EXTICR[1] |= 0x04;
	
	/*
	3. 配置当前中断触发方式
		EXTI0 ==> KEY_UP EXTI2 ==> KEY2 EXTI3 ==> KEY1 EXTI4 == KEY0
		
		下降沿 ==> KEY0 KEY1 KEY2
					EXTI2 EXTI3 EXTI4
		上升沿 ==> KEY_UP
					EXTI0 
	*/
	EXTI->RTSR |= 0x01;
	EXTI->FTSR |= 0x07 << 2;
	
	/*
	4. 中断屏蔽寄存器,开启对应中断需求
		EXTI->IMR,需要开发 EXTI0 EXTI2 EXTI3 EXTI4
		0001 1101
	*/
	EXTI->IMR |= 0x1D;
	
	/*
	5. 设置中断优先级和对应中断使能。
		需要利用 NVIC 提供的函数,已经当前中断对应的 IRQn
		
		void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
			设置指定中断对应的优先级,需要提供的参数是 IRQn 和 优先级整数
			IRQn_Type IRQn ==> Interrupt ReQuest number 中断请求编号
			uint32_t priority 中断优先级
		
	当前操作所有中断,全部使用 1 优先级。
	*/
	NVIC_SetPriority(EXTI0_IRQn, 1);
	NVIC_SetPriority(EXTI2_IRQn, 1);
	NVIC_SetPriority(EXTI3_IRQn, 1);
	NVIC_SetPriority(EXTI4_IRQn, 1);
	
	/*
	void NVIC_EnableIRQ(IRQn_Type IRQn);
		指定 IRQn 对应的中断使能,注册对应中断请求。
		IRQn_Type IRQn ==> Interrupt ReQuest number 中断请求编号
	*/
	NVIC_EnableIRQ(EXTI0_IRQn);
	NVIC_EnableIRQ(EXTI2_IRQn);
	NVIC_EnableIRQ(EXTI3_IRQn);
	NVIC_EnableIRQ(EXTI4_IRQn);			
}
5.2 外部中断处理函数实现

外部中断处理函数是 Cortex-M 内核声明的标准函数,无需二次声明,仅需要完成对应的实现即可

c 复制代码
/*
针对于 EXTI0 外部中断控制线,Handler 处理函数,当前函数为内核声明函数。
仅需要提供对应的实现。
函数要求无参数无返回值,且名称固定 EXIT0 ==> EXTI0_IRQHandler

EXTI0_IRQHandler ==> PA0 ==> KEY_UP 按键
*/
void EXTI0_IRQHandler(void)
{
	/*
	根据当前 EXTI_PR 寄存器判断当前中断是否触发
	如果 EXIT0 中断触发,对应 EXTI_PR 寄存器位 0 ==> PR0
	寄存器标志位为 1
	
	EXTI->PR & 0x01 判断当前 PR0 位置是否为 1
	*/
	if (EXTI->PR & 0x01)
	{
		/*
		根据当前寄存器的使用规则,将 EXTI->PR 中 PR0 位置赋值为 1
		完成寄存器标志复位操作。
		*/
		EXTI->PR = 0x01;
		
		/*
		控制 DS0 对应 PB5 引脚,控制 LED0
		*/
		if (GPIOB->ODR & (0x01 <<5))
		{
			Led0_Ctrl(1);
		}
		else
		{
			Led0_Ctrl(0);
		}
	}
}

/*
EXTI2_IRQHandler ==> PE2 ==> KEY2 按键
*/
void EXTI2_IRQHandler(void)
{
	if (EXTI->PR & (0x01 << 2))
	{
		EXTI->PR = 0x01 << 2;
		
		if (GPIOB->ODR & (0x01 << 8))
		{
			Beep_Ctrl(0);
		}
		else
		{
			Beep_Ctrl(1);
		}
	}
}

/*
EXTI3_IRQHandler ==> PE3 ==> KEY1 按键
*/
void EXTI3_IRQHandler(void)
{
}

/*
EXTI4_IRQHandler ==> PE4 ==> KEY0 按键
*/
void EXTI4_IRQHandler(void)
{
}
6. 内部中断
6.1 内部中断概述

内部中断在内核中称之为异常,一般关注的是指定寄存器的中断标志位。

  • 内部中断中,有三个最高的中断优先级
    • RESET 复位 对应优先级为 -3, 最高优先级,且无法修改
    • NMI 不可屏蔽中断 对应优先级为 -2,且无法修改
    • HardFault 硬件错误中断 对应优先级为 -1,且无法修改
  • 在中断控制中,优先级数值越小,对应的优先等级越高!!!
  • 其中较为重要的内部中断有 SysTick
6.2 内部中断对应 IRQn 和 IRQHandler

内部中断对应 IRQn,根据原码分析,缺少 RESETHardFault 对应的 IRQn,是因为RESETHardFault 对应的中断处理方式为硬件处理方式,与软件内容无关。

内部中断对应的 IRQHandler 函数。其中 Reset_HandlerHardFault_Handler 没有重写的必要性。因为非软件可以处理的异常情况。

  • 其中较为重要的 SysTick_IRQn 和 SysTick_Handler 系统嘀嗒中断处理。
7. 中断优先级
7.1 中断优先级概述

中断优先级是由【占先优先级】和 【次级优先级】两部分组成

  • 占先优先级 ,多个中断触发时,MCU 首先根据占先优先级判断,哪一个优先级高,其他中断进入等待等待状态。【占先优先级谁高谁执行】
  • 次级优先级 ,同时存在多个相同 占先优先级 中断存在,此时 MCU 按照次级优先级来决定执行顺序。但是如果此时有其他中断正常执行,不允许打断中断执行效果。【同步进入,谁高谁执行,如果中断已执行,其他中断不得抢占】
7.2 中断优先级配置

根据当前 Cortex-M3 内核要求,对应的优先级有效位置是 4 位,并且是【高 4 位】。针对于 NVIC 支持的所有优先级分组策略中,有且只有 b111 b110 b101 b100 和 其他有效

  • X 表示占先优先级
  • Y 表示次级优先级
  • Priority Group 分组,对应的当前占先优先级二进制位数
分组 二进制占位 占先区 次级区 占先个数 次级个数
4 XXXX 4 0 16 0
3 XXXY 3 1 8 2
2 XXYY 2 2 4 4
1 XYYY 1 3 2 8
0 YYYY 0 4 0 16
7.3 涉及到的函数

NVIC_SetPriorityGrouping 全局配置

c 复制代码
void NVIC_SetPriorityGrouping(uint32_t bits);

设置整个系统的中断优先级分组方案,决定抢占优先级和子优先级的位数分配。

关键特性

  • 全局性:影响所有中断的优先级解释方式

  • 一次性:通常只在系统初始化时设置一次,之后不应更改

  • 基础性:为后续所有 NVIC_SetPriority 调用提供解释依据

NVIC_SetPriority 个体中断配置

c 复制代码
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority);

在既定分组规则下,为特定中断源设置具体的优先级值。

关键特性

  • 个体性:只影响指定的中断源

  • 依赖性:其数值含义完全取决于 NVIC_SetPriorityGrouping 的设置

  • 多次调用:可为每个中断源单独设置

案例分析

c 复制代码
NVIC_SetPriorityGrouping(2);
// 整个系统中,对应的优先级组为 2 ,2 位占先控制, 2 位次级控制

NVIC_SetPriority(EXTI0_IRQn, 5); // 0101
NVIC_SetPriority(EXTI1_IRQn, 6); // 0110

// 此时 EXTI0_IRQn EXTI1_IRQn 中断同时触发
/*
优先级分析
	占先分析  
		EXTI0_IRQn 和 EXTI1_IRQn 两个中断占先都是 01
	次级分析 
		此时分析次级优先级 01 > 10
		EXTI0_IRQn 执行。
*/

NVIC_SetPriority(EXTI2_IRQn, 2);  // 0010
NVIC_SetPriority(EXTI3_IRQn, 10); // 1010

// 此时 EXTI3_IRQn 正在执行,同时程序运行触发了 EXTI2_IRQn 中断
/*
优先级分析
	占先分析  
		EXTI2_IRQn 占先为 00 和 EXTI3_IRQn 10
		00 > 10
		EXTI3_IRQn 中断被【压栈】,执行 EXTI2_IRQn 中断内容
*/
8. USART 操作中断优化
8.1 USART 中断优化思路

利用 USART_CR 控制寄存器和 USART_SR 状态寄存器来判断当前 USART 的工作状态。

  • 解决数据接收完成问题
8.2 数据接收完成问题

数据读取完成中断判断
CR 控制寄存器

SR 状态寄存器

目标 USART1 对应的 IRQn 和 IRQHandler 函数

  • IRQn ==>USART1_IRQn
  • IRQHandler ==> void USART1_IRQHandler(void)
    中断使能和配置
c 复制代码
void USART1_Interrupt_Enable(void)
{
	/*
	当前 USART1 的控制寄存器中,
		打开 IDLEIE 数据总线空闲中断使能
		打开 RXNEIE 数据总线空闲中断使能
	*/
	USART1->CR1 |= (0x01 << 4) | (0x01 << 5);
	
	/*
	设置当前 USART1 对应的中断优先级为 0001 在
	全局优先级设置为 2 的情况下  占先 0 次级 1
	*/
	NVIC_SetPriority(USART1_IRQn, 1); // 0001 占先 0 次级 1
	
	/*
	告知当前 MCU 使能对应的 USART1_IRQn 中断
	*/
	NVIC_EnableIRQ(USART1_IRQn);
}

利用中断完成数据读取函数,已经配套的结构体数据存储

c 复制代码
#define DATA_SIZE (256)

typedef struct usart1_data
{
	u8 data[DATA_SIZE]; // 接受数据缓冲区
	u8 flag;            // 数据处理标志位
	u16 count;           // 读取到的有效字节个数
} USART1_Data;

extern USART1_Data usart1_val;
c 复制代码
/*
完成 USART1 对应的 USART1_IRQn 对应的中断处理函数
当前中断处理函数是用于接收的数据内容进行处置操作,将接收的数据
存储到 USART1_Data 结构体中,对应的 u8 data[DATA_SIZE] 数组
*/
void USART1_IRQHandler(void)
{
	u32 val = 0;
	
	/*
	usart1_val.flag ==> 1 表示当前数据接收完毕,同时已经回显到
	PC 端 USART 工具
	*/
	if (usart1_val.flag)
	{
		// 对当前数据空间进行擦除,
		memset(&usart1_val, 0, sizeof(USART1_Data));
	}
		
	/*
	如果当前触发的中断为 【RXNE 中断】,表示数据在通过串口
	传递到 MCU 中
	*/
	if (USART1->SR & (0x01 << 5))
	{
		usart1_val.data[usart1_val.count++] = USART1->DR;
		
		/*
		当前接收到的有效字节个数 == DATA_SIZE,当前数据缓冲区数组已满
		*/
		if (DATA_SIZE == usart1_val.count)
		{
			USART1_SendBuffer(usart1_val.data, usart1_val.count);
			usart1_val.flag = 1;
		}
	}

	/*
	如果当前数据总线空闲 【IDLE 中断】,表示数据传递完毕
	*/
	if (USART1->SR & (0x01 << 4))
	{
		/*
		表示当前数据接收已完成
		*/
		usart1_val.flag = 1;
		
		/*
		需要完成对于当前 USART1->SR IDLE 数据总线空闲
		中断标志位进行清除操作。
		【官方要求】
			1. 读取 USART1->SR 寄存器
			2. 读取 USART1->DR 寄存器
		*/
		val = USART1->SR;
		val = USART1->DR;
        
		// 将数据回显到 PC 端 USART 调试工具
		USART1_SendBuffer(usart1_val.data, usart1_val.count);
	}*
}
相关推荐
三佛科技-187366133978 小时前
FT8370A/B/C/CD/CP高性能次边同步整流芯片典型电路及管脚定义
stm32·单片机·嵌入式硬件
D.....l8 小时前
STM32学习(MCU控制)(WiFi and MQTT)
stm32·单片机·学习
Net_Walke13 小时前
【STM32】CLion STM32开发环境搭建
stm32·单片机
laocooon52385788615 小时前
运行当前位置,显示文件全名,检查是否扩展名多次重叠
stm32·单片机·嵌入式硬件
沉醉不知归路116 小时前
cursor导入keil工程详细步骤
stm32
D.....l17 小时前
STM32学习(MCU控制)(I2C 模拟)
stm32·单片机·学习
A9better18 小时前
嵌入式开发学习日志42——stm32之SPI工作方式
stm32·单片机·嵌入式硬件·学习
D.....l19 小时前
STM32学习(MCU控制)(SysTick and TIM)
stm32·单片机·学习
python百炼成钢1 天前
10.串口
linux·stm32·单片机·嵌入式硬件