Skip to main content
Visitor II
December 1, 2021
Solved

How to handle Variable length receive data via UART?

  • December 1, 2021
  • 8 replies
  • 13755 views
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

    This topic has been closed for replies.
    Best answer by Guenael Cadier

    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

    8 replies

    Graduate II
    December 1, 2021

    Why you use blocking Transmit inside receive callback???

    Why clear only 20 when buff is 32

    Why dont check 32 overrun

    Super User
    December 1, 2021

    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

    Super User
    December 1, 2021

    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.

    ST Employee
    December 1, 2021

    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

    Graduate
    August 28, 2024

    @Guenael Cadier , @TMuka.1 , @TDK 

    I find the solution the Guenael Cadier suggested (UART DMA circular buffer ReceptionToIdle) exactly what I am leaning to with my application (variable length commands) and I will be moving now towards the implementation.

    I cannot see in the Examples\UART\ folder the UART_ReceptionToIdle_CircularDMA example for STM32L05x (CubeIDE 1.12.1)

    Actually there is no implementation of HAL_UARTEx_ReceiveToIdle_DMA for STM32L0 family in stm32l0xx_hal_uart.

    Can you please indicate where I could find/download examples? Or does L0 family not support this function?

    Regards

    Super User
    August 28, 2024

    There aren't any examples in the L0, but you can adapt the examples for another family.

    The function definition is here:

    stm32l0xx_hal_driver/Src/stm32l0xx_hal_uart_ex.c at 0eac588d92ca6ab730621131c71614ce240bab8a · STMicroelectronics/stm32l0xx_hal_driver (github.com)

    Visitor II
    December 2, 2021

    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.1Author
    Visitor II
    December 2, 2021

    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

    ST Employee
    December 2, 2021

    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

    TMuka.1Author
    Visitor II
    December 2, 2021

    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

    Super User
    December 2, 2021
    ST Employee
    August 28, 2024

    Hi @_alaBaster 

    I think ReceiveToIdle API should be available in latest STM32L0 HAL drivers version (please check in stm32l0xx_uart_ex.c). If not, maybe you could have a look in latest  version of STM32L0 HAL drivers available on github here .
    API prototypes are present in stm32l0xx_hal_driver/Src/stm32l0xx_hal_uart_ex.c

    Regards

    Graduate
    August 29, 2024

    Hi @Guenael Cadier got it, thanks. I was looking only at stm32l0xx_hal_uart.c

    Kind regards