个人技术分享

/// @brief 串口发送一条格式化数据
void send_OneWeld_list_to_uart(uint16_t id)
{
  uint8_t char_data_uart[ONE_WEld_LIST_BYTE +2];

  char_data_uart[0] = 'M';//MCU 的数据
  char_data_uart[1] = 'I';//list
  memcpy(&char_data_uart[2], &OneWeld_Data_new, sizeof(OneWeld_Data_TypeDef));
  Send_Data_toLCD(char_data_uart, sizeof(char_data_uart),100);
}

/// @brief 通过DMA发送数据到LCD
/// @param pData 数据指针
/// @param Size 数据长度
/// @param timeout 超时时间
#pragma location = ".ccmram"
void Send_Data_toLCD(const uint8_t *pData, uint16_t Size,uint32_t timeout)
{
  uint32_t tickstart; tickstart = HAL_GetTick();

  while (huart1.gState != HAL_UART_STATE_READY)   //等待发送完成
  {
    if (((HAL_GetTick() - tickstart) > timeout) || (timeout == 0U))
      return;
    Refresh_ModbusData_and_IWDG();
  }

  HAL_UART_Transmit_DMA(&huart1, pData, Size); 
}

代码如上图,调用send_OneWeld_list_to_uart函数发送数据到LCD时,有几率出错!

原因是因为串口使用DMA发送,而给DMA的发送数据地址是一个临时变量,

在退出发送函数后,临时变量的地址被释放用于存储其他数据,

而串口发送速度是比较慢的(相较于CPU),这导致发送的数据有几率被修改。

解决方法:

  • 将临时变量char_data_uart变量改为全局变量或静态变量,避免内存被释放。
  • 或在send_OneWeld_list_to_uart函数内等待DMA发送完成后再进行后续的操作,避免退出函数后内存被释放!
  • 修改Send_Data_toLCD函数,根据数据量拷贝数据到静态或全局变量,DMA指向拷贝后的变量

教训:一定要注意DMA的问题,之前还遇到过调试断点导出数据总是出错,运行时则没问题的情况,是因为DMA等大部分外设都不会因为CPU核心的停止而停止工作,调试断点时,CPU停止了,但是DMA并没有停止,导致导出的内存数据是错的。