cancel
Showing results for 
Search instead for 
Did you mean: 

How do I synchronize received UART data ?

johngj
Senior

I am trying to receive a frame containing 10 bytes of serial data, each frame is transmitted at a 100ms rate.

However, as the UART can start receiving the 10 byte frame at any point in time, the received data is becoming out of sync. 

How do I sync the UART peripheral so that it always starts receiving at the start of the 10 byte frame ?

Below is a scope capture of the 10 bytes of serial data:

johngj_0-1706805151387.png

Below is a scope capture of the 10 bytes of serial data being sent at a 100ms rate:

johngj_1-1706805208495.png

I am using the STM32 Nucleo-64 development board with STM32L433RC MCU. 

Its as if I need to monitor the serial line with a timer, in order to detect the period when the serial line is high for say 50ms and use this timer to enable and disable the UART peripheral.  But there must be a better way to do this ?

The UART is configured as shown below and the rx buffer is a size of 10.

johngj_2-1706805851655.png

johngj_3-1706805879295.png

johngj_4-1706805905455.png

The 10 bytes are transmitted by a electronic speed controller, used for radio control applications and contains the following data:

Byte 0: Temperature
Byte 1: Voltage high byte
Byte 2: Voltage low byte
Byte 3: Current high byte
Byte 4: Current low byte
Byte 5: Consumption high byte
Byte 6: Consumption low byte
Byte 7: Rpm high byte
Byte 8: Rpm low byte
Byte 9: 8-bit CRC

 

 

 

 

23 REPLIES 23

Typically people would have a preamble/sync byte, if you're in-sync try to receive the whole packet, if not receive one byte at a time until you get the sync byte, then request packet size minus one?

Most of the STM32 interrupt for every byte, it's only the HAL doing the collection and callback stuff.

You could perhaps use 9-bit mode, and use the 9th bit to indicate the first byte.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Lots of ways to do this

  • Define a transmission format with a specified "start of frame" marker
  • Define it by timing; eg, "1st byte after Xms quiet is start of frame"
  • A query-response protocol
  • A separate hardware sync signal
  • etc, etc, ...
KMill
Senior

I'm 90% sure that MCU has IDLE time detection on the UART. Which means you can trigger an interrupt after a programmable amount if idle time on the RX line. Have a look at HAL_UARTEx_ReceiveToIdle_IT() etc

Karl Yamashita
Lead III

Show your code so we can see how you're receiving the packet.

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

You are dealing with an asynchronous nature of this UART reception: yes, if you start "listening" (receiving) at any time, when the transmitter sends all the packets with 10 bytes - I can start right in the middle of a "packet".

I have similar issue as: I send a command via UART and the response can have any length (e.g. a long help text or a very short value).
If you run all characters in ASCII mode, means: all your bytes are characters in the hex value range 0x20...0x7F - you can use a special character, e.g. "End Of Text", coded as 0x03 byte: every "packet" (actually a string) ends with 0x03:

Wait for the 0x03 and know that this was the "End of Text" (end of package): check the length of received bytes: if it matches with 10 - fine, a complete package. If length is shorter: you have started receiving right in the middle of a packet:
discard this incomplete packet (which should happen only at the very first one, when you start to "synchronize" with the transmitter, afterwards you should be in sync).

If you run all in binary mode, means: all your bytes can have values in range 0x00..0xFF - you cannot use special control characters.

But you have the option to start the package with a length field, e.g. a byte which tells you how many bytes should follow.

If this mismatches - discard all and just wait for the next packet.

BTW:
An easier way, instead to "hook on" a asynchronous transfer, is to implement a handshaking: send the transmitter something and then it will respond: the transmitter side just sends something back when it was requested to do so.
This makes sure you are always in sync.

If you want to keep going with the approach, that you try to "listen" to a stream of packets, coming without any request ("ping-pong", no handshake), sent by the remote transmitter periodically, you can do this:

  • as mentioned by Tesla DeLorean: have a prefix (preamble) and postfix (end character) - it marks you packet
    with START and END - but needs special control characters - you cannot use full binary packets
  • receive at least two packets (the doubled packet size)
  • and look for prefix and end character in this "byte stream"
  • now you should be in sync
  • you will find an "offset" where the prefix byte is located: all the next transmissions should have the same offset to the "start of packet" and you can keep going to take the correct entire packet from the input "stream buffer"

Remark:
Using a timer to measure if there was a gap between packets - it could work as well.
But is very dependent on packet size, the baud rate or if the transmitter would "stall" a bit, e.g. a gap between bytes because the host is busy to keep going with sending bytes in a sequence.

A handshake, at least you "release" the transmitter to start sending regularly the packets is the best approach.

Or use special characters to get in sync with the transmitter.

Or have a length field and use the length info to verify if packet received (in buffer) has the correct length.

Synchronizing with an asynchronous stream of packets needs a mechanism to "find" the packet in a stream and than to stay in sync with it. Once you have synchronized (you might need some packets received but place all in a "stream buffer") you should be fine from then on forward.

 

johngj
Senior

Thanks all

Unfortunately I have no control over the serial data that is being transmitted, so I cannot change the data frame or add handshaking etc

The serial data is a standard known as "KISS ESC telemetry" and is transmitted by an Electronic Speed Controller (ESC).  The ESC I am using is as follows...

https://powerdrives.net/120f3 

Details about the KISS ESC telemetry are explained in the attached pdf

I was hoping there might be a feature suggested by KMill i.e. an IDLE time detection on the UART so I can trigger an interrupt after a programmable amount if idle time on the RX line.  So I will investigate to see whether this is possible. 

 

 

johngj
Senior

As requested by Karl Yamashita, I have attached my code.

There is also an SD card driver in my code, because I am trying to receive the serial data from the ESC and save that data to an SD card.

The project is to design a data logger that can be used with any ESC which uses the KISS telemetry.

 


@johngj wrote:

Details about the KISS ESC telemetry are explained in the attached pdf


So have you read that document?

It seems to tell you that you request the data when you want it:

"the telemetry is requested with a short PWM pulse of 30μS (+-2μS)"

So, once you've done that, you should expect to receive the 10 bytes.

That's what provides the synchronisation - it is, effectively, a request-response protocol.

It also tells you that there's a CRC - so you can use that to determine whether you have a valid "frame".

 

The document is short on details - you should contact Powerdrives for support:

https://powerdrives.net/contact 

 

Thanks Andrew,

Unfortunately I have no control over the telemetry request either.

The datalogger has to be passive and "listen in" to the telemetry serial data.

The telemetry request is performed by a flight controller, in this case a Pixhawk 6C mini and in this case using a protocol called DSHOT (for some reason RC seems to use non-standard protocols  :face_with_rolling_eyes:).

The flight controller sends the DSHOT message which requests the telemetry data.

My datalogger has to then listen for the telemetry data response (from the ESC) and store the data to an SD card.

I've already got the CRC check working in my code (which I attached in an earlier post - see sdcard.7z)

johngj_1-1706881085756.png