Search code examples
cembeddeduart

UART Synchronization Algorithm for receiving Stream of Data


I am receiving this frame $0,S1,B2,Kffffffffffff,T61*34 through UART with this code.

//Receive Data
for(uint8_t i = 0; i < size; i++){
    receivedFrame[i] = EUSART1_Read();
    if(receivedFrame[i] == '*'){
        size = i + 3;
    }
}

The start of the frame is always $ and the end is always * after that comes two bytes holds the check sum for the previous bytes (ex 34).

The frame length is not fixed but it has a minimum length of 26(from $ to *) + 2 bytes of check sum and maximum length of 62 and also + 2 bytes of check sum.

but it is not the best thing to use since so many scenarios could happen to make this unstable for example if the * didn't arrive, this will make every frame I read is wrong.

I searched for better way to receive data through UART but didn't manage to find any thing. I am looking for better ways to deal with receiving frames like this.


Solution

  • This solution is based on the assumption that there is no trailing CR or LF, but it will also work in this case by skipping CR and LF until $ is found.

    I suggest to combine your algorithm with a check for $ which whould reset the position to 0 and store the $ at position 0. This will ignore frames that don't contain * at the expected position.

    You probably should initialize size to the maximum before entering the loop and make sure that size is not increased in case the * is received too late.

    Additionally I would make sure that the first character is '$' and not increment the index if this is not true.

    For this conditional incrementing of the index, a while loop is better suited.

    Untested proposal:

    unsigned char receivedFrame[64];
    uint8_t size = sizeof(receivedFrame);
    uint8_t i = 0;
    while(i < size)
    {
        unsigned char ch = EUSART1_Read();
        if(ch == '$')
        {
            // in case i was > 0, ignore wrong frame, start from beginning
            i = 0;
        }
        else if(receivedFrame[i] == '*'){
            uint8_t new_size = i + 3;
            if(new_size < size)
            {
                size = new_size;
            }
        }
        receivedFrame[i] = ch;
        // stay at the first position if the character is not '$'
        if((i > 0) || (ch == '$'))
        {
            i++;
        }
    }
    

    Additional hint: For testing the algorithm on a PC you could replace EUSART1_Read with fgetc or getchar. Note that these functions return an int value which should be checked for EOF before converting it to an unsigned char.