☰
【电路笔记 STM32】Cortex-M7 UART 中断模式(串口接收中断+串口空闲中断)
2026/10/13 6:59:49 网站建设 项目流程

文章目录

  • 轮询模式 (Polling)
  • 中断模式 (Interrupt)
      • CUBEMX 优先级配置
  • 中断实现数据包的接收(串口接收中断+串口空闲中断)
    • 核心思路
    • 代码实现(HAL库)
      • 定义全局变量
      • 串口初始化(使能RXNE + IDLE中断)
      • 中断服务函数
      • 主循环处理
  • CG

轮询模式 (Polling)

  • 轮询模式最大的优点是实现极其简单、执行路径完全可控,不需要配置 NVIC、中断优先级和 ISR,调试时单步跟踪也直观,适合在串口功能验证或简单裸机场景下快速跑通。
  • 但它的缺点也很明显:HAL_UART_Receive()在超时时间内会阻塞主循环,导致 CPU 无法及时处理其他任务,实时性差且 CPU 占用高;波特率越高或数据越密集,越容易因为主循环被卡住而漏收数据,甚至触发 ORE 溢出错误。因此它只适合低波特率、单任务或对实时性要求不高的场景,正式工程里如果后续还要跑多任务或处理高频通信,建议尽早切换其他模式。
/* main.c */#include"main.h"#include"usart.h"/* USER CODE BEGIN PV */volatileuint8_tusart3_rx_buffer[USART3_RX_BUFFER_SIZE];volatileuint16_tusart3_rx_count=0;volatileuint8_tusart3_rx_flag=0;/* USER CODE END PV */intmain(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 */HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART3_UART_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* USER CODE BEGIN WHILE */while(1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *//* ---- 轮询接收 USART3 数据 ---- */uint8_trx_byte=0;HAL_StatusTypeDef status=HAL_UART_Receive(&huart3,&rx_byte,1,100);if(status==HAL_OK){/* 收到一个字节 */if(rx_byte=='\r'||rx_byte=='\n'){/* 一帧结束 */usart3_rx_flag=1;}else{if(usart3_rx_count<USART3_RX_BUFFER_SIZE){usart3_rx_buffer[usart3_rx_count++]=rx_byte;}}}/* HAL_TIMEOUT 表示没有新数据,继续循环即可 *//* ---- 处理一帧数据 ---- */if(usart3_rx_flag){usart3_rx_flag=0;/* 这里解析 usart3_rx_buffer,长度是 usart3_rx_count *//* 例如:回显、协议解析等 */usart3_rx_count=0;/* 清空计数 */}/* 其他任务放在这里 *//* USER CODE END 3 */}}
  • HAL_UART_Receive的超时时间:这里用100毫秒,可以根据波特率调整。波特率越高、数据越密集,超时可以设短一点,减少主循环阻塞。

中断模式 (Interrupt)

  • STM32 的中断优先级由内核里的NVIC管理,本质是把每个中断的优先级拆成两部分:抢占优先级和子优先级。

    • 抢占优先级:数值越小优先级越高,决定能不能打断正在执行的中断。抢占优先级高的中断可以抢占抢占优先级低的中断,形成中断嵌套。
    • 子优先级:也叫响应优先级,数值越小越优先。但它只在抢占优先级相同时起作用,用来决定多个中断同时挂起时谁先执行;抢占优先级相同时,子优先级高的中断不能打断正在执行的同抢占优先级中断。
    • 如果抢占优先级和子优先级都相同,则按硬件固定的IRQ 编号决定,编号小的优先响应。
    • 中断嵌套:CPU 正在执行一个低抢占优先级的中断服务函数时,来了一个抢占优先级更高的中断,CPU 暂停当前中断,先去执行高优先级中断,执行完再回来继续原来的中断。在 STM32 里,嵌套只和 抢占优先级 有关,和 子优先级 无关。
  • STM32 使用优先级寄存器的高 4 位来配置优先级,这 4 位可以按 5 种分组方式划分:

分组抢占优先级位数子优先级位数
Group 004
Group 113
Group 222
Group 331
Group 440

CUBEMX 优先级配置

  • HAL 库中配置流程通常是:
HAL_NVIC_SetPriority(USART3_IRQn,1,0);HAL_NVIC_EnableIRQ(USART3_IRQn);
  • “4 bits for pre-emption priotiry”:即 4 bits 全部用于抢占优先级(相当于 Group 4 / 无子优先级划分)。

  • 实际工程中比较推荐的做法是:分组只设置一次,不要中途修改;抢占优先级留给真正紧急的中断,比如保护、高速控制、紧急故障;通信类中断如 USART、SPI、I2C 一般放在中低优先级;SysTick 通常放在较低优先级,避免它频繁打断业务中断。

  • [STM32 HAL库][中断]中断优先级,最佳教程,没有之一~

中断实现数据包的接收(串口接收中断+串口空闲中断)

核心思路

  • RXNE中断:每收到1个字节触发,将数据存入缓冲区并计数
  • IDLE中断:总线空闲超过1个字符时间后触发,标志一帧数据接收完毕
  • 当前纯中断方案适合波特率 ≤ 115200、帧长较短的场景,调试简单、逻辑清晰。
    • CPU占用较高:每字节都进中断,921600bps下约每11μs一次中断,不适合持续大数据流
    • 粘帧风险:如果两帧数据间隔小于1个字符时间,IDLE无法区分,会粘成一帧

代码实现(HAL库)

  • CubeMX 配置:NVIC 配置使能 USARTx 全局中断,抢占优先级建议设高(如1),避免被其他中断阻塞导致ORE错误

定义全局变量

/* 接收缓冲区与状态变量 */#defineUSART3_RX_BUFFER_SIZE512volatileuint8_tusart3_rx_buffer[USART3_RX_BUFFER_SIZE];// 接收缓冲区volatileuint16_tusart3_rx_count=0;// 当前已接收字节数volatileuint8_tusart3_rx_flag=0;// 一帧接收完成标志

串口初始化(使能RXNE + IDLE中断)

  • 在生成的MX_USART3_UART_Init()函数的/* USER CODE BEGIN USART3_Init 2 */区域中添加:
/* USER CODE BEGIN USART3_Init 2 */// 使能串口3的接收中断(RXNE)__HAL_UART_ENABLE_IT(&huart3,UART_IT_RXNE);// 使能串口3的空闲中断(IDLE)__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);/* USER CODE END USART3_Init 2 */
  • 整体代码如下:
void MX_USART3_UART_Init(void){/*USERCODE BEGIN USART3_Init0*/ /*USERCODE END USART3_Init0*/ /*USERCODE BEGIN USART3_Init1*/ /*USERCODE END USART3_Init1*/ huart3.Instance=USART3;huart3.Init.BaudRate=9600;huart3.Init.WordLength=UART_WORDLENGTH_8B;huart3.Init.StopBits=UART_STOPBITS_1;huart3.Init.Parity=UART_PARITY_NONE;huart3.Init.Mode=UART_MODE_TX_RX;huart3.Init.HwFlowCtl=UART_HWCONTROL_NONE;huart3.Init.OverSampling=UART_OVERSAMPLING_16;huart3.Init.OneBitSampling=UART_ONE_BIT_SAMPLE_DISABLE;huart3.Init.ClockPrescaler=UART_PRESCALER_DIV1;huart3.AdvancedInit.AdvFeatureInit=UART_ADVFEATURE_NO_INIT;if(HAL_UART_Init(&huart3)!=HAL_OK){Error_Handler();}if(HAL_UARTEx_SetTxFifoThreshold(&huart3, UART_TXFIFO_THRESHOLD_1_8)!=HAL_OK){Error_Handler();}if(HAL_UARTEx_SetRxFifoThreshold(&huart3, UART_RXFIFO_THRESHOLD_1_8)!=HAL_OK){Error_Handler();}if(HAL_UARTEx_DisableFifoMode(&huart3)!=HAL_OK){Error_Handler();}/*USERCODE BEGIN USART3_Init2*/ // 使能串口3的接收中断(RXNE) __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);// 使能串口3的空闲中断(IDLE) __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);/*USERCODE END USART3_Init2*/}
项目当前状态建议
FIFO 模式已禁用(DisableFifoMode)开启可用HAL_UARTEx_EnableFifoMode并设置合适的 FIFO 阈值(如 8/16),可显著减少中断次数(8 个字节才触发一次中断)
波特率9600
ORE 处理已在 ISR 中添加ORE是Overrun Error(溢出错误),必须保留,否则连续接收时一旦溢出会导致中断卡死

中断服务函数

  • 找到USART3_IRQHandler,修改为:
voidUSART3_IRQHandler(void){uint8_tres;/* ========== 接收中断(RXNE)========== */if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE)!=RESET){// 读取数据寄存器(读RDR会自动清除RXNE标志)res=(uint8_t)(huart3.Instance->RDR&0xFF);if(usart3_rx_count<USART3_RX_BUFFER_SIZE){usart3_rx_buffer[usart3_rx_count++]=res;}else{// 缓冲区溢出保护:重置计数,防止数组越界usart3_rx_count=0;}}/* ========== 空闲中断(IDLE)========== */if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE)!=RESET){// 清除IDLE标志(H7系列推荐写法)__HAL_UART_CLEAR_IDLEFLAG(&huart3);// 一帧数据接收完成if(usart3_rx_count>0){usart3_rx_flag=1;// 通知主循环处理}}/* ========== 错误处理(防止ORE导致卡死)========== */if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_ORE)!=RESET){__HAL_UART_CLEAR_FLAG(&huart3,UART_FLAG_ORE);}}

注意:H7系列清除IDLE标志推荐使用__HAL_UART_CLEAR_IDLEFLAG(),它内部已正确处理SR/DR读取序列。在 STM32F1/F4 这类老系列里,USART 没有 ICR 寄存器,IDLE 标志的清除方式是硬件规定的软件序列:老版本F1/F4中常用USART1->SR; USART1->DR;。

主循环处理

/* main.c 的 while(1) 中 */while(1){if(usart3_rx_flag==1){usart3_rx_flag=0;// 清除标志/* 此处处理接收到的数据 * 有效数据范围:usart3_rx_buffer[0] ~ usart3_rx_buffer[usart3_rx_count - 1] * 数据长度:usart3_rx_count */// 示例:将接收到的数据原样回发(用于调试验证)HAL_UART_Transmit(&huart3,(uint8_t*)usart3_rx_buffer,usart3_rx_count,1000);// 重置计数器,准备接收下一帧usart3_rx_count=0;}// 其他任务...}

CG

  • STM32入门——串口接收中断+串口空闲中断实现数据包的接收编程

  • 【电路笔记 STM32】stm32f103 实现HAL_UART_Transmit_DMA + 普通模式/连续模式的 UART DMA 收发

  • 串口中断里如果只自己读 RDR(UART 的接收数据寄存器),却没有处理 ORE overrun 错误(上一个字节还没被软件读走,新的字节又来了),也没有在清除 ORE 后再读一次 RDR,就可能丢字节或读到错误字节。

  • 开 RTS/CTS 需要硬件支持:MCU 和上位机都要接 RTS/CTS 引脚,并且 CubeMX 里 USART3 的硬件流控要配置成RTS/CTS。如果只有 TX/RX/GND 三根线,就没法用硬件流控,只能靠协议层控制发送节奏。

    • RTS:Request To Send,请求发送。
    • CTS:Clear To Send,允许发送。
    • 如果上位机是连续流,帧间几乎没有空闲,IDLE 可能不会及时触发,甚至可能把多帧拼成一帧;这时要么在上层按帧头/长度拆帧,要么考虑开启 RTS/CTS。
  • 硬件 IDLE 空闲中断时长不可配置,固定约为 1 个字符时间;

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询