Функция HAL_UART_Transmit_DMA отправляет неправильные данные

Я пытаюсь использовать UART с DMA,

перед прямым доступом к памяти я пробовал метод опроса с while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);функцией, и я мог успешно общаться,

Когда я использую

в то время как (HAL_UART_Transmit_DMA (& huart2, (uint8_t *) rs485TxBuffer, 17)! = HAL_OK);

функция, данные не отправляются правильно, только первый байт правильный, другие байты неверны. Почему это произошло? Как я могу решить эту проблему ?

Это моя последовательность инициализации:

  MX_DMA_Init();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();

Следующие строки пытаются отправить данные:

 while(1U)
  {

   // Communication Test Block //

    while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
        // while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);

}

Это инициализирующие функции:

..
else if(huart->Instance==USART2)
      {
      /* USER CODE BEGIN USART2_MspInit 0 */
    
      /* USER CODE END USART2_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_USART2_CLK_ENABLE();
    
        __HAL_RCC_GPIOD_CLK_ENABLE();
        /**USART2 GPIO Configuration
        PD5     ------> USART2_TX
        PD6     ------> USART2_RX
        */
        GPIO_InitStruct.Pin = USART2_RS485_TX_Pin|USART2_RS485_RX_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
        /* USART2 DMA Init */
        /* USART2_RX Init */
        hdma_usart2_rx.Instance = DMA1_Stream5;
        hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
        hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart2_rx.Init.Mode = DMA_NORMAL;
        hdma_usart2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(huart,hdmarx,hdma_usart2_rx);
    
        /* USART2_TX Init */
        hdma_usart2_tx.Instance = DMA1_Stream6;
        hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4;
        hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
        hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
        hdma_usart2_tx.Init.Mode = DMA_NORMAL;
        hdma_usart2_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(huart,hdmatx,hdma_usart2_tx);
    
        /* USART2 interrupt Init */
    
        HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
      /* USER CODE BEGIN USART2_MspInit 1 */
    
      /* USER CODE END USART2_MspInit 1 */
      }

Это функция прерывания в stm32f4xx_it.c.

void DMA1_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */

  /* USER CODE END DMA1_Stream5_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_rx);
  /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */

  /* USER CODE END DMA1_Stream5_IRQn 1 */
}

/**
  * @brief This function handles DMA1 stream6 global interrupt.
  */
void DMA1_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */

  /* USER CODE END DMA1_Stream6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_tx);
  /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */

  /* USER CODE END DMA1_Stream6_IRQn 1 */
}

/**
  * @brief This function handles USART2 global interrupt.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
// SerialPrint("Hi");
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

РЕДАКТИРОВАТЬ:

Я пробовал этот метод, но не работал для меня.

  while(1U)
  {

   // Communication Test Block //

    HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17);
    while(!dmaTransmitCompletedFlag);
    dmaTransmitCompletedFlag = 0;
   }

  ..
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{

  dmaTransmitCompletedFlag = 1;

}
Вы передаете в неблокирующем режиме, DMA позаботится об этом, затем вы получаете прерывание, когда передача завершена. Ожидание HAL_OK неправильно.
Я добавил другой метод, который уже пробовал, проверьте.
Насчет ждать полной передачи. UART_DMATransmitCplt
в то время как (dmaTransmitCompletedFlag); а не while(!dmaTransmitCompletedFlag);
@MarkoBuršič Я не понимаю, извините. Как я могу это сделать? Хотя (dmaTransmitCompletedFlag) всегда верно, какова цель?
Когда я пытаюсь получить с DMA, я могу получить все данные правильно. Почему DMA не может передавать правильно?

Ответы (2)

    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

Эти два должны быть установлены для выравнивания байтов. Он настроен на половину слова, поэтому он выполняет DMA для 2 байтов, но регистры UART имеют только 1 байт, поэтому половина байтов попадает в черную дыру:

uint8_t rs485TxBuffer[17]={0x02,0x0A,0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..} 
but I see the data like 02 00 00 00 0D 00 00 84 0A 02 04 on the line. 

Передаются только байты, выделенные жирным шрифтом:

0x02 , 0x0A, 0x00 , 0x01, 0x00 , 0x00, 0x00 , 0x01, 0x0D

Также вы можете включить UART FIFO.

Об этом коде:

while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);

Если ядро ​​DMA занято, HAL_UART_Transmit_DMA вернет HAL_BUSY. Итак, пока(); будет ждать. Когда ядро ​​DMA будет доступно, оно инициирует передачу и вернет HAL_OK. Обратите внимание, что while() завершает работу после инициации передачи, но не ожидает завершения передачи. Передача будет происходить в фоновом режиме, что и является точкой прямого доступа к памяти. Но если вы прикоснетесь к некоторым регистрам UART или используете HAL_UART_Transmit во время работы DMA, это может привести к путанице, и это объяснит ваши проблемы. Чтобы узнать, завершена ли передача, используйте прерывание. И если вы используете глобальную переменную, установите для нее значение volatile.

Большое спасибо, когда я обновляю конфигурацию следующим образом hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; Это работа для меня.
Отличные новости!

Вы не можете повторить передачу, пока предыдущее сообщение не будет полностью отправлено.

dmaTransmitCompletedFlag = 1;
while(1U)
  {

   // Communication Test Block //
    
    if dmaTransmitCompletedFlag 
    {
       dmaTransmitCompletedFlag = 0;
       HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17);
    }
  }

  ..
void UART_DMATransmitCplt(&huart2)
{

  dmaTransmitCompletedFlag = 1;

}
Я пробовал код ниже, но не смог снова отправить правильные данные. Это не работает для меня.
uint8_t rs485TxBuffer[17]={0x02,0x0A, 0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..} но я вижу в строке такие данные, как 02 00 00 00 0D 00 00 84 0A 02 04.
когда я использую функцию HAL_UART_Transmit вместо HAL_UART_Transmit_DMA, все идет отлично. Я не понимаю, почему?