Electronic – UART sometimes missing first few characters on ATmega328P

atmegacmicrocontrollerserialuart

I wrote some code for the ATmega328P, (Packaged in a module that clones the Arduino Nano).

Normally when I send UART data from my computer to these micros, it is received just fine. However I have started a new project and I am getting intermittent characters missing from the beginning of a message.

The code I wrote thus far reads the incoming data and prints it back to the computer. I am trying to send "<A2>". Something like 80% of the messages are received and printed back fine. The rest are missing characters. Oddly, they are at the beginning of the message, giving me "A2>" or "2>"

I am using a baud of 115200, though 9600 and even 600 appear to give the same ratio of bad messages. I tried swapping the micro and there was no change. I'm only sending messages once every 5 seconds or so.

The micro is being powered from the same USB cable going to the laptop that the data is sent over.

Below are oscilloscope/logic analyzer captures of two signals, one that worked fine and another that was missing characters. The probe is placed right at the "RX" pin and grounded right at "GND".

Signal received as "<A2>" (correct):
Scope capture (good)

Signal received as only "2>" (incorrect):
Scope capture (Not good?)

Those two signals look awfully close.

Update: As user duskwuff mentioned, the UART signals are actually not approaching 0V for LOW.

I tried using nothing but the bare module itself (no protoboard, nothing), but I'm getting the same results.

To further diagnose, I tried manually writing to the RX pin. That makes the signal swing properly down to 0V.

Test code:

void setup()
{
    delay(500);
    pinMode(0, OUTPUT);

    while (true)
    {
        digitalWrite(0, HIGH);
        delay(2000);
        digitalWrite(0, LOW); //This reaches 0V
        delay(2000);
    }
}

Watching the RX line during code flash, the LOW is also about 1.5V.

Now I'm extra confused. How can I get the same result with multiple bare-modules, even brand-name ones? That makes me lean toward blaming software.

Arduino Nano Clone
Image from wikimedia.org

The purpose of my this code is to create a sort of protocol for received messages that processes them one at a time, even if multiples are sent all at once ("<A1><A2><A3>…") or if they are sent in only partial pieces ("<A" and then "2>").

const int pinBuzzer = A5;      // the number of pin for the buzzer

unsigned long T = 0; // "T" value


//max input size expected for a single incoming msg
#define INPUT_SIZE 32
char serialCommandstr[INPUT_SIZE + 1];
char serialCommand_singleChar; //single char for itterating through incoming WX
byte serialCommandindex = 0; //index of RX buffer for itterating
bool serialCommand_msgPending = false; // if msg exists in buffer without termination ('>');
bool serialCommand_ready = false; // if terminated msg ready (a '>' is received)


void setup()
{
    delay(500);
    Serial.begin(115200);

} // END Setup()


void loop()
{
    Serial.println("Waiting for input in the form of \"<XY>\"");
    Serial.println("X=[A/B], Y=[1~8]");


    serialCommand_ready = false;
    while (serialCommand_ready == false)
    {
        serialPreParse();
    }
    if (serialCommandstr[1] == 'A')
    {
        T = (serialCommandstr[2] - 1) - char('0');
        Serial.print("A, ");
    }

    Serial.print("T=");
    Serial.print(T);
    Serial.print("us...");

    //Beep to indicate waveform start
    tone(pinBuzzer, 1319, 50);
    delay(51);

}

void serialPreParse()
{
    if (Serial.available() && serialCommand_msgPending == false)
    {
        // NEW incoming message (buffer previously empty)

        //reinitialize
        serialCommandstr[0] = '\0';
        serialCommand_singleChar = '\0';
        serialCommandindex = 0;
        serialCommand_msgPending = true; // if msg received without termination
        serialCommand_ready = false; // if terminated msg ready

    }

    while (Serial.available())
    {
        if (serialCommandindex == INPUT_SIZE)
        {
            //maximum size of C string reached
            break;
        }
        serialCommand_singleChar = Serial.read();
        //strcat(serialCommand_singleChar, serialCommandstr);
        serialCommandstr[serialCommandindex] = serialCommand_singleChar;
        serialCommandindex++;
        serialCommandstr[serialCommandindex] = '\0'; //null-termination

        if (serialCommand_singleChar == '>')
        {
            //End of single msg.
            serialCommand_ready = true;
            serialCommand_msgPending = false;
            break;
        }

    }


    if (serialCommand_ready == true)
    {
        if (serialCommandstr[0] != '<')
        {
            Serial.println("\nIncomplete command!");
        }

        Serial.println();
        Serial.print("RX:\"");
        Serial.write(serialCommandstr);
        Serial.println("\"");

    }


}

Best Answer

There is a flaw in your handling of your serialCommand_msgPending flag.

Consider the following scenario:

  1. No data has arrived, you call serialPreParse()
  2. serialCommand_msgPending is false
  3. Serial.available() returns 0 in the first line of serialPreParse(), so that block is skipped (so serialCommand_msgPending is still false)
  4. A byte arrives on the serial port
  5. In the while loop, Serial.available() returns 1 (or more, but not a full message), so you read the byte and exit the while loop (no more bytes available) and return from serialPreParse()
  6. serialPreParse() is called again
  7. Serial.available() returns non-zero and serialCommand_msgPending is false, so first block is now executed, re-initialising your buffer and setting serialCommand_mgsPending to true.
  8. Subsequently, the while loop in serialPreParse() will receive the rest of the message, however you've lost the first byte(s) because they are overwritten by the subsequent bytes.

To fix it, you just need to move the check for serialCommand_msgPending and the re-initialisation code into the while loop:

void serialPreParse()
{
    while (Serial.available())
    {
         if (!serialCommand_msgPending)
         {
            // NEW incoming message (buffer previously empty)

            //reinitialize
            serialCommandstr[0] = '\0';
            serialCommand_singleChar = '\0';
            serialCommandindex = 0;
            serialCommand_msgPending = true; // if msg received without termination
            serialCommand_ready = false; // if terminated msg ready
        }

        if (serialCommandindex == INPUT_SIZE)
        {
            //maximum size of C string reached
            break;
        }
        serialCommand_singleChar = Serial.read();
        //strcat(serialCommand_singleChar, serialCommandstr);
        serialCommandstr[serialCommandindex] = serialCommand_singleChar;
        serialCommandindex++;
        serialCommandstr[serialCommandindex] = '\0'; //null-termination

        if (serialCommand_singleChar == '>')
        {
            //End of single msg.
            serialCommand_ready = true;
            serialCommand_msgPending = false;
            break;
        }
    }


    if (serialCommand_ready == true)
    {
        if (serialCommandstr[0] != '<')
        {
            Serial.println("\nIncomplete command!");
        }

        Serial.println();
        Serial.print("RX:\"");
        Serial.write(serialCommandstr);
        Serial.println("\"");

    }


}