Electronic – STM32 HAL UART Transmit DMA problem

dmahal-librarymicrocontrollerstm32uart

After setting up my project for a custom STM32F7 board which includes a FT2232H UART<->USB converter I got multiple problems when sending (and receiving data). The code I use is mostly generated by CubeMX and is at the end of the post.

First of all I can't get the stm to transmit at baud rates higher then the standard 115200, according to the datasheets both the FT2232H and the STM32F7 should be capable of at least 12M baud. For the FT2232H it's working as I send some characters from my terminal (USB side) and got the char back when I shorted the RX and TX pins on the FT2232H output side.

Second is that I can't call the sendUART() function multiple times in a row, why isn't the DMA Fifo used to store the stuff I want to send?

Also whats the right way to echo back all received data but make use of the fifo so no data is lost when it's not polled in time?

Maybe these are dumb questions but I already tried to find a solution on here and the rest of the internet but can't find any.

void MX_UART4_Init(void)
{

  huart4.Instance = UART4;
  huart4.Init.BaudRate = 115200;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart4) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(uartHandle->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspInit 0 */

  /* USER CODE END UART4_MspInit 0 */
    /* UART4 clock enable */
    __HAL_RCC_UART4_CLK_ENABLE();

    /**UART4 GPIO Configuration    
    PA0/WKUP     ------> UART4_TX
    PA1     ------> UART4_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* UART4 DMA Init */
    /* UART4_TX Init */
    hdma_uart4_tx.Instance = DMA1_Stream4;
    hdma_uart4_tx.Init.Channel = DMA_CHANNEL_4;
    hdma_uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart4_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart4_tx.Init.Mode = DMA_NORMAL;
    hdma_uart4_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_uart4_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_uart4_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_uart4_tx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_uart4_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_uart4_tx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_uart4_tx);

    /* UART4_RX Init */
    hdma_uart4_rx.Instance = DMA1_Stream2;
    hdma_uart4_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart4_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart4_rx.Init.Mode = DMA_NORMAL;
    hdma_uart4_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_uart4_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_uart4_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_uart4_rx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_uart4_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_uart4_rx) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_uart4_rx);

  /* USER CODE BEGIN UART4_MspInit 1 */

  /* USER CODE END UART4_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspDeInit 0 */

  /* USER CODE END UART4_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_UART4_CLK_DISABLE();

    /**UART4 GPIO Configuration    
    PA0/WKUP     ------> UART4_TX
    PA1     ------> UART4_RX 
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1);

    /* UART4 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmatx);
    HAL_DMA_DeInit(uartHandle->hdmarx);
  /* USER CODE BEGIN UART4_MspDeInit 1 */

  /* USER CODE END UART4_MspDeInit 1 */
  }
} 

/* USER CODE BEGIN 1 */
void sendUART(char msg[]){
    //HAL_UART_Transmit(&huart4,(uint8_t *) msg, strlen(msg),10000);
    HAL_UART_Transmit_DMA(&huart4,(uint8_t *) msg, strlen(msg));
}

void echo(){
    if(HAL_UART_Receive_DMA(&huart4, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK){}
    else if(HAL_UART_Transmit_DMA(&huart4, (uint8_t*)aRxBuffer, RXBUFFERSIZE)!= HAL_OK){
    }
}

Best Answer

First of all I can't get the stm to transmit at baud rates higher then the standard 115200, according to the datasheets both the FT2232H and the STM32F7 should be capable of at least 12M baud.

The hardware supports speeds up to 27 Mbit (well, you haven't told your part number, I'm looking at the F756 datasheet), but according to stm32f7xx_hal_uart.h, HAL won't accept a speed above 9M

#define IS_UART_BAUDRATE(BAUDRATE) ((BAUDRATE) < 9000001)

Moreover, it depends on the system clock speed, in the default configuration, when you don't touch the Clock Configuration tab in STM32CubeMX, everything operates on the 16 MHz internal HSI clock. That means at most 1 Mbit while you are using UART_OVERSAMPLING_16, or twice that if you switch to UART_OVERSAMPLING_8 (but then you'd lose noise detection).

Second is that I can't call the sendUART() function multiple times in a row, why isn't the DMA Fifo used to store the stuff I want to send?

Although there is a 16 byte DMA FIFO, it's not accessible to the software. There is no way to just append some more data to an ongoing DMA transfer. HAL does nothing but starts a DMA transfer from the buffer address supplied by the caller.

You have to wait until the transfer is finished, or suspend DMA, and still wait until the FIFO is empty. You can of course allocate a buffer yourself, add data as it comes, and restart DMA whenever it finishes and there is new data in the buffer.

Also whats the right way to echo back all received data but make use of the fifo so no data is lost when it's not polled in time?

It looks like to me, that you can't have both DMA and interrupts on every received character. At least, the ISR status register value would be useless, and the interrupt handler won't be able to decide what to do. Reading it could even interfere with the DMA transfer. Therefore, you must choose one.

Using DMA to put the data in a buffer (or two), you can then poll the transfer counter regularly in the idle loop or a timer interrupt. There won't be an immediate response, but it'd perhaps not matter at all, because the USB interface would incur some delay too.