Skip to main content
Marco S
Associate III
April 7, 2020
Question

How to use the 'HAL_UART_Receive_IT' within a function?

  • April 7, 2020
  • 11 replies
  • 8572 views

Hello,

I'm working with an STM32G4 MCU and I want to listen the UART3 with interruption. I am testing with a code which changes the color of a led. The following code works fine :

//main.c
 
#include "main.h"
 
 
 
FDCAN_HandleTypeDef hfdcan3;
 
FDCAN_HandleTypeDef hfdcan4;
 
 
 
char uart3_buffer[64]={0};
 
uint8_t RxChar3;
 
 
 
int main(void)
 
{
 
 /* USER CODE BEGIN 1 */
 
 
 
 /* USER CODE END 1 */
 
 
 
 /* MCU Configuration--------------------------------------------------------*/
 
 
 
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 
 HAL_Init();
 
 
 
 /* USER CODE BEGIN Init */
 
 
 
 /* USER CODE END Init */
 
 
 
 /* Configure the system clock */
 
 SystemClock_Config();
 
 
 
 /* USER CODE BEGIN SysInit */
 
 
 
 /* USER CODE END SysInit */
 
 
 
 /* Initialize all configured peripherals */
 
 MX_GPIO_Init();
 
 MX_FDCAN3_Init();
 
 MX_UART4_Init();
 
 MX_USART3_UART_Init();
 
 
 
 /* USER CODE BEGIN 2 */
 
 
 
 
 
 /* USER CODE END 2 */
 
 
 
 /* Infinite loop */
 
 /* USER CODE BEGIN WHILE */
 
 while (1)
 
 {
 
	 	/* Listen the USART3. */
 
		HAL_UART_Receive_IT(&huart3, &RxChar3, 1);
 
 
 
		/* Copy only the non null characters into the buffer. */
 
		if(RxChar3==49){ // ASCII of character 1.
 
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, GPIO_PIN_SET); /* Function to light on a led activating a GPIO. */
 
		}
 
		else if(RxChar3==50){ // ASCII of character 2.
 
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);/* Function to light on a led activating a GPIO. */
 
		}
 
		else if(RxChar3==51){ // ASCII of character 3.
 
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, GPIO_PIN_RESET); /* Function to power off a led deactivating a GPIO. */
 
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); /* Function to power off a led deactivating a GPIO. */
 
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_RESET); /* Function to power off a led deactivating a GPIO. */
 
		}
 
		else{
 
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_SET); /* Function to light on a led activating a GPIO. */
 
		}
 
 
 
		/* Sends the current value of Rxchar3 variable (only for debugging). */
 
		snprintf(uart3_buffer, sizeof(uart3_buffer), "Rxchar3: %u\n", RxChar3);
 
		HAL_UART_Transmit(&huart3, (uint8_t*)uart3_buffer, strlen(uart3_buffer), 0xFFFFFFFF);
 
	 HAL_Delay(500);
 
 /* USER CODE END WHILE */
 
 
 
 /* USER CODE BEGIN 3 */
 
 }
 
 /* USER CODE END 3 */
 
}

Nevertheless, as I have more than one than one serial port configured in the MCU I want to put the 'HAL_UART_Receive_IT' function within another function which receives as input parameter the number of serial interface. I have done as follows:

main.c

//main.c
FDCAN_HandleTypeDef hfdcan3;
FDCAN_HandleTypeDef hfdcan4;
 
char uart3_buffer[64]={0};
uint8_t RxChar3;
 
int main(void)
{
 /* USER CODE BEGIN 1 */
 
 /* USER CODE END 1 */
 
 /* MCU Configuration--------------------------------------------------------*/
 
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 
 /* USER CODE BEGIN Init */
 
 /* USER CODE END Init */
 
 /* Configure the system clock */
 SystemClock_Config();
 
 /* USER CODE BEGIN SysInit */
 
 /* USER CODE END SysInit */
 
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_FDCAN3_Init();
 MX_UART4_Init();
 MX_USART3_UART_Init();
 
 /* USER CODE BEGIN 2 */
 
 
 /* USER CODE END 2 */
 
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
	 	/* Listen the USART3. */
		UART_RX_String01(3); // Listen UART3
 
		/* Copy only the non null characters into the buffer. */
		if(RxChar3==49){ // ASCII of character 1.
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, GPIO_PIN_SET); /* Function to light on a led activating a GPIO. */
		}
		else if(RxChar3==50){ // ASCII of character 2.
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);/* Function to light on a led activating a GPIO. */
		}
		else if(RxChar3==51){ // ASCII of character 3.
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, GPIO_PIN_RESET); /* Function to power off a led deactivating a GPIO. */
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); /* Function to power off a led deactivating a GPIO. */
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_RESET); /* Function to power off a led deactivating a GPIO. */
		}
		else{
			HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_SET); /* Function to light on a led activating a GPIO. */
		}
 
		/* Sends the current value of Rxchar3 variable (only for debugging). */
		snprintf(uart3_buffer, sizeof(uart3_buffer), "Rxchar3: %u\n", RxChar3);
		HAL_UART_Transmit(&huart3, (uint8_t*)uart3_buffer, strlen(uart3_buffer), 0xFFFFFFFF);
	 HAL_Delay(500);
 /* USER CODE END WHILE */
 
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
}

uart_func.h

//uart_func.h
#ifndef INC_UART_FUNCTIONS_H_
#define INC_UART_FUNCTIONS_H_
 
#include "stm32g4xx_hal.h"
 
extern UART_HandleTypeDef huart3;
extern UART_HandleTypeDef huart4;
 
void UART_RX_String01(uint8_t);
 
#endif /* INC_UART_FUNCTIONS_H_ */

uart_func.c

//uart_func.c
#include "uart_func.h"
void UART_RX_String01(uint8_t port_number){
	uint8_t RxStringBuffer;
	HAL_StatusTypeDef RX_State;
 
	/* Select the UART port to be listened. */
	if (port_number==3){
		RX_State = HAL_UART_Receive_IT(&huart3, &RxStringBuffer, 1);
 
		/* Send the function's return value. */
		snprintf(uart_buffer, sizeof(uart_buffer), "Rx State %u", RX_State);
		HAL_UART_Transmit(&huart3, (uint8_t*)uart_buffer, strlen(uart_buffer), 0xFFFFFFFF);
	}
 
	if (port_number==4){
		RX_State = HAL_UART_Receive_IT(&huart4, &RxStringBuffer, 1);
 
		/* Send the function's return value. */
		snprintf(uart_buffer, sizeof(uart_buffer), "Rx State %u", RX_State);
		HAL_UART_Transmit(&huart4, (uint8_t*)uart_buffer, strlen(uart_buffer), 0xFFFFFFFF);
	}
}

The 'main.h' header file includes the 'uart_func.h' header. The problem I have with this is that in this case the MCU blocks around 20 seconds when I send a character to the serial interface. Then it continues turning. Why does this happen? Or isn't it possible to call this function out of the main.c ?

This topic has been closed for replies.

11 replies

TDK
April 8, 2020

It looks like you're calling HAL_UART_Receive_IT repeatedly in the main loop. The idea is to call this once and handle the logic for receiving within the interrupt routine. Where is it blocking?

"If you feel a post has answered your question, please click ""Accept as Solution""."
Marco S
Marco SAuthor
Associate III
April 8, 2020

Hello TDK,

In the first code when calling 'HAL_UART_Receive_IT' repeatedly in the main loop the microcontroller seems to work well and it doesn't block. In the second code, when the 'HAL_UART_Receive_IT' is called within the function 'UART_RX_String01' (which is repeatedly called in the main loop) the microcontroller blocks during around 20 seconds in the line 9 of the file 'uart_func.c', when calling 'HAL_UART_Receive_IT'.

Guenael Cadier
ST Employee
April 8, 2020

Hello @Community member​ 

HAL_UART_Receive_IT() API is used for initiating a reception : when function exits (with HAL_OK return code is call is successfull) then reception is active and any further incoming byte will be copied in reception buffer. So, in first implementation, immediately after calling HAL_UART_Receive_IT(), you have to wait for the character to be received (until that, value of RxChar3 is not updated).

I guess your while(1) loop is performing HAL_GPIO_WritePin actions endlessly using the stored RxChar3 value, but this will change only when a char is received.

Moreover, when a reception is started with HAL_UART_Receive_IT() API, and till it is completed (1 byte received in your case), further calls to HAL_UART_Receive_IT() API will be ending with HAL_BUSY return code (nothing done, as reception is already ongoing).

To know when a reception in IT mode could be considered as completed, you should wait for HAL_UART_RxCpltCallback() callback execution.

A weak (empty body) definition of this callback exists in HAL code, you could define your own callback in your code.The callback has a parameter which is the UART handle, so you could adapt treatment to huart3 or huart4, when called.

Note : In your 2nd implementation, received char will be stored in RxStringBuffer, which is a UART_RX_String01() local variable, so not visible outside the function. your main loop is still testing RxChar3 value, so will not reflect received char value anyway.

Here is one proposal for avoiding endless calls to HAL_UART_Receive_IT, if my understanding of what you want to do is right (not a final implementation, not tested nor compiled, just ideas for understanding the API calls then callbacks execution scheme). Goal is to use a global variable indicating if reception is completed or not :

main.c

+
 
/**
 * @brief Rx Transfer completed callback.
 * @param huart UART handle.
 * @retval None
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 /* Inidicate reception is completed */
 if (huart == &huart3)
 {
 usart3_reception_ongoing = 0;
 ... 
 }
}

uart_func.c

void UART_RX_String01(uint8_t port_number)
{
 HAL_StatusTypeDef RX_State;
 
 /* Select the UART port to be listened. */
 if (port_number==3)
 {
 if ( usart3_reception_ongoing == 0)
 {
 usart3_reception_ongoing = 1;
 RX_State = HAL_UART_Receive_IT(&huart3, &RxStringBuffer, 1);
 ...
 }

with following decalarations of global variables :

volatile uint8_t usart3_reception_ongoing = 0;

Volatile attribute is useful to remain independent on your compiler, and on your code optimization options, regarding proper access to variables that are accessed either in interrupt routines (and callbacks), and in other code parts;

Hope this helps.

Regards

Guenael

Piranha
Principal III
April 8, 2020
if ( usart3_reception_ongoing == 0)
 {
 usart3_reception_ongoing = 1;
 ...
 }

This is flawed because it's not interrupt-safe.

And how about ST making an actually useful high-level drivers, not this "call receive for a single character" idiot made crap HAL is providing now? Is a multi-billion company able of making usable USART driver in 12+ years, which I (one person) was able to make in a few days?

Guenael Cadier
ST Employee
April 8, 2020

As stated in my comment to initial post, my purpose was just for "understanding the API calls then callbacks execution scheme".

Regarding HAL UART API, we will extend it with enhanced services, that will allow to address specific use cases as reception of unknown length.

Guenael

Dr X
Associate
April 15, 2020

Hello,

I've got exactly same problem, when I declare

static uint8_t RxStringBuffer;

locally or as @Guenael Cadier​  mentioned globally it works fine but with similar approach to uart_func.c which declares this variable locally (with or without volatile specifier) it does not receive data correctly.

Data will be received correctly with both definition for HAL_UART_Receive() though.

Guenael Cadier
ST Employee
April 16, 2020

Hi @Dr X​ 

When using initial split of code between main.c/uart_func.c approach, with UART_RX_String01() function declaring RxStringBuffer variable as local, here it what is happening :

  • RxStringBuffer variable is declared locally, i.e. its scope is restricted to function UART_RX_String01(). Adress of this variable will be in stack.
  • inside this function, calling
RX_State = HAL_UART_Receive_IT(&huart3, &RxStringBuffer, 1);
  • indicates that received data should be copied at this address (&RxStringBuffer)
  • So when function exits, your received data could be considered as "lost" (stored in an area that is no more referenced).
  • Tests executed in main.c have no link with received value :
if(RxChar3==49){ ...
  • It is not possible that RxChar3 could contain any of the received data.

If you want to be able to use the value of your received data in the main.c scope, the variable containing it, should be "visible" in the main.c. A variable declared locally in a function is no more available when function exits.

I hope I understood your question well ...

Regards

Dr X
Associate
April 17, 2020

Hi @Guenael Cadier​ 

Thanks.

I understand that HAL_UART_Transmit/ Receive_IT function are meant to be non-blocking and by little bit digging I found out that in fact as you mention, these two functions delegate the execution to other function that may execute after exiting the uart_func.c (therefore RxStringBuffer

is not in the scope anymore ). to prove it I did the following experiment with HAL_UART_Transmit_IT();

void NonCriticalTask(UART_HandleTypeDef *usrt)
{
	char msg[30];
	
	sprintf(msg, "\r\n this is just a test \r\n");
	HAL_UART_Transmit_IT(usrt, (uint8_t *)msg, strlen(msg));
 
 /* delay to keep msg in the scope when micro spits our the data */
 /* HAL_UART_TxCpltCallback can be used but a delay is enought to prove the point */
 HAL_Delay(10000);
 
	
}

And it works fine with the delay but spits out gibberish without delay.

From strict C language point of view NonCriticalTask() function should work fine with or without delay since HAL_UART_Transmit_IT

is called when msg is still in the scope, but from HAL point of view because of the delegation that I mentioned before, it doesn’t work. To me it is counter intuitive though, and I failed to find anything related to this consideration in HAL documentation. However your answer was clear enough for me and I understand it now :smiling_face_with_smiling_eyes: .