Función HAL_UART_Transmit_DMA que envía datos incorrectos

Estoy tratando de usar UART con DMA,

antes del DMA probé el método de sondeo con while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);función y pude comunicarme con éxito,

Cuando uso el

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

función, los datos no se envían correctamente, solo el primer byte correcto, otros bytes son incorrectos. ¿Por qué sucedió esto? Como puedó resolver esté problema ?

Esta es mi secuencia inicial:

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

Las siguientes líneas intentan enviar datos:

 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);

}

Estas son las funciones de inicialización:

..
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 */
      }

Esta es la función de interrupción en 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 */
}

EDITAR:

Probé este método pero no funcionó para mí.

  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;

}
Transmite con el modo sin bloqueo, el DMA se encarga de eso, luego obtiene una interrupción cuando finaliza la transmisión. Esperar a HAL_OK es incorrecto.
Agregué otro método que ya probé, verifique.
Qué tal esperar una transmisión completa. UART_DMATransmitCplt
while(dmaTransmitCompletedFlag); y no while(!dmaTransmitCompletedFlag);
@MarkoBuršič No entiendo lo siento. ¿Cómo puedo hacer eso? Mientras (dmaTransmitCompletedFlag) siempre es cierto, ¿cuál es el propósito?
Cuando trato de recibir con DMA, pude recibir todos los datos correctamente. ¿Por qué el DMA no se pudo transmitir correctamente?

Respuestas (2)

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

Estos dos deben ajustarse a la alineación de bytes. Está configurado en media palabra, por lo que hace DMA en 2 bytes, pero los registros UART solo tienen 1 byte, por lo que la mitad de los bytes caen en un agujero negro:

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. 

Solo se transmiten los bytes en negrita:

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

También puede habilitar el UART FIFO.

Acerca de este código:

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

Si el núcleo DMA está ocupado, HAL_UART_Transmit_DMA devolverá HAL_BUSY. Entonces el while(); esperará. Cuando el núcleo DMA esté disponible, iniciará la transferencia y devolverá HAL_OK. Tenga en cuenta que while() sale después de que se inicia la transferencia, pero no espera a que finalice la transferencia. La transferencia ocurrirá en segundo plano, que es el punto de DMA. Pero si toca algunos registros UART o usa HAL_UART_Transmit mientras se ejecuta el DMA, entonces podría interferir y eso explicaría sus problemas. Para saber si la transferencia ha terminado, utilice la interrupción. Y si usa una variable global, configúrela como volátil.

Muchas gracias, cuando actualizo configuraciones como esta hdma_usart2_tx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; Su trabajo para mí.
¡Noticias excelentes!

No puede volver a transmitir hasta que el mensaje anterior no se haya enviado por completo.

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;

}
Probé el siguiente código, pero no pude enviar los datos correctos nuevamente. No funciona para mi.
uint8_t rs485TxBuffer[17]={0x02,0x0A, 0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..} pero veo los datos como 02 00 00 00 0D 00 00 84 0A 02 04 en la línea.
cuando uso la función HAL_UART_Transmit en lugar de HAL_UART_Transmit_DMA, todo va muy bien. no entiendo porque?