[STM32 HAL库]串口中断编程思路

news/2025/1/20 12:48:52/

一、前言

最近在准备蓝桥杯比赛(嵌入式赛道),研究了以下串口空闲中断+DMA接收不定长的数据,感觉这个方法的接收效率很高,十分好用。方法配置都成功了,但是有一个点需要进行考虑,就是一般我们需要对串口接收的数据进行处理,这个数据处理是在中断的回调函数里面处理还是在主函数里面处理好呢?以下就这两个方法进行分析:

二、方法分析

目前我想到的有两种方法:

方法一

在回调函数里直接处理数据

优点:

  • 实时性强:数据接收完成后立即处理,减少了数据处理的延迟。
  • 代码简洁:数据接收和处理逻辑在同一个地方,代码易于理解和维护。

缺点:

  • 占用中断处理时间:如果数据处理逻辑复杂或耗时,会影响中断的响应速度,进而影响系统其他功能的实时性。
  • 可维护性差:如果数据处理逻辑复杂,中断处理函数会变得冗长,难以维护。

方法二

在回调函数中设置标志位,在主函数里读取标志位再进行数据处理

优点:

  • 保护中断响应速度:中断处理函数只负责设置标志位,数据处理在主循环中进行,保证了中断的响应速度。
  • 代码结构清晰:中断处理函数和数据处理逻辑分离,代码结构更清晰,易于维护和扩展。
  • 资源利用率高:可以在主循环中根据系统状态灵活调度数据处理,避免在中断中处理复杂逻辑造成的资源浪费。

缺点:

  • 增加了一定的复杂性:需要额外管理标志位,以及同步数据接收和处理的逻辑。
  • 可能引入延迟:数据处理被推迟到主循环中进行,可能会引入一定的处理延迟。

总结

  • 数据处理的复杂度:如果数据处理逻辑复杂或耗时,建议采用方法二,以保护中断响应速度。
  • 系统的实时性要求:如果系统对实时性要求较高,且数据处理不是非常耗时,方法一可能更合适。但如果数据处理可能影响到系统的其他实时功能,方法二则更为稳妥。
  • 代码的可维护性和扩展性:如果希望代码结构更清晰,易于维护和扩展,方法二通常是更好的选择。

三、实际操作

配置的方法可以看之前写的文章
链接: [STM32 HAL库]串口空闲中断+DMA接收不定长数据

实验现象:将电脑发来的数据,原封不到的发送回去。特别注意BUFF_SIZE的大小,太小会造成接收数据的丢失。

方法一

在这个方法中,在中断的回调函数里直接发送回去数据,并手动开启下一次的中断。

#define BUFF_SIZE	128
uint8_t rx_buffer[BUFF_SIZE];  // 创建接收缓存,大小为BUF_SIZE
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);//手动开启串口DMA模式接收数据__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);	//手动关闭DMA_IT_HT中断	while (1){}
}
void SystemClock_Config(void)
{//...
}
/* 串口接收完成回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){HAL_UART_Transmit(&huart1, rx_buffer, Size, 0xffff);// 将接收到的数据再发出HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完毕后重启串口DMA模式接收数据__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);// 手动关闭DMA_IT_HT中断memset(rx_buffer, 0, BUFF_SIZE);// 清除接收缓存	}
}
/* 串口错误回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{if(huart->Instance == USART1){HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE); // 接收完毕后重启串口DMA模式接收数据__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);// 手动关闭DMA_IT_HT中断memset(rx_buffer, 0, BUFF_SIZE);// 清除接收缓存}
}

方法二

在这个方法中,在串口接收完成的回调函数置接收完成的标志位,然后在主函数中进行判断。判断成立则进行数据的发送,并手动开启下一次的中断和清除标志位。

需要注意的是,不要在回调函数里面手动开启下一次的中断,因为有可能会出现主函数数据还未处理完成,下一个串口数据就到来而覆盖上一次的串口数据。
所以,这里程序的处理方法是:程序处理完本次数据,则开启下一次中断接收;程序未处理完本次数据,则不开启下一次中断接收

#define BUFF_SIZE	128
uint8_t rx_buffer[BUFF_SIZE];  	// 创建接收缓存,大小为BUF_SIZE
_Bool 	u1_rx_end_flag = 0;		//USART1接收数据完成标志位 1:接收完成
uint16_t u1_rx_size;			//USART1接收数据实际长度
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);//手动开启串口DMA模式接收数据__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);		   	//手动关闭DMA_IT_HT中断	while (1){/* 判断接收是否完成 */if(u1_rx_end_flag == 1){/* 对接收的数据进行处理 */HAL_UART_Transmit(&huart1, rx_buffer, u1_rx_size, 0xffff);// 将接收到的数据再发出memset(rx_buffer, 0, BUFF_SIZE);	// 清除接收缓存/* 开启下一次中断 */HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rx_buffer,BUFF_SIZE);//手动开启串口DMA模式接收数据__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);//手动关闭DMA_IT_HT中断	/* 清除标志位 */u1_rx_end_flag = 0;}}
void SystemClock_Config(void)
{//...
}
/* 串口接收完成回调函数 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART1){u1_rx_end_flag = 1;	//置标志位u1_rx_size = Size;		//获取接收数据长度}
}	
/* 串口错误回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef * huart)
{if(huart->Instance == USART1){HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, BUFF_SIZE);//手动开启串口DMA模式接收数据__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);// 手动关闭DMA_IT_HT中断memset(rx_buffer, 0, BUFF_SIZE);// 清除接收缓存}
}

值得一提的是,若是没有手动开启串口空闲中断,那么串口错误中断也不会被开启,也就无法进入串口错误回调函数

四、实验现象

两个方法实现现象一致
在这里插入图片描述

应该还有更好的串口接收模式,现在来说,这个方法应该够用了。


http://www.ppmy.cn/news/1564662.html

相关文章

WEB渗透技术研究与安全防御

目录 作品简介I IntroductionII 1 网络面临的主要威胁1 1.1 技术安全1 2 分析Web渗透技术2 2.1 Web渗透技术的概念2 2.2 Web漏洞产生的原因2 2.3 注入测试3 2.3.1 注入测试的攻击流程3 2.3.2 进行一次完整的Sql注入测试4 2.3.3 Cookie注入攻击11 3 安全防御方案设计…

MySQL程序之:使用命令选项连接到服务器

本节介绍如何使用命令行选项来指定如何为mysql或mysqldump等客户端建立到MySQL服务器的连接。有关使用类似URI的连接字符串或键值对建立连接的信息,请参阅“使用类似URI的字符串或键值对连接到服务器”。有关无法连接的其他信息,请参阅“解决连接到MySQL…

设置完端口转发后,本机可以ping通公网设备,但公网设备无法ping通本机内网ip

设置端口转发后,本机可以ping通公网设备,但公网设备无法ping通本机内网IP,通常与以下原因有关: 1. 端口转发仅针对特定端口 端口转发的作用:端口转发仅将特定端口的流量(如TCP/UDP)从公网IP转发…

Redis的安装和配置、基本命令

一、实验目的 本实验旨在帮助学生熟悉Redis的安装、配置和基本使用,包括启动Redis服务、使用命令行客户端进行操作、配置Redis、进行多数据库操作以及掌握键值相关和服务器相关的命令。 二、实验环境准备 1. JAVA环境准备:确保Java Development Kit …

今天你学C++了吗?——C++中的STL

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

基于当前最前沿的前端(Vue3 + Vite + Antdv)和后台(Spring boot)实现的低代码开发平台

项目是一个基于当前最前沿的前端技术栈(Vue3 Vite Ant Design Vue,简称Antdv)和后台技术栈(Spring Boot)实现的低代码开发平台。以下是对该项目的详细介绍: 一、项目概述 项目名称:lowcode-s…

基于海思soc的智能产品开发(高、中、低soc、以及和fpga的搭配)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 市场上关于图像、音频的soc其实非常多,这里面有高、中、低档,开发方式也不相同。之所以会这样,有价格的因素&am…

使用numpy求解线性代数相关问题

在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组类型相乘是逐元素相乘,而矩阵类型相乘则是矩阵乘法。 以下使用numpy.array类型来进行线性代数问题求解。 矩阵的转置: A.T import numpy as n…