dma串口发送数据后一直显示HAL_UART_STATE_BUSY_TX

项目场景:

提示:这里简述项目相关背景:

为了实现车间通信新增了一个利用函数HAL_UART_Transmit_DMA进行串口DMA发送的代码 其中函数HAL_UART_Transmit_DMA用到的串口句柄UART_HandleTypeDef * huart 是类里面的成员 通过调用对象的初始化函数传递进去

问题描述

DMA发送函数只能被调用一次

在测试车间通信时,信息的接收是正常的 但发送信息只能发送一次,每次都是第一次发送成功, 后面的发送都没法被另一个机器人接收

状态位无法被重置

逐步debug DMA发送函数HAL_UART_Transmit_DMA 发现函数会对串口句柄的一个状态位gState进行判断 只有在huart->gState==HAL_UART_STATE_READY的时候 才会正常进行发送
而第一次发送可以成功,便是因为一开始gState为HAL_UART_STATE_READY,因此可以成功发送 而在第一次发送时,HAL_UART_Transmit_DMA函数会将gState更改为HAL_UART_STATE_BUSY_TX状态
gState位随后一直保持为HAL_UART_STATE_BUSY_TX状态,导致后面的发送无法执行

gState状态位的重置机制

为了搞明白gState一直保持不动的原因 我便翻阅代码搞明白了这个状态位的重置机制: 执行函数HAL_UART_Transmit_DMA时,gState被更改为HAL_UART_STATE_BUSY_TX状态 发送成功后,会触发串口中断,执行相应的串口中断执行函数USART1_IRQHandler,然后在函数再调用HAL_UART_IRQHandler 在HAL_UART_IRQHandler中,再调用UART_EndTransmit_IT函数,在此函数中标志位gState被重置为HAL_UART_STATE_READY,在此基础上,DMA发送函数HAL_UART_Transmit_DMA才能继续被执行

串口句柄完全迷失!

搞清楚状态位重置的机制后,我便重点在这个流程中逐步debug 随后发现了诡异的一幕

可见图中,句柄huart的地址,和右边所存在的6个串口的句柄都不一样 也就是说,在执行DMA发送函数HAL_UART_Transmit_DMA的串口,是一个根本不存在的"假串口"!

于是我层层回溯,发现作为类成员的串口句柄已经是这样的一个"假串口"了 所以问题毫无疑问在于对象的初始化函数之中

对形参取址 我将目光投向了初始化函数被调用的地方

void SentryChassis_Classdef::Init_SC(UART_HandleTypeDef referee_UART)
{
          
   
	/*裁判系统初始化*/
	referee_SC.Init(&referee_UART,Get_SystemTimer);
}

在Init_SC函数里,我发现传入的串口referee_UART的Instance(即串口的寄存器首地址)是正确的, 但对于串口referee_UART &取址后,所得到的便是一个的"假串口"了 这个时候问题已经很显然了

原因分析:

Init_SC函数被调用时需要传入参数referee_UART 但由于我既没有设置成传入指针,也没有设置成传入引用 导致传入的形参是一个临时被创建的变量 对这个变量进行取址操作,得到的指针自然不会指向任何一个实际的"串口" 也就是在这里"我创建了一个假串口"出来

而这个假串口被类成员记录了下来 由于第一次发送时gState状态位为HAL_UART_STATE_READY 所以可以执行DMA发送函数

又由于"假串口"的Instance是正确的 所以能通过这个串口发送出去,被另一个机器人接收到

但是Instance是正确的不能使其在发送成功后 通过中断处理函数重置gState状态位 所以后面的发送不再执行


解决方案:

显然,要保持现有的代码框架,最简单的方法便是 将传入一个形参 更改为 传入串口的引用
void SentryChassis_Classdef::Init_SC(UART_HandleTypeDef& referee_UART)
{
          
   
	/*裁判系统初始化*/
	referee_SC.Init(&referee_UART,Get_SystemTimer);
}

对引用取址,就相当于对串口的本身取址 因此就能得到正确的串口句柄 至此,问题解决!

经验分享 程序员 微信小程序 职场和发展