Day07_STM32-单片机-中断
Day07_STM32 单片机 - 中断
一、处理器的工作模式
1.1 Cortex-M 核工作模式
1.异常模式
2.线程模式
1.2 Cortex-A 核工作模式
ARM-v7架构:
1.非特权模式:User模式
2.特权模式:
2.1 非异常模式:Sys模式
2.2 异常模式:
IRQ模式 - 普通中断异常模式
FIQ模式 - 快速中断异常模式
SVC模式 - 超级管理异常模式
ABT模式 - 中止访问异常模式
UDF模式 - 未定义异常模式
ARM-v8架构:
多两种:MON模式 - 安全监管模式、HYP模式 - 虚拟化技术模式
二、异常处理流程
图 1 异常处理流程图
2.1 异常源
异常源:触发处理器执行异常事件对应的处理逻辑的源头(集合);
异常源支持很多异常事件。
=================================================================
- 5 种异常模式对应 7 种异常源;
- 每种异常源存在很多异常事件 - 异常源就是异常事件的集合;
- SVC 模式下的复位异常源优先等级最高,处理器优先处理。
2.2 自动保护现场 - CPU 自动完成
自动保护现场需要做 4 件事:
1.将 CPSR 寄存器中的值保存到 SPSR_
寄存器中
2.修改 CPSR 寄存器中的位:
2.1 修改 CPSR 寄存器中的 I / F 位(是否屏蔽 IRQ / FIQ 模式);
2.2 修改 CPSR 寄存器中的 T 位(是否使用 ARM / Thumb 汇编指令集);
2.3 修改 CPSR 寄存器中的 M 位(进入对应的异常模式)。
3.将函数的返回地址保存到 LR 寄存器中;
4.PC 寄存器指向异常向量表。
2.3 手动恢复现场 - 程序员手动完成
- 将 SPSR_
寄存器中的值恢复到 CPSR 寄存器中; - 将 LR 寄存器中的值赋值给 PC 寄存器。
2.4 异常向量表
地址 异常类型
0x1C FIQ
0x18 IRQ
0x14 (Reserved)
0x10 Data Abort
0x0C Prefetch Abort
0x08 Software Interrupt
0x04 Undefined Instruction
0x00 Reset
异常向量表:异常源的集合
当发生异常事件后,处理器检测当前异常事件所属异常源,就需要给各个异常源分配不同的内存空间地址,处理器去寻址查找。
异常向量表占内存空间的 32 个字节,每个异常源占 4 个字节空间,同时有 4 个字节的空间保留。
图 2 异常处理的流程
为什么引入异常向量表:
在异常出发后,最后是需要执行异常处理的函数;
而在开发板 / 系统上电复位后,函数的地址、参数的地址、变量的地址可能发生改变,对于 CPU 无从知晓变化后的地址,所以可以固定一片内存空间地址,引导 CPU 找到异常处理函数。
ARM 公司在开发并生产内核时,固定一片 32 个字节的地址空间,存放各个异常源的地址,称为异常向量表。
2.5 汇编代码模拟异常处理流程
.text
.global _start
_start:
@ 构建异常向量表
b reset_handler
b undefined_handler
b swi_handler
b prefetch_handler
b data_handler
b . @ 占4个字节空间操作
b irq_handler
b fiq_handler
reset_handler:
@ 为SVC模式下的SP寄存器给定一个内存空间地址
ldr sp, =0x40000820
@ 从SVC模式切换到User模式下,执行用户代码
msr cpsr, #0xD0
@ 用户代码,以下相当于main函数
mov r0, #0x1
mov r1, #0x2
@ 软中断异常事件触发
@ swi 2这条汇编指令执行完毕后,需要看到的效果
@ 1、CPSR寄存器中的值是否被保存到SPSR寄存器中
@ 2、CPSR寄存器中的值是否被改变(IFTM位)
@ 3、LR寄存器中是否保存函数的返回地址
@ 4、PC是否指向异常向量表中软中断异常源的地址
swi 2
add r2, r1, r0
undefined_handler:
swi_handler:
@ 压栈保存现场,保存局部变量和函数返回地址
stmfd sp!, {r0-r1, lr}
@ 软中断异常源下第一个异常事件的处理函数
mov r0, #0xff
mov r1, #0xee
add r2, r1, r0
@ 出栈恢复现场,恢复局部变量和函数返回地址
@ 还需要恢复SPSR寄存器中的值给到CPSR寄存器
@ ^的作用:用于将SPSR寄存器中的值赋值给CPSR寄存器
ldmfd sp!, {r0-r1, pc}^
prefetch_handler:
data_handler:
irq_handler:
fiq_handler:
stop:
b stop
.end
当异常发生时,CPU 根据异常的类型,找到异常向量表中对应异常源的固定地址,随后跳转到改地址去执行相应的异常处理函数,这和异常源的名字无关,名字只是帮助开发者理解的符号。
三、分析按键的电路图
KEY1 - PC9
KEY2 - PC8
KEY3 - PC5
图 3 按键抖动示意图
GPIO 检测按键的抬起和按下(高/ 低电平),中断检测按键抬起 / 按下的时机(上升沿/ 下降沿)。
四、分析芯片手册
图 4 NVIC 和 EXTI
EXTI:外部中断和事件控制器
NVIC:嵌套向量中断控制器
嵌套:中断的嵌套,指的是中断具备优先级等级,高优先级等级的中断信号可以打断低优先级的中断信号。
向量:所有异常中断统一由向量表进行管理。
在使用 Cortex-M 核(MCU),中断处理一般使用 EXTI + NVIC 来实现
在使用 Cortex-A 核(MPU),中断处理一般使用 EXTI + GIC(GICC+CPID) 来实现
4.1 EXTI 介绍
1)介绍
图 5 EXTI 介绍
外部中断和事件控制器(EXTI)通过可配置的事件输入来管理各个 CPU 和系统唤醒。它向电源控制提供唤醒请求,并向 CPU NVIC 生成中断请求以及向 CPU 事件输入生成事件。对于 CPU,需要额外的事件生成模块(EVG)来生成CPU事件信号。
2)主要特性
图 6 EXTI 主要特性
- 支持 26 个输入事件编号;
- 可选择的边沿触发方式 - 上升沿触发、下降沿触发、上升沿下降沿都触发;
- 中断挂起状态标志位 - 上升沿和下降沿的中断状态在硬件层面被分开记录和处理;
- 支持中断信号和输入事件的屏蔽功能;
- 支持多个 GPIO 端口的选择。
4.2 输入事件编号
图 7 输入事件编号列表
输入事件编号(Event Input Number)是硬件层用于唯一标识外部中断/事件(EXTI)源的数字ID,每个编号对应一条独立的EXTI线(如编号0对应
EXTI[0]
)。其核心作用是通过数字映射将物理引脚的电平变化与系统中断/事件处理逻辑精准关联。
4.3 EXTI 内部框图
图 8 EXTI 内部框图
图 10 EXTI 内部框图描述
EXTI 外设控制器中有一个寄存器块,这个寄存器块被连接在 AHB3 总线上(EXTI 被连接在 AHB3 总线上)。
输入事件触发块用于提供输入事件边沿触发的逻辑,也就是需要设置输入事件的触发方式为上升沿触发 / 下降沿触发 / 双边沿触发。
输入事件屏蔽块用于提供输入事件的分配(可以分配给 PWR 用于系统唤醒、可以分配给 CPU 下的 NVIC 用于中断控制、也可以分配给 EVG 用于事件输出、还可以把上述操作屏蔽掉)。
IO 端口选择块提供了 GPIO 端口选择的功能(也就是让 EXTI 知道是哪个 GPIO 端口产生了中断信号)。
4.4 寄存器功能分析
图 11 EXTI 功能描述
中断的挂起:中断信号到达,等待被处理,需要执行这个中断信号对应的逻辑代码。也就是执行中断信号对应逻辑代码前,需要判断这个中断信号是否发生,即这个中断是否被挂起。
4.5 GPIO 端口选择
图 12 EXTI 多路选择(GPIO 端口选择)
4.6 寄存器分析
1)EXTI_FTSR1 寄存器
图 13 EXTI_FTSR1
下降沿触发选择寄存器
写入 0:PC9 引脚下降沿触发不使能
写入 1:PC9 引脚下降沿触发使能
2)EXTI_FPR1 寄存器
图 14 EXTI_FPR1
图 15
下降沿触发挂起寄存器
rc_w1权限:
r c_w1
写入0:
没有影响
**写入1:**人为清除对应的值**读 0:**没有下降沿触发请求存在
也就是下降沿没有触发,中断没有挂起
**读 1:**下降沿触发请求存在
也就是下降沿触发,中断被挂起当我们检测按键是否按下时,也就是检测中断是否被挂起,也就是读取这个寄存器对应位是否为 1:
读取到 1:就代表中断被挂起,可以执行处理逻辑;
读到到 0:就代表中断没有被挂起,不需要执行处理逻辑;
当我们读取到中断被挂起后,为了让中断不一直处于被挂起状态,需要人为手动解除中断的挂起。
3)EXTI_EXTICRm 寄存器
图 16 EXTI_EXTICRm
图 17 GPIO 端口的选择
EXTI_CRm 寄存器有 4 个(m = 1 ~ 4),一个寄存器管理 4 个 EXTI 线,其中每 8 位管理一个 EXTI 线(如 PA0、PB0……、PJ0 共属一条 EXTI0 线,往 8 位写入从 0x00 ~ 0x09 可以选择从 A ~ J 的对应引脚触发中断。
4)EXTI_IMR1 寄存器
图 18 EXTI_IMR1
EXTI 中断屏蔽寄存器
按键 KEY1 的引脚是 PC9,所属中断线为 EXTI9,对应输入事件的编号是 9.
图 19 EXTI_IMR1 - IM9 位
写入 0 是屏蔽对应输入事件产生的中断的信号,所以我们需要向 Bit 9 写入 0b1使能 EXTI9 的中断信号。
4.7 NVIC 章节
图 20 NVIC 嵌套向量控制器主要特性
中断号:每个中断信号例如 EXTI0 ~ EXTI15 在被 NVIC 检测时,都具有自己的一个唯一标识符,就是中断号。
NVIC 支持 140 个可屏蔽的中断通道以及 16 个可编程控制的优先级等级。
优先级等级的数字越小,代表优先级越高。
图 21 NVIC 嵌套向量表
五、CubeMX 配置
图 22 步骤 1
图 23 步骤 2
NVIC 的作用:
- 使能对应的中断信号(是否屏蔽中断信号)
- 为对应的中断信号设置优先级等级
- 检测中断信号是否触发(中断信号挂起寄存器)
抢占式优先级等级(Preemption Priority)
也就是中断优先级等级,分为 0 - 15,数字越小,抢占式优先级等级越高。
响应式优先级等级(Sub Priority)
由于抢占式优先级等级只有 0 - 15,对于中断信号过多的情况,会存在抢占式优先级等级相同的情况,可以设置一个次优先级等级进行二次划分。
图 24 步骤 3
图 25 步骤 4
图 26 步骤 5
六、逻辑代码
DCD EXTI0_IRQHandler ; EXTI Line0 interrupt DCD EXTI1_IRQHandler ; EXTI Line1 interrupt DCD EXTI2_IRQHandler ; EXTI Line2 interrupt DCD EXTI3_IRQHandler ; EXTI Line3 interrupt DCD EXTI4_IRQHandler ; EXTI Line4 interrupt DCD EXTI5_IRQHandler ; EXTI Line5 interrupt DCD EXTI6_IRQHandler ; EXTI Line6 interrupt DCD EXTI7_IRQHandler ; EXTI Line7 interrupt DCD EXTI8_IRQHandler ; EXTI Line8 interrupt DCD EXTI9_IRQHandler ; EXTI Line9 interrupt DCD EXTI10_IRQHandler ; EXTI Line10 interrupt DCD EXTI11_IRQHandler ; EXTI Line11 interrupt DCD EXTI12_IRQHandler ; EXTI Line12 interrupt DCD EXTI13_IRQHandler ; EXTI Line13 interrupt DCD EXTI14_IRQHandler ; EXTI Line14 interrupt DCD EXTI15_IRQHandler ; EXTI Line15 interrupt
在 startup_stm32u575xx.s 汇编文件中,EXTI9_Handler 就是 EXTi9 异常源对应的异常处理函数。
void EXTI9_IRQHandler(void) { /* USER CODE BEGIN EXTI9_IRQn 0 */ /* USER CODE END EXTI9_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9); /* USER CODE BEGIN EXTI9_IRQn 1 */ /* USER CODE END EXTI9_IRQn 1 */ }
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN); 是 HAL 库提供的处理 EXTI9 异常源产生中断的中断处理函数。
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if (__HAL_GPIO_EXTI_GET_RISING_IT(GPIO_Pin) != 0U) { __HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_Pin); HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin); } if (__HAL_GPIO_EXTI_GET_FALLING_IT(GPIO_Pin) != 0U) // 判断下降沿中断信号是否触发 == 下降沿中断信号被挂起 // if( EXTI->FPR1 & (0x1 << 9) == 1) { __HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_Pin); // 清除下降沿中断挂起标志位 // EXTI->FPR1 |= (0x1 << 9); HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin); // 下降沿中断信号触发后,需要执行的逻辑代码的回调函数 } }
__weak void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin) 使用 __weak 修饰的函数,称之为弱函数;
__weak的作用:使用__weak修饰的函数,表示当前函数可以被重写。
6.2 API 接口
1)HAL_GPIO_EXTI_Falling_Callback 下降沿触发回调函数
__weak void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
功能:
HAL 库提供的用于执行下降沿中断信号触发后需要执行的弱函数(支持被重写)
参数:
GPIO_Pin:触发下下降沿中断信号的GPIO引脚编号
返回值:
无
2)HAL_GPIO_EXTI_Rising_Callback 上升沿触发回调函数
__weak void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
功能:
HAL 库提供的用于执行上降沿中断信号触发后需要执行的弱函数(支持被重写)
参数:
GPIO_Pin:触发上升沿中断信号的GPIO引脚编号
返回值:
无
6.3 HAL 库代码编写
在 stm32u5xx_it.c 文件中重写上升沿和下降沿触发回调函数:
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,0);
}
}
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_4,1);
}
}
6.4 标准库代码编写
void EXTI9_IRQHandler(void)
{
/* USER CODE BEGIN EXTI9_IRQn 0 */
// 此处,可以直接写异常处理函数
// 判断PC9引脚的下降沿中断信号是否触发 == 获取下降沿中断挂起标志位的值
if ((EXTI->FPR1 & (0x1 << 9)) != 0) // if((EXTI->FPR1 & (0x1 << 9)) == 0x200)
{
// 进入if语句,代表下降沿中断触发
// 清除PC9下降沿中断挂起标志位
EXTI->FPR1 |= (0x1 << 9);
// 执行PC9下降沿中断触发后的代码逻辑 == 点亮LED灯(PC4)
GPIOC->ODR |= (0x1 << 4);
}
// 判断PC9引脚的上升沿中断信号是否触发 == 获取上升沿中断挂起标志位的值
if ((EXTI->RPR1 & (0x1 << 9)) != 0) // if((EXTI->RPR1 & (0x1 << 9)) == 0x200)
{
// 进入if语句,代表上升沿沿中断触发
// 清除PC9上升沿中断挂起标志位
EXTI->RPR1 |= (0x1 << 9);
// 执行PC9上升沿中断触发后的代码逻辑 == 熄灭LED灯(PC4)
GPIOC->ODR &= (~(0x1 << 4));
}
/* USER CODE END EXTI9_IRQn 0 */
// HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
/* USER CODE BEGIN EXTI9_IRQn 1 */
/* USER CODE END EXTI9_IRQn 1 */
}
6.5 中断的处理流程
自动保护现场(CPU 自动完成)
⇩
异常向量表对应异常源
DCD EXTI9_IRQHandler ; EXTI Line9 interrupt
⇩
异常源的对应处理函数
void EXTI9_IRQHandler(void)
⇩
异常事件处理函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if (__HAL_GPIO_EXTI_GET_RISING_IT(GPIO_Pin) != 0U) { __HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_Pin); HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin); } if (__HAL_GPIO_EXTI_GET_FALLING_IT(GPIO_Pin) != 0U) // 判断下降沿中断信号是否触发 == 下降沿中断信号被挂起 // if( EXTI->FPR1 & (0x1 << 9) == 1) { __HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_Pin); // 清除下降沿中断挂起标志位 // EXTI->FPR1 |= (0x1 << 9); HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin); // 下降沿中断信号触发后,需要执行的逻辑代码的回调函数 } }
七、串口中断
7.0 准备工作
图 27 配置 USART1 中断使能
7.1 串口发送中断(不常使用)
1)API 接口
HAL_UART_Transmit_IT
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
功能:
HAL 库提供的用于通过串口进行中断的方式发送数据的函数。
参数:
huart:USART1 的句柄
pData:需要发送的数据
Size:需要发送的数据大小,单位字节
返回值:
成功返回 HAL_OK
失败返回错误码
HAL_UART_TxCpltCallback
__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
功能:
HAL 库提供的用于串口发送中断触发后,执行的回调函数(可以被重写);
当串口发送中断函数执行完毕后,会自动调用这个函数;
当使用串口发送中断函数发送完完整数据后,此时发送中断的中断信号触发,这个函数才被执行。
参数:
huart:USART1 的句柄
返回值:
无
__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
当使用串口发送中断函数发送一半数据后,此时发送中断的中断信号触发,这个函数被执行。
2)测试代码
在 stm32u575xx_it.c 文件中重写函数:
//串口发送中断对应的回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit(&huart1,(uint8_t*)"nihao",sizeof("nihao"),1);
}
main.c 的 while(1) 中:
while(1)
{
HAL_UART_Transmit_IT(&huart1,(uint8_t*)"111",3);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
3)测试效果
图 28 串口发送中断运行结果
4)执行流程
自动保护现场(CPU 自动完成)
⇩
异常向量表对应异常源
DCD USART1_IRQHandler ; USART1 global interrupt
⇩
异常源的对应处理函数
void EXTI9_IRQHandler(void)
⇩
异常事件处理函数
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if (__HAL_GPIO_EXTI_GET_RISING_IT(GPIO_Pin) != 0U) { __HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_Pin); HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin); } if (__HAL_GPIO_EXTI_GET_FALLING_IT(GPIO_Pin) != 0U) // 判断下降沿中断信号是否触发 == 下降沿中断信号被挂起 // if( EXTI->FPR1 & (0x1 << 9) == 1) { __HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_Pin); // 清除下降沿中断挂起标志位 // EXTI->FPR1 |= (0x1 << 9); HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin); // 下降沿中断信号触发后,需要执行的逻辑代码的回调函数 } }
⇩
发送完成中断函数
static void UART_EndTransmit_IT(UART_HandleTypeDef *huart)
⇩
发送完成中断回调函数
__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
7.2 串口接收中断(经常使用)
串口接收中断实现 - 随时随地接收数据
1)API 接口
HAL_UART_Receive_IT
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:
HAL 库提供的用于开启串口接收中断,使用中断方式解说数据的函数。参数:
huart:USART1 句柄对象pData:需要接收的数据
Size:接收的数据字节大小,单位字节
返回值:
成功返回 HAL_OK失败返回错误码
HAL_UART_RxCpltCallback
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
功能:
HAL 库提供的用于串口接收中断触发后,执行的回调函数,可以重写;当串口通过中断的方式接收完完整数据后,此时接收中断信号触发,这个函数被执行。
参数:
huart:USART1 句柄对象返回值:
无
2)测试代码
在 main.c 中全局定义一个数据接收容器
uint8_t buf[5]={0}
在 main.c 中在开启串行接收中断
// 开启串口接收中断,并使用串口接收中断接收数据
HAL_UART_Receive_IT(&huart1, buf, sizeof(buf));
在 stm32u5xx_it.c 中重写 HAL_UART_RxCpltCallback 函数
// 串口接收中断对应的回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 回显接收到的数据
extern uint8_t buf[5];
HAL_UART_Transmit(&huart1, buf, sizeof(buf), 3);
memset(buf, 0, sizeof(buf));
// 再次开启串口接收中断,用于下一次数据接收
HAL_UART_Receive_IT(&huart1, buf, sizeof(buf));
}
/* USER CODE END 1 */
3)测试效果
图 29 串口接收中断运行效果
7.3 串口的空闲中断(IDLE)(经常使用)
串口空闲中断实现 随时随地接收不定长接收数据 - 串口的接收中断 + 串口的空闲中断
串口的空闲中断:接收数据时,数据长度是未知的,使用空闲中断可以实现不定长数据的接收。
1)USART_CR1 寄存器
图 30 USART_CR1
图 31 IDLEIE 空闲中断状态使能位
USART_CR1 寄存器的第 4 位是 IDLEIE 空闲中断状态使能位:
写入 0:不使能空闲中断状态标志位写入 1:使能空闲中断状态标志位 - USART_ISR 寄存器的 Bit 4 IDLE 位
2)USART_ISR 寄存器
图 32 USART_ISR
图 33 IDLE 空闲中断状态标志位
USART_ISR 寄存器的第 4 位是 IDLE 空闲中断状态标志位:
读到 0:空闲中断信号未产生 - 数据没有到达或没有接收完毕读到 1:空闲中断信号已产生 - 数据已经到达,并接收完毕
3)USART_ICR 寄存器
图 34 USART_ICR
图 35 IDLECF 清除空闲中断状态标志位
USART_ICR 寄存器的第 4 位 IDLECF 是清除空闲中断状态标志位:
写入 1:清除 USART_ISR 寄存器中的空闲中断状态标志位
4)空闲中断原理
图 36 空闲中断原理
5)API 接口
使用空闲中断标志位的步骤:
- 使能空闲中断状态标志位;
- 读取空闲中断状态标志位是否为 1 ;
- 如果是被置 1,先清除空闲中断状态标志位,方便下一次被硬件置 1 ;
- 执行中断后逻辑。
__HAL_UART_ENABLE_IT 中断 / 状态标志位使能宏函数
/** @brief Enable the specified UART interrupt. * @param __HANDLE__ specifies the UART Handle. * @param __INTERRUPT__ specifies the UART interrupt source to enable. * This parameter can be one of the following values: * @arg @ref UART_IT_RXFF RXFIFO Full interrupt * @arg @ref UART_IT_TXFE TXFIFO Empty interrupt * @arg @ref UART_IT_RXFT RXFIFO threshold interrupt * @arg @ref UART_IT_TXFT TXFIFO threshold interrupt * @arg @ref UART_IT_CM Character match interrupt * @arg @ref UART_IT_CTS CTS change interrupt * @arg @ref UART_IT_LBD LIN Break detection interrupt * @arg @ref UART_IT_TXE Transmit Data Register empty interrupt * @arg @ref UART_IT_TXFNF TX FIFO not full interrupt * @arg @ref UART_IT_TC Transmission complete interrupt * @arg @ref UART_IT_RXNE Receive Data register not empty interrupt * @arg @ref UART_IT_RXFNE RXFIFO not empty interrupt * @arg @ref UART_IT_RTO Receive Timeout interrupt * @arg @ref UART_IT_IDLE Idle line detection interrupt * @arg @ref UART_IT_PE Parity Error interrupt * @arg @ref UART_IT_ERR Error interrupt (frame error, noise error, overrun error) * @retval None */ #define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) (\ ((((uint8_t)(__INTERRUPT__)) >> 5U) == 1U)?\ ((__HANDLE__)->Instance->CR1 |= (1U <<\ ((__INTERRUPT__) & UART_IT_MASK))): \ ((((uint8_t)(__INTERRUPT__)) >> 5U) == 2U)?\ ((__HANDLE__)->Instance->CR2 |= (1U <<\ ((__INTERRUPT__) & UART_IT_MASK))): \ ((__HANDLE__)->Instance->CR3 |= (1U <<\ ((__INTERRUPT__) & UART_IT_MASK))))
功能:
HAL 库提供的用于使能中断 / 状态标志位的函数
参数:
HANDLE:USART1 的句柄对象
INTERRUPT:需要使能中断 / 状态标志位
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE) == USART1->CR1 |= (0x1<<4)
__HAL_UART_GET_FLAG(HANDLE, FLAG) 获取中断 / 状态标志位的值宏函数
/** @brief Check whether the specified UART flag is set or not. * @param __HANDLE__ specifies the UART Handle. * @param __FLAG__ specifies the flag to check. * This parameter can be one of the following values: * @arg @ref UART_FLAG_TXFT TXFIFO threshold flag * @arg @ref UART_FLAG_RXFT RXFIFO threshold flag * @arg @ref UART_FLAG_RXFF RXFIFO Full flag * @arg @ref UART_FLAG_TXFE TXFIFO Empty flag * @arg @ref UART_FLAG_REACK Receive enable acknowledge flag * @arg @ref UART_FLAG_TEACK Transmit enable acknowledge flag * @arg @ref UART_FLAG_RWU Receiver wake up flag (if the UART in mute mode) * @arg @ref UART_FLAG_SBKF Send Break flag * @arg @ref UART_FLAG_CMF Character match flag * @arg @ref UART_FLAG_BUSY Busy flag * @arg @ref UART_FLAG_ABRF Auto Baud rate detection flag * @arg @ref UART_FLAG_ABRE Auto Baud rate detection error flag * @arg @ref UART_FLAG_CTS CTS Change flag * @arg @ref UART_FLAG_LBDF LIN Break detection flag * @arg @ref UART_FLAG_TXE Transmit data register empty flag * @arg @ref UART_FLAG_TXFNF UART TXFIFO not full flag * @arg @ref UART_FLAG_TC Transmission Complete flag * @arg @ref UART_FLAG_RXNE Receive data register not empty flag * @arg @ref UART_FLAG_RXFNE UART RXFIFO not empty flag * @arg @ref UART_FLAG_RTOF Receiver Timeout flag * @arg @ref UART_FLAG_IDLE Idle Line detection flag * @arg @ref UART_FLAG_ORE Overrun Error flag * @arg @ref UART_FLAG_NE Noise Error flag * @arg @ref UART_FLAG_FE Framing Error flag * @arg @ref UART_FLAG_PE Parity Error flag * @retval The new state of __FLAG__ (TRUE or FALSE). */ #define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->ISR & (__FLAG__)) == (__FLAG__))
功能:
HAL库提供的用于获取中断 / 状态标志位对应值的函数
参数:
HANDLE:USART1 的句柄对象
FLAG:需要获取的中断 / 状态标志位
__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) == USART1->ISR & (0x1<<4)
__HAL_UART_CLEAR_FLAG 清除中断 / 状态标志位的宏函数
/** @brief Clear the specified UART pending flag. * @param __HANDLE__ specifies the UART Handle. * @param __FLAG__ specifies the flag to check. * This parameter can be any combination of the following values: * @arg @ref UART_CLEAR_PEF Parity Error Clear Flag * @arg @ref UART_CLEAR_FEF Framing Error Clear Flag * @arg @ref UART_CLEAR_NEF Noise detected Clear Flag * @arg @ref UART_CLEAR_OREF Overrun Error Clear Flag * @arg @ref UART_CLEAR_IDLEF IDLE line detected Clear Flag * @arg @ref UART_CLEAR_TXFECF TXFIFO empty clear Flag * @arg @ref UART_CLEAR_TCF Transmission Complete Clear Flag * @arg @ref UART_CLEAR_RTOF Receiver Timeout clear flag * @arg @ref UART_CLEAR_LBDF LIN Break Detection Clear Flag * @arg @ref UART_CLEAR_CTSF CTS Interrupt Clear Flag * @arg @ref UART_CLEAR_CMF Character Match Clear Flag * @retval None */ #define __HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->ICR = (__FLAG__))
功能:
HAL库提供的用于清除中断/状态标志位的函数
参数:
HANDLE:USART1的句柄对象
FLAG:需要清除的中断/状态标志位
__HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_IDLEF) == USART1->ICR |= (0x1<<4);
6)huart1 句柄对象提供的成员
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
RxXferCount 代表接收时的计数,接收 1 个字节,值会减 1
buf 初始为 1024 字节,则 RxXferCount = 1024
如果当前接收了 500 字节,则还能接收 1024 - RxXferCount = 524 个字节
pRxBuffPtr 是接收数据时的指针偏移
在接收完完整数据后,需要将 pRxBuffPtr 的指向重新指向 buf 的首地址
7)写法 1
stm32u5xxit.c
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
// 声明外部缓冲区,用于存储接收到的数据
extern uint8_t buf[1024];
// 用于记录接收数据的长度
int rx_len=0;
/* USER CODE END USART1_IRQn 0 */
// 调用HAL库的USART中断处理函数,处理底层中断逻辑
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
// 检查是否发生了IDLE空闲中断(数据帧接收完成)
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=0)
{
// 清除IDLE中断标志位
__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_IDLEF);
// 计算接收到的数据长度:缓冲区总大小 - 剩余未填充的字节数
rx_len=sizeof(buf)-huart1.RxXferCount;
// 将接收缓冲区指针重新指向缓冲区起始位置
huart1.pRxBuffPtr=buf;
// 将接收到的数据通过USART1发送回去(实现回显功能)
// 超时时间设置为5ms
HAL_UART_Transmit(&huart1,buf,rx_len,5);
// 清空接收缓冲区,为下一次接收做准备
memset(buf,0,1024);
// 重新开启USART1的中断接收模式,等待下一次数据
HAL_UART_Receive_IT(&huart1,buf,1024);
}
/* USER CODE END USART1_IRQn 1 */
}
uint8_t buf[1024];
//开启串口接收中断
HAL_UART_Receive_IT(&huart1,buf,sizeof(buf));
//开启串口空闲中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);