Go Down

Topic: USART dropping bytes. Guidance, anyone? (Read 3678 times) previous topic - next topic

Vaselinessa

May 29, 2012, 07:29 am Last Edit: May 30, 2012, 07:30 pm by Vaselinessa Reason: 1
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:

Code: [Select]
#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:

Code: [Select]
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:

Code: [Select]
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

Coding Badly


Does this...

Quote
ATMega328p code:

[font=Courier New]int main()
{
   init();
   while (1)
   {
      sendByte('h');
      sendByte('q');
      sendByte('!');
      sendByte('\n');
      _delay_ms( 1000 );
   }
   return 1;
}[/font]


...make any difference?

PaulS

Code: [Select]
  char i = 0;
Char? i isn't a char. uint8_t or byte, but not char.

Code: [Select]
char buffer[20];

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

No need to actually make sure that there is room in the array?
The art of getting good answers lies in asking good questions.

Vaselinessa

#3
May 30, 2012, 06:45 pm Last Edit: May 30, 2012, 07:37 pm by Vaselinessa Reason: 1
Apologies for the lateness of my reply. I took ill yesterday.

1.
Quote
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.)
Code: [Select]
hqhqhqhqhqhqhqhq

2.
Quote
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.

3.
Quote
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).)

jraskell

(3<<UCSZ00)

I think that should be

(3<<UCSZ0)

Vaselinessa

Quote
(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.

jraskell

Does the following code exhibit the same problem?

Code: [Select]

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

Vaselinessa

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

Coding Badly

Does this...

Quote
ATMega328p code:

[font=Courier New]int main()
{
   init();
   while (1)
   {
      sendByte('h');
      sendByte('q');
      _delay_ms( 1000 );
      sendByte('!');
      sendByte('\n');
      _delay_ms( 1000 );
   }
   return 1;
}[/font]


...make any difference?

Vaselinessa

#9
May 30, 2012, 11:25 pm Last Edit: May 30, 2012, 11:27 pm by Vaselinessa Reason: 1
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.)

Coding Badly

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?

Quote
(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...

Quote
ATMega328p code:

[font=Courier New]int main()
{
   init();
   while (1)
   {
      sendByte('h');
      sendByte('q');
      sendByte('x');
      _delay_ms( 1000 );
      sendByte('!');
      sendByte('\n');
      _delay_ms( 1000 );
   }
   return 1;
}[/font]


Vaselinessa

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

Code: [Select]
hq!
hq!
hq!
...

Coding Badly

#12
May 31, 2012, 04:53 am Last Edit: May 31, 2012, 06:51 am by Coding Badly Reason: 1

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")...
http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B

So, the solution is to add parenthesis...

[font=Courier New]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;
}[/font]



Vaselinessa

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

Thank you.

Coding Badly


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

Go Up