STM32 HAL 101 – UART – Receiving arbitrary length string

Despite the fact that UART communication is the simplest and mostly used one, there are some problems or limitations that we can face when coming from other more complex communication protocols like, for example, USB.

The first problem that we can find is the lack of having an efficient way to receive an arbitrary amount of data over UART/USART.

In fact any receive function that we can use need the field “length” to be specified. What if we can’t say -a priori- what will be the length of our string?

Here is a possible implementation using the DMA.

Step 1: CubeMX setup of peripherals

Starting from the setup of the uart peripheral in our CubeMX project:

Nothing special here, just the most common setup: 8 bit of word length, no parity and 1 stop bit.

Don’t forget to enable the global interrupt for the UART from the NVIC Settings tab:

And then we’re only missing one more step: enabling the DMA for the rx.

The CubeMX part is finished, let’s now generate the code for the environment that you prefer and open the main.c.

Step 2: main.c edits

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
//.....
/* USER CODE BEGIN PV */
uint8_t txBuffer[] = "test,ciao!ciao!\n";
uint8_t rxBuffer[24];
uint8_t rxByte;
uint16_t rxIdx;
uint8_t rxFlag;
/* USER CODE END PV */

//... in main():

  /* USER CODE BEGIN 2 */

  __HAL_UART_FLUSH_DRREGISTER(&huart2);
  HAL_UART_Receive_DMA(&huart2, &rxByte, 1);

  HAL_UART_Transmit(&huart2, txBuffer, strlen((char*) txBuffer),100);

  int i;
  /* USER CODE END 2 */

//...in while(1){:
    /* USER CODE BEGIN 3 */
    if(rxFlag == 1) {
    	rxFlag = 0;
    	HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
    	for(int i=0;i<24;i++) rxBuffer[i] = 0;
    	snprintf((char*)txBuffer, sizeof(txBuffer), "Ciao!%d\r\n", i);
    	HAL_UART_Transmit(&huart2, txBuffer, strlen((char*) txBuffer),100);
    	i++;
    }
    HAL_Delay(100);
  }
  /* USER CODE END 3 */

//.. at the end of file:
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {

	if (huart->Instance == USART2) {
		__HAL_UART_FLUSH_DRREGISTER(&huart2);
		if(rxByte == 10 || rxIdx >= 23) {
			rxBuffer[rxIdx] = rxByte;
			rxFlag = 1;
			rxIdx = 0;
		}
		else {
			rxBuffer[rxIdx] = rxByte;
			rxIdx++;
		}
	}

}
/* USER CODE END 4 */

In the USER CODE PV we are declaring our globals needed for the comunication:
-txBuffer si the buffer used for trasmission
-rxByte is the variable for the single byte that we’ll receive by DMA
-rxBuffer is the complete string that we’ll build

The magic happen at the bottom of the file, in HAL_UART_RxCpltCallback().
The idea is to fill the rxBuffer, byte-by-byte, until we receive a termination char (that can be choosen) or until the buffer is full.

NOTES

In the attachment you can find the whole project. It has been developed for a STM32F4-Discovery board but can be easily ported on other platforms.

If you’re testing the UART on one board by a jumper between TX and RX and if you’re having problems like receiving only one byte in a random fashion please make sure to call the HAL_UART_Receive_IT/HAL_UART_Receive_DMA BEFORE sending any data!!
We had this stupid problem and have tried to find a solution for 2 days 😀 and this is basically why I’ve written this brief guide.

Please feel free to leave a comment. Any suggestion will be highly appreciated!!

Attachments

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: