7 years, 8 months ago.

Reasonable expectation for UART throughput?

I am using a Maple Mini (as described at https://os.mbed.com/users/hudakz/code/MapleMini_Hello/ ) to read a UART stream from a GPS module. The GPS outputs messages of about 100 bytes in size, five times per second, at 19200 baud.

If this is the only thing my program is doing, it can read the incoming bytes fast enough to store the data successfully. That is, it simply waits for a specific header byte, copies a certain number of subsequent bytes into a struct, and compares a checksum. No problem.

However, if I make my program do ANYTHING else, it misses incoming bytes, so the checksum always fails and it will never receive a full message intact. For example, if I simply echo the same bytes to a second UART to look at them on my computer, even that is enough to screw it up so badly that it only catches about 1/4 of the bytes it should. And if I try to display the data on a little I2C screen, likewise.

I read that the UART buffer holds only 16 bytes which seems outrageously small, so I suppose my program is just not able to clear it fast enough? If so, then what is the point of reading data in, if I can't do anything with it? Surely the F103CBT6 is more capable than this??

For comparison, the equivalent program running on a 8MHz arduino is able to successfully read in the GPS messages approximately 50% of the time, while simultaneously updating the I2C screen ten times per second! I gotta be missing something...

fwiw here's the code that does read input ok, but anything more than this will cause it to stumble:

#include "mbed.h"

DigitalOut myled(PB_1);
Serial gps(PA_2, PA_3, 19200);
Serial pc(PB_10, PB_11, 9600);

const unsigned char UBX_HEADER[] = { 0xB5, 0x62 };

struct NAV_PVT {
    unsigned char cls;
    unsigned char id;
    unsigned short len;
    unsigned long iTOW;          // GPS time of week of the navigation epoch (ms)

    unsigned short year;         // Year (UTC)
    unsigned char month;         // Month, range 1..12 (UTC)
    unsigned char day;           // Day of month, range 1..31 (UTC)
    unsigned char hour;          // Hour of day, range 0..23 (UTC)
    unsigned char minute;        // Minute of hour, range 0..59 (UTC)
    unsigned char second;        // Seconds of minute, range 0..60 (UTC)
    char valid;                  // Validity Flags (see graphic below)
    unsigned long tAcc;          // Time accuracy estimate (UTC) (ns)
    long nano;                   // Fraction of second, range -1e9 .. 1e9 (UTC) (ns)
    unsigned char fixType;       // GNSSfix Type, range 0..5
    char flags;                  // Fix Status Flags
    unsigned char reserved1;     // reserved
    unsigned char numSV;         // Number of satellites used in Nav Solution

    long lon;                    // Longitude (deg)
    long lat;                    // Latitude (deg)
    long height;                 // Height above Ellipsoid (mm)
    long hMSL;                   // Height above mean sea level (mm)
    unsigned long hAcc;          // Horizontal Accuracy Estimate (mm)
    unsigned long vAcc;          // Vertical Accuracy Estimate (mm)

    long velN;                   // NED north velocity (mm/s)
    long velE;                   // NED east velocity (mm/s)
    long velD;                   // NED down velocity (mm/s)
    long gSpeed;                 // Ground Speed (2-D) (mm/s)
    long heading;                // Heading of motion 2-D (deg)
    unsigned long sAcc;          // Speed Accuracy Estimate
    unsigned long headingAcc;    // Heading Accuracy Estimate
    unsigned short pDOP;         // Position dilution of precision
    short reserved2;             // Reserved
    unsigned long reserved3;     // Reserved

    uint8_t dummy[8];
};

NAV_PVT pvt;

void calcChecksum(unsigned char* CK)
{
    memset(CK, 0, 2);
    for (int i = 0; i < (int)sizeof(NAV_PVT); i++) {
        CK[0] += ((unsigned char*)(&pvt))[i];
        CK[1] += CK[0];
    }
}

bool processGPS()
{
    static int fpos = 0;
    static unsigned char checksum[2];
    const int payloadSize = sizeof(NAV_PVT);

    while ( gps.readable() ) {
        char c0 = gps.getc();
        unsigned char c = *(unsigned char*)(&c0);
        //pc.putc(c); // this will cause about 3/4 of incoming bytes to be missed!
        if ( fpos < 2 ) {
            if ( c == UBX_HEADER[fpos] )
                fpos++;
            else
                fpos = 0;
        } else {
            if ( (fpos-2) < payloadSize )
                ((unsigned char*)(&pvt))[fpos-2] = c;

            fpos++;

            if ( fpos == (payloadSize+2) ) {
                calcChecksum(checksum);
            } else if ( fpos == (payloadSize+3) ) {
                if ( c != checksum[0] )
                    fpos = 0;
            } else if ( fpos == (payloadSize+4) ) {
                fpos = 0;
                if ( c == checksum[1] ) {
                    return true;
                }
            } else if ( fpos > (payloadSize+4) ) {
                fpos = 0;
            }
        }
    }
    return false;
}

int main()
{    
    // Turn LED off
    myled = 0;
    wait_ms(1000);

    while ( ! processGPS() );

    // Turn LED on to show a message was received
    myled = 1;
}

Hey Chris, are you still getting issues with this? A while back I had a similar issue when talking to an ESP8266. Have you tried using the BufferedSerial library yet? switching to that seemed to fix all of the UART woes I was having.

posted by Russell Bateman 26 Mar 2018

Hi Russell, after a while I decided to try buffering the incoming bytes myself and that did the trick. So I guess it was the same solution as you, but I rolled my own.

posted by iforce2d Chris 27 Mar 2018
Be the first to answer this question.