cancel
Showing results for 
Search instead for 
Did you mean: 

Need help with RS232 receive & STMCubeMx

Osto
Senior
Posted on August 02, 2014 at 08:45

Hi,

I'm using RS232 on USART2 of STM32F407 with CubeMx 4.0. I have a protocol which starts with '[' and ends with ']'. I need everythin between 2 braces and I have variable record length. So its not possible to use HAL_UART_Receive_IT because the length need to be known at time of call.

Therre is also no way to set a delimiter or any character to stop receving or call HAL_UART_RxCpltCallback when it receives a defined character. Something like readline() or similar. Right now, I have a big buffer (max possible size + 10) and check the huart structure manually and process them when there is anything received.

But I think, its not the right way to do that.

Have anybody an idea how such problem should be solved?

Thanks in advance.
10 REPLIES 10
mike239955
Associate II
Posted on August 07, 2014 at 18:48

ostovary.m

I had a similar problem.  I needed to pick-off strings that were of variable length.  Fixed length buffers would not work at all.  So I had CUBE take a single character buffer and let me know when it filled it via the HAL_UART interrupt handling.  I have a module that I reuse a lot for character queue manipulation.  There is an ENQUEUE method that an ISR can use to stuff characters into the queue and a DEQUEUE method that is used by the string gathering stuff to build a string for processing.

To get this to work with CUBE, here's what I did:

Immediately after setting up my UART, I made a call to the HAL that setup the receive buffer as follows:

// Need a place for the character to go and a UART handle data structure:

uint8_t character_in;

UART_HandleTypeDef my_UART_handle;

void my_UART_setup(void)

{

//

// Normal handle setup stuff here

// ... you get to fill this in......

//

// Now, tell CUBE about the single character receive buffer:

    if (HAL_UART_Receive_IT(&my_UART_handle, &character_in, 1) != HAL_OK)

    {

        while(1); // This deserves better error handling....

    }

}

With this and the interrupts all enabled, the UART interrupt hits on a character received.  Per the CUBE methodology, I have a callback that deals with the received character event:

// I put this in stm32f4xx_it.c with all the other interrupt handlers:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

    if (huart->Instance == USART1)

    {

        my_HAL_UART_RxCpltCallback(huart);

    }

}

This then calls my little callback which I co-located with all my stuff for actually dealing with the string:

void my_HAL_UART_RxCpltCallback(UART_HandleTypeDef *hUART)

{

    CQ_EnqueueChar(rx_queue_struct_ptr, character_in); // this is one of my queue

                                                // handling functions

    HAL_UART_Receive_IT(hUART, &character_in, 1);   // This kind of re-arms the receive.

}

In my processing task, I pick off the characters using my CQ_dequeueChar() method which is totally system independent.

Structurally it's pretty simple.  One place where I have had a MAJOR issue, though, is where one of my serial ports is actually a '485-type setup where characters that are sent echo immediately back into the UART.  This mechanism seems to have enough overhead that my ISR handling gets tripped-up.  I had to drop-kick the CUBE approach and go down to the register level using the Instance sub-structure in the handle structure.

Hope this is right (for one thing -- expert feedback more than welcome) and that it helps (for another...).

Best regards,

Michael.

jasonp4113
Associate II
Posted on August 08, 2014 at 02:04

Hi

My method was to put in another callback per byte into ''xxxx_HAL_UART.c'':

      }

HAL_UART_RxCpltCallback(huart);

    }

else

{

HAL_UART_RxByteCallback(huart); // My callback

}

    return HAL_OK;

  }

  else

  {

return HAL_BUSY; 

  }

That way you can interrogate each byte as they are received and also you can then also make the HAL_UART_Receive_IT() function receive as many bytes as the size of your buffer without having to reload the HAL_UART_Receive_IT() after every byte.

You just have to make a note about the HAL mod in case you rebuild the HAL using Cube.

Jason

Osto
Senior
Posted on August 09, 2014 at 21:49

Hi Michael,

This way is a good way but very time consuming. I have 115000 baud at 5 UART/USART's. I lose actually charachters during ''rush hour''.

Thanks.

Mehrdad
Osto
Senior
Posted on August 09, 2014 at 21:52

Hi Jason,

This is a good way but when I change something in the config with STM32Cube then the call back is renoved.

Thanks,

Mehrdad
jasonp4113
Associate II
Posted on August 12, 2014 at 00:12

Mehrdad,

I understand this and had noted that in my post. If you have got away without making any mods to the HAL drivers to get things working properly - you are doing very well.

(I document all HAL mods I need to make in case I have to re-build a project using Cube - and there are a few so far...)

Osto
Senior
Posted on August 17, 2014 at 15:25

Dear Jason,

I changed the HAL-Files so that the interrupt vector will show to my own service routine and I do all myself. I use HAL only for initialising and some inline functions.

I have to change the verctors every time I generate new project. Its done in 2 monutes and I solve all my problems.

In my opnion is at least the serial part of the HAL not well organzied. I programmed hunderts of communication protocols in the last 30 years but I dont know even one protocol which can use the HAL. Serial (low speed) protocols are always variable length and  you never know how many characters are received with next message but you have always  a start of message char(s) and a end of message char(s). Specially if you use RS485 then you have a big problem with HAL.
Andrew Neil
Evangelist
Posted on August 19, 2014 at 09:20

This thread looks to be essentially the same topic: 

[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Variable-size%20Rx%20on%20UART%20using%20Interrupts&currentviews=15]https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2FVariable-size%20Rx%20on%20UART%20using%20Interrupts&currentviews=15

Andrew Neil
Evangelist
Posted on August 19, 2014 at 11:00

''Serial (low speed) protocols are always (sic) variable length''

No, of course they're not! eg, XMODEM.

However, I do agree that they are

very

common indeed - so any decent UART library really should provide decent support for it!

Also quite common is for a certain length of ''silence'' to mark the end of a ''message''...

rreay
Associate II
Posted on September 24, 2014 at 22:05

I ran into the same problems as everyone else.  I'm writing a shell that the user types to, I don't have fixed length buffer.  Also as soon as you start trying to echo characters as they come in the system as it exists gets hung up and you start getting a ton of overflow errors.

It sounds like I ended up solving this the same way as dupont.michael and ostovary.m .  I changed the behavior to make the TX INT handler send from a buffer and the RX INT handler to directly buffer.  I do minimal state changes between transactions.

For anyone still paying attention to this thread I include details.

I let the HAL configure HW. The RX and TX callbacks were reinterpreted to be called for every character, I ended up not using them anyway.  I rewrote a handful of functions but none of the interfaces changed.

HAL_UART_Init: 

    // original code plus:

    * configure a tx queue and an rx queue of characters (simple circular buffer)

    * enable UART_IT_PE, UART_IT_ERR, and UART_IT_RXNE

HAL_UART_Transmit_IT:

    * add characters to tx buffer

    * enable UART_IT_TXE

UART_Transmit_IT:

    * get a character from the que

    * if there was a character 

      * send huart->Instance->DR = c;

      * call callback

    * else disable UART_IT_TXE

    

HAL_UART_IRQHandler: 

    Unchanged.

    

HAL_UART_Receive_IT:

    Nothing here. The IT handler queues everything directly and UART_IT_RXNE is enabled at init time and never touched until I shut the UART down.  

    

    I could, but didn't, change this to a wrapper around my queue_get(rx)

    

UART_Receive_IT:

    * read the character from DR

    * add it to the RX queue

    * call callback

    

There's some fancy footwork to make those queues both thread and interrupt safe and I used a semaphore to make reads from the rx queue block when it's empty.  That let me write my higher level code simply as:

for(;;) {

    c = queue_get(rx);

    process(c);

}

All of this is actually new code with new function names.  If I have to have Cube rebuild my project the only manual changes are:

* any calls to HAL_UART_IRQHandler() in the generated *_it.c.  

* add a field to UART_HandleTypeDef to track my queues.