逐步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 状态想要被清除必须要进入串口中断HAL_UART_IRQHandler中的UART_EndTransmit_IT里面,在UART_EndTransmit_IT函数中清除了gState标志位。
我的问题就是串口中断只能第一次进入,后面就不进入了,所以发送失败。在调试的时候偶尔能发送和接收都正常,各位能帮忙给点建议吗?下面是我的代码:
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART3)
{
if (HAL_UARTEx_ReceiveToIdle_DMA(&huart3, usart3RxBuff, MAX_COM3_RXSIZES) == HAL_BUSY) // 接收发生错误后重启
{
__HAL_UART_CLEAR_OREFLAG(&huart3);
huart3.RxState = HAL_UART_STATE_READY;
huart3.Lock = HAL_UNLOCKED;
HAL_UARTEx_ReceiveToIdle_DMA(&huart3, usart3RxBuff, MAX_COM3_RXSIZES);
}
initSqQueue(&uart[_COM3].Rx, usart3RxBuff, sizeof(usart3RxBuff));
memset(usart3RxBuff, 0, MAX_COM3_RXSIZES);
return;
}
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART3)
{
uart[_COM3].Rx.rear = Size; // 写入rear
// bleAppConfig.rxAct = 1;
#ifdef _UART3_DEBUG
uartxEcho(&huart3, &uart3);
#endif
if (uart[_COM3].rxEnd == true)
{
uart[_COM3].rxIdle = false;
initUartTick(&uart[_COM3], CONST_UART_DLY_TIM);
}
else
{
uart[_COM3].rxIdle = true;
initUartTick(&uart[_COM3], CONST_UART_DLY_TIM);
// MAP_Interrupt_disableSleepOnIsrExit();
}
return;
}
UNUSED(huart);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART3)
{
uart[_COM3].sBusy = false;
#ifdef MAX_COM3_TXSIZES
memset(usart3TxBuff, 0, MAX_COM3_TXSIZES);
#endif
return;
}
UNUSED(huart);
}
主程序开始调用函数HAL_UARTEx_ReceiveToIdle_DMA(&huart3, usart3RxBuff, MAX_COM3_RXSIZES); 发送调用的函数为HAL_UART_Transmit_DMA(huart, (const uint8_t *)buf, num);调试时buf和num都是正确的,发出去就没有数据。如果不使用DMA发送的话,那么接收的时候就会进入HAL_UART_ErrorCallback错误回调。 |
在STM32HAL库里定义很多状态变量,用于对各自外设模块的状态管理,比方ready,busy,idle,timeout,error等,
以便正确使用各个外设,避免使用上的混乱。
以usart为例,库里为USART的发送、接收操作分别定义了两个状态变量,即gState和RxState。其中gState表示USART的发送状态,RxState表述接收时可能的状态。
是ready还是busy或其它出错、超时状态。USART初始化后gState为reddy状态,当要调用UART发送函数时软件将其设置为Busy.
当usart完成发送或中止发送时,软件将gState设置回Ready。
如果说你调用的查询式发送函数,即HAL_UART_Transmit(),则在该函数里查询到数据发送完毕或发生超时都会将gState设置为Ready.
如果说你调用的是中断式发送函数,即HAL_UART_Transmit_IT(),同时你沿用CubeMx创建的中断服务程序代码框架,在相应的发送中断服务程序
里检测到发送完毕时,也会将gState修改为ready为下次发送做准备。 如果说你调用API函数是HAL_UART_Transmit_IT(),而中断服务程序是另外组织的,
要记得在发送完成时将gState改为Ready,不然下次调用HAL_UART_Transmit_IT()函数会因为gstate检查不通过而不能得以执行。
如果说你调用的是DMA方式发送函数,即HAL_UART_Transmit_DMA(),同样,也沿用CubeMx创建的中断服务程序代码框架,且开启了DMA完成中断和UART事件的中断,
在相应的DMA完成中断代码里也会适时地将gState从busy改为ready。如果说,你虽然调用了HAL_UART_Transmit_DMA()函数,但没有开启DMA完成中断,
也没有在适当的位置将gState改为ready,这时就会发生即使你多次调用HAL_UART_Transmit_DMA()函数而只会做一次发送的情形。因为在后续的
HAL_UART_Transmit_DMA()函数里做gState检查发现是busy而强行退出。当然,这时手动将其改为Ready也是可以的,但要保证合适的时间点。
比方,我就是不想开启DMA相关中断,只是想通过DMA方式发送几次字符串而已,像下面这样
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)String1, sizeof(String1));
HAL_Delay(200);
huart2.gState = HAL_UART_STATE_READY;
huart2.hdmatx->State = HAL_DMA_STATE_READY;
__HAL_UNLOCK(huart2.hdmatx);
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)String2, sizeof(String2));
HAL_Delay(200);
huart2.gState = HAL_UART_STATE_READY;
huart2.hdmatx->State = HAL_DMA_STATE_READY;
__HAL_UNLOCK(huart2.hdmatx);
当然,这里还有一种情况,尽管当我们基于中断或DMA方式调用UART发送,也开启了相关中断并沿用库函数的写法,还是可能出现只有第一次发送有效的
情况,比方像下面这样:
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)String1, sizeof(String1));
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)String2, sizeof(String2));
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)String3, sizeof(String3));
这里的三次调用太近了,第一次调用时软件将gstate改为busy,当运行第二个第三个调用时,第一次的uart发送可能根本没有完成,自然也就没法
基于它的完成中断将gstate改为ready,这样导致后续的两次调用都无效了。所以像这种情况,第二次调用前要确保上次传输完成了。
整体上,上面分析了两种可能的情形,请结合你的代码检查下,看看是哪种原因或是还是别的什么原因。
DMA发送函数只能第一次调用有效?
我后面全部改成了不用DMA的方式了,哈哈哈哈,然后后面想了想我可能知道是哪里的问题了,我是在循环着调用DMA的发送函数,也有发送完成中断的,但是并没有在发送完成中断中将gState改为ready,但是我从代码的执行过程里面看到,他应该不需要自己手动的去设置,因为只要能够进入串口的中断
HAL_UART_IRQHandler
,在这里面就会调用UART_EndRxTransfer
,这个函数就会将gState改为ready,我的问题可能就是没有判断发送是否完成就继续下一次发送了,因为时间问题,我并没有深入去查找,不用DMA现在看来是一切正常了,谢谢你的解释还有个问题我想请教你一下,就是我使用STM32L431进入STOP1模式,原始功耗为53uA。在进入停止模式之前,我调用HAL_ADC_DeInit(&hadc1)禁用ADC,并在退出STOP1模式后调用MX_ADC1_Init()重新启用ADC。测量的功耗为66uA,大约多出10uA。为什么呢?如果退出STOP1模式后不调用MX_ADC1_Init(),功耗仍为53uA。是因为ADC会产生功耗吗?应该不会的吧。
下面是我的进入和退出STOP模式的函数:
[md]https://shequ.stmicroelectronics.cn/thread-644680-1-1.html