STM32_HAL库编程_串口+DMA接收数据异常问题记录

2023-11-12

1.开发环境概述

	Keil - 5.27
	STM32CUBEMX
	STM32F103RCT6

2.问题现象

在利用RCT6做主机通信,C8T6做从机通信时。拔插从机电路板,有概率出现主机通信故障无法接收到从机数据,或者从机通信故障无法接收到主机数据的问题。两者开发均使用HAL库编程,串口数据接收方式为UART+DMA接收。

3.排查过程

利用LED灯检查每次数据过来时的串口中断触发状态,发现在拔插从机导致通信出错后,从机后续发出的数据均不能触发主机的串口接收中断。出现这种情况有两种可能,第一:主机死机了程序流程无法执行,第二:中断被屏蔽了,所以无法触发。先排除第一种情况,主板程序运转时有控制另一个LED灯定时翻转,在通信异常后,工作LED灯依旧正常翻转,由此可见主机并未死机。那么只剩第二种情况,可能在哪一步流程中,MCU把串口的接收中断给关闭了。

追入HAL_UART_IRQHandler()函数中,发现其有一段串口错误处理流程,当串口出错时。该函数会关闭串口接收状态UART_EndRxTransfer(huart);,并调用HAL_UART_ErrorCallback()回调函数给用户进行对应处理。这样一来问题就很清楚了,串口出错后,程序关闭了串口接收使能,所以后续从机所有的数据均不能触发主机的接收中断进行处理。并且程序将HAL_UART_ErrorCallback()函数而非HAL_UARTEx_RxEventCallback()函数,若没有在HAL_UARTEx_RxEventCallback()函数中写入对应的处理程序,那么就相当于少执行了一次HAL_UARTEx_RxEventCallback()函数。

解决办法

单独实现HAL_UART_ErrorCallback()函数,并在其中重新使能串口DMA接收即可。
在这里插入图片描述
中断处理流程

  1. 串口中断函数
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	  LED2_TOGGLE();
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
}
  1. 串口中断接收数据回调函数
/**
*串口1空闲中断回调函数
**/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if (huart->Instance == USART1) //串口1空闲中断
	{
		if (Size >= 7)
		{
			if(RxBuffer1[0] == Slave_Data[0].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[1].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[2].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[3].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[4].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[5].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[6].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[7].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[8].Slave_Address	||
				 RxBuffer1[0] == Slave_Data[9].Slave_Address	||	RxBuffer1[0] == Broadcast_Modbus_Addr||RxBuffer1[0] == BAK_Modbus_Addr)	//判断地址是否符合
				{
					modbus1_ok = TRUE;		//接收标志置位
					modbus1_RxNum = Size;	//保存数据长度
				}
		}
		
//		Start_Reback_Dma = 0;					//这两个是用来打印调试信息的
//		ReStart_Dma_Switch = FALSE;
		
		uint8_t Ret = 0;
		Ret = HAL_UARTEx_ReceiveToIdle_DMA(&huart1,RxBuffer1,RBufferSize); //使用串口IDLE中断接收数据
		if (Ret != HAL_OK)
		{
			char Buff[20] = {0};
			sprintf(Buff,"ERROR:%d\r\n",Ret);
			HAL_UART_Transmit(&huart2,(uint8_t *)Buff,sizeof(Buff),0xFFFF); //输出信息提示
		}
//		LED2_TOGGLE();	//收到一包数据,翻转一次LED灯
	}
	
	else if (huart->Instance == USART3)	//串口3空闲中断
	{
		if (!memcmp(RxBuffer3,"AT+Shiled_Slave:{",17))/*屏蔽从机指令*/
		{
			UI_Command = Command_1;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("\r\nRecv AT+Shiled_Slave OK\r\n");											//回应主机
			HAL_UART_Transmit(&huart2,(uint8_t *)"Recv AT+Shiled_Slave OK\r\n",25,0xffff);
		}
		
		else if (!memcmp(RxBuffer3,"AT+SelfInspection:{",19))/*通信自检指令*/
		{
			UI_Command = Command_2;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("Recv AT+SelfInspection OK\r\n");										//回应主机
			HAL_UART_Transmit(&huart2,(uint8_t *)"\r\nRecv AT+SelfInspection OK\r\n",29,0xffff);
		}
		
		else if(!memcmp(RxBuffer3,"AT+Reset_Slave:{",16))/*复位从机指令*/
		{
			UI_Command = Command_3;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("Recv AT+Reset_Slave OK\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)"\r\nRecv AT+Reset_Slave OK\r\n",26,0xffff);
		}
		
		else if (!memcmp(RxBuffer3,"AT+Slave_Config:{",17))/*配置主板通信指令*/
		{
			UI_Command = Command_4;																			//命令标号定义
			UI_Cmd_IsArrive = TRUE;																			//命令接收标志置位
			memcpy(UI_Cmd_Recevice,RxBuffer3,Size);											//内存中缓存命令
			UICmd_Recevice_Counter = Size;															//保存接收的字节数
			
			printf("Recv AT+Slave_Config OK\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)"\r\nRecv AT+Slave_Config OK\r\n",27,0xffff);
		}
		
		HAL_UARTEx_ReceiveToIdle_DMA(&huart3,RxBuffer3,RBufferSize); //使用串口IDLE中断接收数据
	}
}

  1. 串口错误回调函数
/**
*串口错误回调函数
**/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
		if (huart->Instance == USART1)
		{
			char Buff[20] = {0};
			snprintf(Buff,20,"USART ERROR:\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)Buff,strlen(Buff),0xFFFF); //输出信息提示
			
			uint8_t Ret,St;
			St = (&huart1)->RxState;
			Ret = HAL_UARTEx_ReceiveToIdle_DMA(&huart1,RxBuffer1,RBufferSize); //重设,使用串口IDLE中断接收数据
			
			if (Ret != HAL_OK)
			{
				memset(Buff,0,20);
				snprintf(Buff,20,"ERROR:%d,RXs = %d\r\n",Ret,St);
				HAL_UART_Transmit(&huart2,(uint8_t *)Buff,sizeof(Buff),0xFFFF); //输出信息提示
			}
			else
				HAL_UART_Transmit(&huart2,(uint8_t *)"DMA START OK\r\n",sizeof("DMA START OK\r\n"),0xFFFF); //输出信息提示
		}
		
		else if (huart->Instance == USART3)
		{
			char Buff[20] = {0};
			snprintf(Buff,20,"USART ERROR:\r\n");
			HAL_UART_Transmit(&huart2,(uint8_t *)Buff,strlen(Buff),0xFFFF); //输出信息提示
			
			uint8_t Ret,St;
			St = (&huart3)->RxState;
			Ret = HAL_UARTEx_ReceiveToIdle_DMA(&huart3,RxBuffer3,RBufferSize); //重设,使用串口IDLE中断接收数据
			
			if (Ret != HAL_OK)
			{
				memset(Buff,0,20);
				snprintf(Buff,20,"ERROR:%d,RXs = %d\r\n",Ret,St);
				HAL_UART_Transmit(&huart2,(uint8_t *)Buff,sizeof(Buff),0xFFFF); //输出信息提示
			}
			else
				HAL_UART_Transmit(&huart2,(uint8_t *)"DMA START OK\r\n",sizeof("DMA START OK\r\n"),0xFFFF); //输出信息提示
		}
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32_HAL库编程_串口+DMA接收数据异常问题记录 的相关文章

  • C 嵌入式应用程序中 time() 函数的问题

    我在用time 在 ARM 微控制器上 处理器一到达此函数就会重新启动 奇怪的是 当我处于调试模式时 代码运行得很好 但一旦我想将其应用到独立模式 我就会遇到重置 我是否忽略了什么 这个功能有替代品吗 代码部分是这样的 include
  • 源和目标具有不同的 EABI 版本

    我正在尝试使用 ARM 工具链编译 so 文件 但是我不断收到这个错误 错误 源对象的 EABI 版本为 0 但目标对象的 EABI 版本为 5 我无法更改工具链中的任何内容 因为我必须使用给定的工具链 我以前从未见过这个错误 我使用了这个
  • 为 ARM 交叉编译 zlib

    我尝试为arm poky linux gnueabi交叉编译zlib 但启动 make 时出现错误 zlib 1 2 11 AR HOST ar CC HOST gcc RANLIB HOST ranlib configure prefix
  • M1 MacBook Pro 上的 Android Studio 无法使用 ABI armeabi-v7a 模拟系统映像

    我的 M1 Macbook Pro 上的 Android Studio 可以很好地模拟 ABI arm64 v8a 的所有系统映像 API 24 29 30 31 但是 它无法使用 ABI armeabi v7a 运行所有映像 例如 API
  • 产生并处理软件中断

    有人可以告诉我如何在Linux下生成软件中断然后用request irq处理它吗 或者也许这是不可能的 您可以使用软中断来代替 您可以通过编辑 include linux interrupt h 来定义您的 sofirq 然后使用函数 ra
  • 使用 Android NDK 使用 -fsigned-char 进行构建安全吗?

    为了与其他平台保持一致 我需要使用signed char在我正在处理的一些本机代码中 但默认情况下在Android NDK上char类型是unsigned 我尝试明确使用signed char类型 但它生成太多警告differ in sig
  • 了解 ctags 文件格式

    我使用 Exhuberant ctags 来索引我的 c 项目中的所有标签 c project 是 Cortex M7 微控制器的嵌入式软件 结果是一个标签文件 我正在尝试阅读该文件并理解所写的内容 根据我找到的 ctags 和 Exhub
  • 基于 Windows 8 ARM 的平板电脑上的 VB6

    随着 Windows 8 将支持 VB6 我的问题是 Microsoft 是否在任何地方表示 是或否 VB6 应用程序将在基于 ARM 的平板电脑上运行 如果没有 是否有任何 ARM 模拟器 以便我们可以在 Windows 8 ARM 平板
  • 了解 U-Boot 内存占用

    我不明白加载 U Boot 时 RAM 中发生了什么 我正在开发 Xilinx Zynq ZC702 评估套件 并尝试使用 U Boot 在其上加载 Linux 内核 于是我使用Xilinx工具Vivado和SDK生成了一个BOOT bin
  • AOSP 的“午餐”组合是什么意思?我需要选择什么?

    我是 Android 设备 ROM 开发的新手 无论如何 我现在正在为具有 64 位处理器的中国设备构建 AOSP 我按照 source android com 上的菜单进行操作 当我运行 午餐 命令时 终端显示 午餐菜单 选择一个组合 我
  • Qemu flash 启动不起作用

    我有一本相当旧的 2009 年出版 嵌入式 ARM Linux 书 其中使用u boot and qemu 的用法qemu与u boot书中对二进制的解释如下 qemu system arm M connex pflash u boot b
  • 架构armv7的重复符号

    尝试在我现有的应用程序中使用 Layar SDK 时出现以下错误 我该如何解决这个问题 Ld Users pnawale Library Developer Xcode DerivedData hub afxxzaqisdfliwbzxbi
  • 如何获取结构体中任意成员的位位置

    如何获取结构体中任意成员的位位置 在示例中 gt typedef struct BitExamStruct unsigned int v1 3 unsigned int v2 4 unsigned int v3 5 unsigned int
  • GCC:如何在 MCU 上完全禁用堆使用?

    我有一个在基于 ARM Cortex M 的 MCU 上运行并用 C 和 C 编写的应用程序 我用gcc and g 编译它并希望完全禁用任何堆使用 在 MCU 启动文件中 堆大小已设置为 0 除此之外 我还想禁止代码中意外使用堆 换句话说
  • 错误:-march= 开关的值错误

    我写了一个Makefile 但无法让它工作 我有一个选项应该选择编译到哪个处理器 然而 当我跑步时make从命令行它说 tandex tandex P 6860FX emulators nintendo sdks 3DS SDK HomeB
  • GCC 变量映射和 MISRA-C

    我主要知道两种使用 GCC 声明内存映射寄存器的方法 有许多变体 使用双字段 每个外设的数据结构等 要么使用初始化为正确地址的指针 例如volatile uint32 t pMyRegister uint32 t 0xDEADBEEFUL
  • 当我尝试在 Armv8 程序集中分配数组时,执行冻结

    所以我正在用汇编语言进行编程 这只是一个简单的代码 这样我就可以学习如何分配数组 以便稍后在 NEON 编程中使用它们 ASM FUNC FPE data balign 8 array skip 80 array1 word 10 20 3
  • 在LPC2148 ARM处理器上创建中断向量的汇编代码

    我最近刚刚开始使用 LPC2148 ARM 处理器 我试图理解一些有关创建中断向量的汇编代码 这是代码 Runtime Interrupt Vectors Vectors b start reset start ldr pc undf un
  • 支持 ARM 上的 Windows 10 桌面应用程序 - MFC 和 COM 以及 OPOS 可以工作吗?

    我试图了解将在 x86 Windows 10 上运行的 C MFC 应用程序移植到具有 Qualcomm Snapdragon 处理器的 ARM Windows 10 设备的障碍 32位应用程序具有以下特点 MFC 与 C 用于用户界面 C
  • STM32F4 定时器 - 计算周期和预分频,以生成 1 ms 延迟

    我在用STM32F407VGT6 with CubeMX 因此 我从通用定时器开始 但我被预分频值和周期值所困扰 基本上我想每隔一段时间生成一个定时器中断n 其中 n 1 2 3 ms 并执行一些任务 计算周期和预分频值的公式有很多变化 公

随机推荐

  • Kibana常用命令

    Kibana常用命令 查看集群的健康情况 GET cat health v 查看节点的情况 GET cat nodes v 查询各个索引状态 GET cat indices v 创建索引 PUT movie index 查看某一个索引的分片
  • 9月17日星期二 恒指/美原油/美黄金 走势分析

    财经早餐 2019年09月17日星期二 重点关注的财经数据与事件 09 30 中国70个大中城市住宅销售价格月报 09 30 澳洲联储公布9月货币政策会议纪要 17 00 德国及欧元区9月ZEW经济景气指数 21 15 美国8月工业产出月率
  • idea删除模块后重新创建显示该模块已经被注册

    idea删除模块后重新创建显示该模块已经被注册 原因 注册信息没有删除干净 解决方案 找到gradle xml modules xml workspace xml文件 进一步删除模块信息
  • odoo 打开form 视图 默认为编辑状态

    为了编辑方便 客户会经常要求 odoo 打开form 视图 默认为编辑状态 或者根据某些条件 为编辑状态 下面介绍两种方式进行 1 在form 初始化加载时进行拦截 设置属性打开编辑状态 FormView include 满足条件 form
  • springboot的yaml写法之map、list和map<String,List>

    分两步 设置yaml 注入参数 1 设置yaml 文件名 application yml runcommand firstMapList key11 k1 k2 k3 key22 k1 k2 k3 list lk1 lk2 lk3 maps
  • java 编译时注解框架 lombok-ex

    lombok ex lombok ex 是一款类似于 lombok 的编译时注解框架 编译时注 拥有运行时注解的便利性 和无任何损失的性能 主要补充一些 lombok 没有实现 且自己会用到的常见工具 创作目的 补充 lombok 缺失的注
  • nginx输出php错误日志,Nginx错误日志(error_log)配置及信息详解

    Nginx状态信息 status 配置及信息详解 nginx与php fpm一样内建了一个状态页 对于想了解nginx的状态以及监控nginx非常有帮助 为了后续的zabbix监控 我们需要先了解一下nginx的状态页 Nginx状态信息
  • 【Matplotlib】【Python】如何使用matplotlib颜色映射

    颜色映射 colormap 是一系列颜色 它们从起始颜色渐变到结束颜色 在可视化中 颜色映射用于突出数据的规律 例如 你可能用较浅的颜色来显示较小的值 并使用较深的颜色来显示较大的值 模块pyplot内置了一组颜色映射 要使用这些颜色映射
  • 学习UpdatePanel控件-

    原文可以显示图片 转载 http blog csdn net ILOVEMSDN archive 2007 11 11 1879343 aspx UpdatePanel控件的使用 2008 10 07 05 46 P M ScriptMan
  • JVM 二. 类加载相关

    目录 一 类的加载 二 类的加载器 类加载器的双亲委派机制 一 类的加载 什么是类的加载 我们编写java代码存储为 java结尾的文件 经过编译器编译 将java代码转换为虚拟机指令生成 class结尾的文件 当需要某个类时 虚拟机加载指
  • ZCMU--5155: 小蒜数(C语言)

    题目描述 在所有不大于 n 的正整数中 蒜头君将不是 9 的倍数的所有奇数剔除掉 将是 7 的倍数的所有偶数剔除掉 剩下的数从小到大首尾相接拼起来 组成了一个 小蒜数 请问这个数一共有多少位 输入 输入为整数 n 1 n 1000 测试点编
  • swiper中使用iframe导致无法滑动的3个解决方案

    看到这个标题 很多同学都会疑惑 为什么swiper中要放iframe呢 事实上 当我遇到这个需求前 我也没想到会有这样的骚操作 swiper中嵌入网站 每次滑动切换一个网站 想想听炫酷的 可做起来就不酷了 当你开开心心的把iframe放到每
  • 音频格式RAW和PCM区别和联系

    定义 RAW 在一些外国品牌的播放机中名为 BitSream 我们通常称为 源码 意义是把光盘上的音频格式不加处理地 原汁原味 地从同轴和光纤输出 这就要求用户的功放具备这种音频格式的解码功能 PCM 名为 脉冲调制编码 它的作用是当前的将
  • 多益校招面经--软件开发岗

    多益网络2021校招面经 软件开发岗 笔试通过 专业面试凉凉 第一次面试 太紧张了 很多东西提起来脑子一片空白 现在结束后想了一下都能想明白 以下是面经 1 个人介绍 2 项目介绍 3 开发语言的了解程度 个人是C 4 C 和JAVA的区别
  • 通过接口传递经纬度,并计算距离

    微信小程序调用接口 存储经纬度 PostMapping update location public R updateLocation RequestBody UserQuery userQuery if userService getLo
  • MDK5__配色方案的修改

    一 必要的知识 与MDK主题相关的文件有两个 在X Keil v5 UV4路径下 global propglobal prop def其中global prop def是系统默认的主题配置 如果修改过字体等 系统会生成一个global pr
  • Qt元对象系统及应用(一)

    目录 1 元对象系统 2 元对象与属性系统 2 1 动态属性 3 元对象与信号槽 3 1 信号槽应用场景 3 2 信号槽的连接 3 3 信号槽要点总结 1 元对象系统 元对象系统 Meta Object System 是Qt框架中一个非常重
  • python学习目录,从入门到上手

    这是我学习python的一套流程 根据书本学习 从入门到上手 整理不易 一 Python入门 环境搭建 变量 数据类型 二 Python运算符 条件结构 循环结构 三 Python函数 四 做一次综合练习 做一个控制台的员工管理 需求 员工
  • WMS仓库管理系统与ERP仓储系统的区别与联系

    现代企业越来越重视物流及仓储管理 然而提到WMS仓库管理系统与ERP仓储系统 许多企业管理者依旧是一脸茫然的表情 无法清晰的区别分辨 其实这两款软件在功能上有相似的地方 下面就由沈阳达策带您一看究竟 实际上 ERP仓储系统和WMS仓库管理系
  • STM32_HAL库编程_串口+DMA接收数据异常问题记录

    1 开发环境概述 Keil 5 27 STM32CUBEMX STM32F103RCT6 2 问题现象 在利用RCT6做主机通信 C8T6做从机通信时 拔插从机电路板 有概率出现主机通信故障无法接收到从机数据 或者从机通信故障无法接收到主机