Die HAL_UART_Transmit_DMA-Funktion sendet falsche Daten

Ich versuche, UART mit DMA zu verwenden,

Vor dem DMA habe ich die Polling-Methode mit while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);Funktion ausprobiert und konnte erfolgreich kommunizieren.

Wenn ich die verwende

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

Funktion, Daten werden nicht korrekt gesendet, nur das erste Byte ist korrekt, andere Bytes sind falsch. Warum ist das passiert? Wie kann ich dieses Problem lösen?

Dies ist meine Initialisierungssequenz:

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

Die folgenden Zeilen versuchen, Daten zu senden:

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

}

Dies sind die Initialisierungsfunktionen:

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

Dies ist die Interrupt-Funktion in 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 */
}

BEARBEITEN:

Ich habe diese Methode ausprobiert, aber sie hat bei mir nicht funktioniert.

  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;

}
Sie übertragen im nicht blockierenden Modus, der DMA kümmert sich darum, und Sie erhalten eine Unterbrechung, wenn die Übertragung beendet ist. Das Warten auf HAL_OK ist falsch.
Ich habe eine andere Methode hinzugefügt, die ich bereits ausprobiert habe, bitte überprüfen.
Was ist mit einer vollständigen Übertragung zu warten. UART_DMATransmitCplt
while(dmaTransmitCompletedFlag); und nicht while(!dmaTransmitCompletedFlag);
@MarkoBuršič Ich verstehe nicht, sorry. Wie kann ich das tun? während (dmaTransmitCompletedFlag) immer wahr ist, was ist der Zweck?
Wenn ich versuche mit DMA zu empfangen, konnte ich alle Daten korrekt empfangen. Warum konnte die DMA nicht korrekt übertragen werden?

Antworten (2)

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

Diese beiden sollten auf Byte-Alignment gesetzt werden. Es ist auf Halbwort eingestellt, also führt es DMA auf 2 Bytes aus, aber die UART-Register haben nur 1 Byte, sodass die Hälfte der Bytes in ein schwarzes Loch fällt:

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. 

Es werden nur die fettgedruckten Bytes übertragen:

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

Sie können auch den UART FIFO aktivieren.

Über diesen Code:

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

Wenn der DMA-Kern beschäftigt ist, gibt HAL_UART_Transmit_DMA HAL_BUSY zurück. Also das while(); Ich werde warten. Wenn der DMA-Kern verfügbar ist, initiiert er die Übertragung und gibt HAL_OK zurück. Beachten Sie, dass while() beendet wird, nachdem die Übertragung initiiert wurde, aber nicht auf das Ende der Übertragung wartet. Die Übertragung erfolgt im Hintergrund, was der Sinn von DMA ist. Aber wenn Sie einige UART-Register berühren oder HAL_UART_Transmit verwenden, während der DMA läuft, könnte es damit herumspielen, und das würde Ihre Probleme erklären. Verwenden Sie den Interrupt, um zu erfahren, ob die Übertragung abgeschlossen ist. Und wenn Sie eine globale Variable verwenden, setzen Sie sie auf flüchtig.

Vielen Dank, wenn ich die Konfiguration wie folgt aktualisiere hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; Seine Arbeit für mich.
Hervorragende Nachrichten!

Sie können nicht erneut senden, bis die vorherige Nachricht nicht vollständig gesendet wurde.

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;

}
Ich habe den folgenden Code ausprobiert, aber ich konnte keine korrekten Daten erneut senden. Es funktioniert nicht für mich.
uint8_t rs485TxBuffer[17]={0x02,0x0A, 0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..} aber ich sehe die Daten wie 02 00 00 00 0D 00 00 84 0A 02 04 auf der Leitung.
Wenn ich die Funktion HAL_UART_Transmit anstelle von HAL_UART_Transmit_DMA verwende, läuft alles großartig. Ich verstehe nicht warum?