问题
代码测试时发现设备进入hard_fault,通过call stack查看信息发现最后死在HAL_UART_Transmit函数。
解决
1. 调试发现huart->TxXferCount 被设置成0xFFFE,且运行不断减小,该值的含义为剩余发送字节数,看工程代码实际仅设置为1个字节,所以按照逻辑不应该出现这种情况。根据这个值初步判断应该为在中断中huart->TxXferCount 被设置为0,而返回线程模式后HAL库继续执行之前的操作,将会使huart->TxXferCount--;导致溢出为0xFFFF。接下来排查是哪个中断将这个值清零。
2. 通过仿真发现了调用关系如下
HAL_UART_IRQHandler --》 HAL_DMA_Abort_IT --》 hdma->XferAbortCallback(hdma) -》 UART_DMAAbortOnError --》 huart->TxXferCount = 0x00U; 当出错时,该变量会被清零,如果这时刚好在while循环内部,且huart->TxXferCount =1,那么在执行while循环体时huart->TxXferCount已经被置0,那么在退出循环体前进行了减1操作,导致值溢出,发送0xFFFF个字节(64KB),导致内存溢出。
while (huart->TxXferCount > 0U)
{
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if (pdata8bits == NULL)
{
huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU);
pdata16bits++;
}
else
{
huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU);
pdata8bits++;
}
huart->TxXferCount--;
}
3. 新问题引入:为什么会出现HAL_DMA_Abort_IT, 当接收数据出现错误时,如果USART_CR3_DMAR使能,会失能DMAR接收,并清除uart->TxXferCount,导致出现第1步的原因。
static void UART_DMAAbortOnError(DMA_HandleTypeDef *hdma)
{
UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
huart->RxXferCount = 0x00U;
huart->TxXferCount = 0x00U;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered error callback*/
huart->ErrorCallback(huart);
#else
/*Call legacy weak error callback*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
4. 为什么会出现数据错误呢?查看SR寄存器错误为FE(帧错误),实际串口不断在进入中断,而设备是未进行任何数据输入的,这里接收到的数据肯定是杂乱无序,不按照uart协议通信的,导致stm32检测到帧错误的问题。
排查:开发板串口接收引脚悬空,配置成上拉模式后正常。为保险起见,修改HAL库函数
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint8_t *pdata8bits;
uint16_t *pdata16bits;
uint32_t tickstart = 0U;
/* Check that a Tx process is not already ongoing */
if (huart->gState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* Init tickstart for timeout managment */
tickstart = HAL_GetTick();
huart->TxXferSize = Size;
huart->TxXferCount = Size;
/* In case of 9bits/No Parity transfer, pData needs to be handled as a uint16_t pointer */
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
pdata8bits = NULL;
pdata16bits = (uint16_t *) pData;
}
else
{
pdata8bits = pData;
pdata16bits = NULL;
}
/* Process Unlocked */
__HAL_UNLOCK(huart);
while (huart->TxXferCount > 0U)
{
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if (pdata8bits == NULL)
{
huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU);
pdata16bits++;
}
else
{
huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU);
pdata8bits++;
}
if(huart->TxXferCount > 0)
{
huart->TxXferCount--;
}
}
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
/* At end of Tx process, restore huart->gState to Ready */
huart->gState = HAL_UART_STATE_READY;
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
修改的地方如下,这样这个值就不会出现溢出变成65535,导致发送缓存区地址溢出:
if(huart->TxXferCount > 0)
{
huart->TxXferCount--;
}
版权声明:本文为CSDN博主「Hi,Mr.Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34672688/article/details/117478889
分享不易,点个赞再走吧☺☺☺