cancel
Showing results for 
Search instead for 
Did you mean: 

How to handle Variable length receive data via UART?

TMuka.1
Associate II
uint8_t rx_buffer[32], rx_flag, rx_index, rx_data, RxData[32];
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance==USART1)
	{
		//if the data is not being received, clear the buffer
		if(rx_index ==0)
		{
			for (int i=0; i<20; i++)
			{
				rx_buffer[i]=0;
			}
		}
		//if the character received is other than 'enter' ASCII13, save the data in buffer
		if(rx_data!=13)
		{
			rx_buffer[rx_index++]=rx_data;
		}
		else
		{
			rx_index=0;
			rx_flag=1;  // turn the rx_flag 'ON'
			HAL_UART_Transmit(&huart1, rx_buffer, sizeof(rx_buffer), 100); // transmit the data via UART
		}
		HAL_UART_Receive_IT(&huart1, &rx_data, 1);  // restart the interrupt reception mode & receive only 1 char at a time!
 
	}
 
}

I declared a 32byte rx_buffer[], when I send more than 32 Bytes, the board couldn't receive any more serial data through interrupts! Attached my sample callback function and code inside that portion!

Regards,

Thangz

1 ACCEPTED SOLUTION

Accepted Solutions
Guenael Cadier
ST Employee

Dear @TMuka.1​ 

First, reading your code, my understanding is that if you receive more than 32 bytes different from 13, rx_index might become > 32 and then your callback will write data outside of the allocated RxData buffer. Maybe you should protect from this case to happen.

More over, just for information, ST now provides new HAL UART API in order to manage "continuous" reception or reception of unknown length (which is usually the reason for using HAL_UART_Receive_IT() with expected rx length value = 1). It is based on reception going till either expected length is received OR IDLE event occurs. It is available either in Interrupt or DMA modes.

For example, a use case could be based on HAL_UARTEx_ReceiveToIdle_DMA() API with DMA configured in Circular mode, that allows to have continuous reception and user callback executed as soon as either buffers are filled or Idle event occurs (pause in the transmission, i.e. enough time elapses after last received char) to retrieve data.

This callback will be executed when any of following events occurs :

   - HT (Half Transfer) : Half of Rx buffer is filled)

   - TC (Transfer Complete) : Rx buffer is full.

     (In case of Circular DMA, reception could go on, and next reception data will be stored in index 0 of reception buffer by DMA).

   - Idle Event on Rx line : Triggered when RX line has been in idle state (normally high state) for 1 frame time, after last received byte.

Usually, FW package contains a UART example highlighting such use case (example name UART_ReceptionToIdle_CircularDMA), with an example of callback implementation for retrieving all received data.

A usage example is provided in some recent STM32 Cube packages (I don't know if example is available on L1), illustrating use on this new API with a Circular DMA implementation (I think it could be found (for reference) in STM32 Cube package for G0/G4/L5, ... series under the name Examples\UART\UART_ReceptionToIdle_CircularDMA). Maybe it could help you.

Please have a look to this example to confirm it could address your needs.

Regards

View solution in original post

10 REPLIES 10
MM..1
Chief II

Why you use blocking Transmit inside receive callback???

Why clear only 20 when buff is 32

Why dont check 32 overrun

KnarfB
Principal III

HAL_UART_Transmit is a blocking function. Its not appropriate to call it in an interrupt context (what HAL_UART_RxCpltCallback is). You probably get an overflow error and miss incoming chars.

An alternative is demonstrated here: https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx

hth

KnarfB

TDK
Guru

The other answers are correct as far as the explanation to why things don't work for you.

However, if you want to receive variable-length data, you can use HAL_UARTEx_ReceiveToIdle which will return when the data stream goes idle or the specified number of characters are received, whichever happens first.

If you feel a post has answered your question, please click "Accept as Solution".
Guenael Cadier
ST Employee

Dear @TMuka.1​ 

First, reading your code, my understanding is that if you receive more than 32 bytes different from 13, rx_index might become > 32 and then your callback will write data outside of the allocated RxData buffer. Maybe you should protect from this case to happen.

More over, just for information, ST now provides new HAL UART API in order to manage "continuous" reception or reception of unknown length (which is usually the reason for using HAL_UART_Receive_IT() with expected rx length value = 1). It is based on reception going till either expected length is received OR IDLE event occurs. It is available either in Interrupt or DMA modes.

For example, a use case could be based on HAL_UARTEx_ReceiveToIdle_DMA() API with DMA configured in Circular mode, that allows to have continuous reception and user callback executed as soon as either buffers are filled or Idle event occurs (pause in the transmission, i.e. enough time elapses after last received char) to retrieve data.

This callback will be executed when any of following events occurs :

   - HT (Half Transfer) : Half of Rx buffer is filled)

   - TC (Transfer Complete) : Rx buffer is full.

     (In case of Circular DMA, reception could go on, and next reception data will be stored in index 0 of reception buffer by DMA).

   - Idle Event on Rx line : Triggered when RX line has been in idle state (normally high state) for 1 frame time, after last received byte.

Usually, FW package contains a UART example highlighting such use case (example name UART_ReceptionToIdle_CircularDMA), with an example of callback implementation for retrieving all received data.

A usage example is provided in some recent STM32 Cube packages (I don't know if example is available on L1), illustrating use on this new API with a Circular DMA implementation (I think it could be found (for reference) in STM32 Cube package for G0/G4/L5, ... series under the name Examples\UART\UART_ReceptionToIdle_CircularDMA). Maybe it could help you.

Please have a look to this example to confirm it could address your needs.

Regards

S.Ma
Principal

Either use a dma to fill up an incoming buffer big enough so you can check it every say 5 msec, or do the 8 bit mcu way, get an interrupt and callback for each incoming byte...

TMuka.1
Associate II

Hello,

@Guenael Cadier_O

I checked the example Examples\UART\UART_ReceptionToIdle_CircularDMA from L5 Series FW as you mentioned, it was really nice! But there is no 'HAL_UARTEx_ReceiveToIdle_DMA(&huart1, aRXBufferUser, RX_BUFFER_SIZE))' function for STM32L1 series!

Is there any equivalent HAL commands which does the same task as HAL_UARTEx_ReceiveToIdle_DMA() in STM32L1?

PFA main.c file!

Regards,

Thangz

Dear @TMuka.1​ 

I think you may find new API available in up to date version (v1.10.3) of STM32CubeL1 firmware package on Github at following link.

https://github.com/STMicroelectronics/STM32CubeL1

ReceptionToIdle APIs should be present in this package.

Regards

TDK
Guru

HAL_UARTEx_ReceiveToIdle_DMA is right here in the latest CubeL1 package:

https://github.com/STMicroelectronics/STM32CubeL1/blob/dd75b08e4ed505fabe7b6b61120b18094e768973/Drivers/STM32L1xx_HAL_Driver/Src/stm32l1xx_hal_uart.c#L1799

If you feel a post has answered your question, please click "Accept as Solution".

Dear @Guenael Cadier​ ,

I tried to use the aforementioned function, but after using that ReceptionToIdle() function the board wasn't receiving any message from the serial terminal anymore! I am not sure what is wrong here! I implemented exactly same as the previous Rx interrupt implementation! I just copied the entire HAL driver folder into the project directory and built all together, there was some error in sub-program stm32l1xx_hal_msp_template(), where the void HAL_MspInit(void) was first used and also an empty function, I just commented those function and got no more errors! Then I tried to debug, the board was not receving any Rx Message from serial terminal!

/**
  * @brief  Initializes the Global MSP.
  * @retval None
  */
/*
void HAL_MspInit(void)
{
  /* NOTE : This function is generated automatically by STM32CubeMX and eventually
            modified by the user
   */
//}
/**

I am not sure if there was something else which is wrong in my main.c file!

PFA main.c

Regards,

Thangaraj