STM32 UART receive method

After a conversation with @dBC about receiving over UART I’ve come up with the following method.

in main.c

/* USER CODE BEGIN 2 */
  // enable IDLE flag interrupt. this will trigger UART interrupt handler in the event the host stop transmitting.
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
  // this initialises the UART Rx DMA. UART is now listening and received data will load directly to buffer.
  HAL_UART_Receive_DMA(&huart2, rx_buff, sizeof(rx_buff));
  /* USER CODE END 2 */

in stm32f3xx_it.c

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
  if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) == SET)
  {
    __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_IDLE);
    rx_flag = 1;
  /* USER CODE END USART2_IRQn 1 */
}

in main.c

/* USER CODE BEGIN WHILE */
  while (1)
  {
    // if the IDLE flag interrupt has been trigger, this flag should be set to indicate the Rx buffer has data.
    if (rx_flag)
    {
      uint16_t currentMillis1 = HAL_GetTick();
      //
      HAL_UART_DMAStop(&huart2); 
      memset(rx_buff,0,sizeof(rx_buff));
      HAL_UART_Receive_DMA(&huart2, rx_buff, sizeof(rx_buff));
      rx_flag = 0;
      //
      uint16_t currentMillis2 = HAL_GetTick();
      uint16_t millis_taken = currentMillis2 - currentMillis1;
      

      sprintf(log_buffer, "millis_taken: %d\r\n", millis_taken);
      debug_printf(log_buffer);
    }

    // print the millis since boot every interval.
    currentMillis = HAL_GetTick();
    if (currentMillis - previousMillis >= interval[0])
    {
      previousMillis = currentMillis;
      sprintf(log_buffer, "millis: %ld\r\n", currentMillis);
      debug_printf(log_buffer);
    }
    /* USER CODE END WHILE */

just bashing send with a mouse button.

millis_taken: 0
millis: 212000
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis: 214000
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis_taken: 0
millis: 216000
millis: 218000

I’d like to know how many clock cycles is takes. Anyone know how to check?

Just for the record, I use and recommend this tutorial from stm for the uart rx path.

HAL_GetTick() returns a uint32_t so I think that might be why you’re getting the occasional wrap.

Check out the DWT registers, they have a nifty cycle counter. That’s not an stm32 thing, it’s in the ARM core (M3 and M4) so the ARM documentation will tell you how to use them.

Important to have this logged for sure. I couldn’t get the LL drivers working but could be something to revisit.

There’s a more efficienct way of resetting the dma buffer index too. Instead of HAL_UART_DMAStop() and HAL_UART_Receive_DMA(), there’s something like huart2.hdmarx->Instance->CNDTR = sizeof(rx_buff). Couldn’t get that working.

You remind me now, I read it’s a 20-bit counter so needs a 32-bit holder. uint32_t should be used instead aye.

Will do. :+1:

The serial log has millis posted every 2 secs, the millis_taken is a response to a Tx from my computer. The log above shows it’s taking less than 1ms to process a received string of about 50 chars to reset the rx_buffer and get ready for the next packet.
uint32_t wouldn’t have made a difference in this case as the program is early in it’s counting, and GetTick hasn’t gone as far as 65,535.
Does this make sense?

…I’ll stick a deliberate delay in there to get some clearer numbers, making sure the millis_taken is giving something expected. Will report back.

Not sure what you’re referring to there. Remember the HAL source code is right there in your tree alongside your code. You can see there that HAL_GetTick() does nothing more than return the current contents of a variable called uwTick. It’s declared as uint32_t and gets incremented each systick interrupt (every msec).

You should use uint32_t to store timestamps, then you can do (t2 - t1) type measurements without any fear of wrapping as the wrapping is handled by the unsigned subtraction.

Agreed on using uint32_t, that’s how it’s done.
Correction, it’s a 24-bit decrement timer, the system tick. Doesn’t strictly matter for the purpose of this test, knowing this.

I’ve tested the code again. This time copying the incoming array to a separate array, to create something that could get used futher on in the program.

if (rx_flag)
{
  uint32_t currentMillis1 = HAL_GetTick();
  memcpy(rx_string, rx_buff, sizeof(rx_buff));
  memset(rx_buff,0,sizeof(rx_buff));
  HAL_UART_DMAStop(&huart2); 
  HAL_UART_Receive_DMA(&huart2, rx_buff, sizeof(rx_buff));
  rx_flag = 0;
  uint32_t currentMillis2 = HAL_GetTick();
  uint32_t millis_taken = currentMillis2 - currentMillis1;
  sprintf(log_buffer, "rx_string: %s\r\n", rx_string);
  debug_printf(log_buffer);
  sprintf(log_buffer, "millis_taken: %d\r\n", millis_taken);
  debug_printf(log_buffer);
}

Still takes less than 1ms.
Simplified:

if (rx_flag)
{
  rx_flag = 0;
  memcpy(rx_string, rx_buff, sizeof(rx_buff));
  memset(rx_buff,0,sizeof(rx_buff));
  HAL_UART_DMAStop(&huart2); 
  HAL_UART_Receive_DMA(&huart2, rx_buff, sizeof(rx_buff));
}

sending 50 chars at a time at 460800 baud. 22 seconds worth of button bashing.

millis: 12000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 14000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 16000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 1
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 18000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 1
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 20000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 1
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 22000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 24000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 26000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 1
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 28000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 30000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 1
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 32000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0
millis: 34000
rx_string: 12345678901234567890123456789012345678901234567890
millis_taken: 0

I’m pretty much content with this. It works.
I’d like to directly reset the counter of the DMA register instead of DMA STOP/START, then it’s nearer on par with the efficiency of a DMA ring buffer outlined in the tutorial above. Edit: Got it.
huart2.hdmarx->Instance->CCR &= ~DMA_CCR_EN;
huart2.hdmarx->Instance->CNDTR = sizeof(rx_buff);
huart2.hdmarx->Instance->CCR |= DMA_CCR_EN;