USART dropping bytes. Guidance, anyone?

I’m using an Arduino as an AVR programmer for an ATMega328p chip. I want to send Serial data from the ATMega328p back to the Arduino, which the latter shall print to the Serial monitor. (Incidentally, I’d welcome advice on a preferable method of debugging.)

I’m just working with the ATMega328p’s USART. It seems to kind of work, but I’m trying to send 4-byte packages, and about one in six bytes gets dropped (way more than the 0.2% error rating which the datasheet indicated for a 96K baudrate). What should I be doing differently?

ATMega328p code:

#define F_CPU 16000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define baudrate 9600
#define UBRRVAL ((F_CPU/(baudrate*16UL)) - 1)

void init()
{
	// set baudrate
	UBRR0H = UBRRVAL >> 8; // set high byte for baudrate
	UBRR0L = (uint8_t) UBRRVAL; // set low byte for baudrate
	// enable transmitter only
	UCSR0B = (1<<TXEN0);
	// set frame format: asynch, no parity, 1 stop bit, 8-bit
	UCSR0C = (0<<UMSEL00) | (0<<UPM00) | (0<<USBS0) | (3<<UCSZ00);
}

void sendByte(uint8_t dataByte) // datasheet p 206
{
	// wait for empty transmit buffer
	while ( UCSR0A & (1<<UDRE0) == 0 );
	// put data into buffer (this effectively sends the data)
	UDR0 = dataByte;
}

int main()
{
	init();
	while (1) 
	{
		sendByte('h');
		sendByte('q');
		sendByte('!');
		sendByte('\n');
	}
	return 1;
}

Arduino Mega2560 code:

char buffer[20];

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop()
{
  char i = 0;
  
  if (Serial1.available()) {
    
    // input
    while(Serial1.available()) {
      buffer[i++] = Serial1.read();
    }
    // output
    for (char j = 0; j < i; j++) {
      Serial.print(buffer[j]);
    }    
    
  }
}

Sample output is as follows:

h!
hq!
q!
hq!
q!
hq!hq!
hq
hq!
hq
hq!

(Yes, I’m looking at the datasheet1 (it’s technically for 168p, but I can’t find anything more than a summary for the 328p).)

(The reason I’m working directly with the chip’s USART is I tried to #include Arduino library files in the code for my ATMega328p, but my attempts to transmit to the Arduino failed.)

1http://www.atmel.com/Images/doc8025.pdf

Does this…

ATMega328p code:

int main()
{
init();
while (1)
{
sendByte(‘h’);
sendByte(‘q’);
sendByte(’!’);
sendByte(’\n’);
_delay_ms( 1000 );
}
return 1;
}

…make any difference?

  char i = 0;

Char? i isn't a char. uint8_t or byte, but not char.

char buffer[20];

    while(Serial1.available()) {
      buffer[i++] = Serial1.read();
    }

No need to actually make sure that there is room in the array?

Apologies for the lateness of my reply. I took ill yesterday.

Does this… …make any difference?

It does. It looks as though the last two bytes of each packet are dropped consistently. (I don’t understand it.)

hqhqhqhqhqhqhqhq

Char? i isn’t a char.

It’s true that I’m counting, but unsigned byte, uint8_t, and char are fundamentally the same type. I used char because it’s part of ANSI C.

No need to actually make sure that there is room in the array?

A worthy question. I believe, however, that the code executes quickly enough that the array will never nearly fill at 9600 bps. (To check, I added some monitor output at the end of the output ‘for’ loop, and it appears that I don’t get more than a byte of serial input before the Arduino is ready to access it (speaking for this code at this baudrate).)

(3<<UCSZ00)

I think that should be

(3<<UCSZ0)

(3<<UCSZ00) I think that should be (3<<UCSZ0)

Just tried it but got the error “error: 'UCSZ0' was not declared in this scope.

This result corroborates what I understand from the datasheet.

Does the following code exhibit the same problem?

void loop()
{
    if (Serial1.available()) 
    {
          Serial.print((char)Serial1.read());    
    }
}

Yes: it only outputs the first two bytes of each packet.

Does this…

ATMega328p code:

int main()
{
init();
while (1)
{
sendByte(‘h’);
sendByte(‘q’);
_delay_ms( 1000 );
sendByte(’!’);
sendByte(’\n’);
_delay_ms( 1000 );
}
return 1;
}

…make any difference?

Yes. It appears to drop no bytes now. Any insight?

(It's good to see a transmission working, but as a solution, it's still rather kludgy and slows down data transmission too much.)

Vaselinessa:
Any insight?

@jraskell’s suggestion proves the problem is not with the Mega. My earlier suggestion proves the problem is not with the receiver (PC or Serial Monitor). What’s left?

(It’s good to see a transmission working, but as a solution,

That is not a solution. It’s a test to determine where the problem lies.

One more test and we’ll move on to a solution…

ATMega328p code:

int main()
{
init();
while (1)
{
sendByte(‘h’);
sendByte(‘q’);
sendByte(‘x’);
_delay_ms( 1000 );
sendByte(’!’);
sendByte(’\n’);
_delay_ms( 1000 );
}
return 1;
}

Thanks for sticking with me so long. The output is the same as before, albeit slower. I.e. 2 bytes per package, no 'x'.

hq!
hq!
hq!
...

The ATmega328 USART has, what is essentially, a two byte output queue. If two bytes are sent but not the third then your program is not correctly managing the two byte queue. In other words, the bug is in sendByte.

Like you, I thought equals-comparison has lower precedence than bitwise-and but that is not the case (search for “Operator precedence”)…

So, the solution is to add parenthesis…

void sendByte(uint8_t dataByte) // datasheet p 206
{
// wait for empty transmit buffer
while ( ( UCSR0A & (1<<UDRE0) ) == 0 );
// put data into buffer (this effectively sends the data)
UDR0 = dataByte;
}

Wow! Works! I’ll try to remember that equals comparison has higher operator precedence than bitwise operations.

Thank you.

Excellent. I will too. And, you are welcome.

I'll try to remember that equals comparison has higher operator precedence than bitwise operations.

Don't bother, if you're like me there's no room left to remember another rule, just use ()s every time there's even a chance of a precedence issue.


Rob