Interrupt driven USART only transmitting one character and garbage afterwards

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Hi all,

Using an atmega644pa, 16Mhz, 5V

 

I followed the interrupt driven USART receive tutorial which seems to be working for one character at a time. @ 115200 baud rate.

The transmit function works perfectly.

 

But I am having trouble receiving more than 3 transmitted characters from a string. For example: 

It works with 1 character at a time:

I am using the arduino serial monitor to send and receive characters, could that be the problem?

but when I want to print "three" I get some of the first characters and some garbage after it:

 

void init_USART0() {
  unsigned int number = (F_CPU)/(16*(BAUD0)-1);
  UBRR0H = (unsigned char)(number>>8);
  UBRR0L = (unsigned char)number;
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1 << RXCIE0);// Enable receiver and transmitter
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);// Set frame format: 8data, 2stop bit
}

void transmit_USART0(char c) {
  while (!(UCSR0A & (1<<UDRE0))); //Wait for empty transmit buffer
  UDR0 = c;
}

unsigned char USART0_Receive(void){
  while (!(UCSR0A & (1<<RXC0)));
  return UDR0;
}

ISR(USART0_RX_vect){
   transmit_USART0(USART0_Receive());
}

int main(){
  sei();
  init_USART0();
}

 

 

This topic has a solution.
Last Edited: Mon. Jul 8, 2019 - 08:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joshagirgis wrote:

int main(){
  sei();
  init_USART0();
}

 

You don't want main() to end, and add a loop forever.

Also, best to enable interrupts after initializing.

 

int main(){
  init_USART0();
  sei();
  while(1);  // wait forever
  
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the edits:

Same issue persists:

 

Sending "Three" yields: "TK⸮⸮" 

Last Edited: Mon. Jul 8, 2019 - 07:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interrupts and polling??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

polling??

Not sure what you mean.  I apologize for my ignorance!

I am using an interrupt instead of just synchronously grabbing the characters.

Last Edited: Mon. Jul 8, 2019 - 07:38 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joshagirgis wrote:

unsigned int number = (F_CPU)/(16*(BAUD0)-1);

 

This should be:

unsigned int number = F_CPU/16/BAUD0 - 1;

 

Even better, set the U2X bit and change 16 to 8 in the calculation.

 

--Mike

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joshagirgis wrote:

unsigned char USART0_Receive(void){
  while (!(UCSR0A & (1<<RXC0)));
  return UDR0;
}

Should be:

joshagirgis wrote:

unsigned char USART0_Receive(void){
//  while (!(UCSR0A & (1<<RXC0)));
  return UDR0;
}

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avr-mike wrote:

joshagirgis wrote:

unsigned int number = (F_CPU)/(16*(BAUD0)-1);

 

This should be:

unsigned int number = F_CPU/16/BAUD0 - 1;

 

Even better, set the U2X bit and change 16 to 8 in the calculation.

 

--Mike

 

 

F_CPU/16/BAUD0 - 1 is incorrect for some reason.

 

I can only transmit at 115200 baud for  

 

#define BAUD0 115200

number = (F_CPU)/(16*(BAUD0)-1);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


This added no effect on the issue :/ 

I think with the interrupt that while loop is redundant but does not repair the issue.

 

This is with a different terminal program:

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
unsigned int number = F_CPU/16/BAUD0 - 1;

 

Even better, set the U2X bit and change 16 to 8 in the calculation.

I set U2X and changed it to 8. And that fixed it.

Oddly enough just using F_CPU/16/BAUD0 - 1; and just asynch normal mode didn't work, it only worked with (F_CPU)/(16*(BAUD0)-1); but not for receiving, hence this post.

I suppose the UART wasn't working fast enough or at a close enough baud??\

anybody know why?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joshagirgis wrote:

ISR(USART0_RX_vect){
   transmit_USART0(USART0_Receive());
}

This ISR() will wait for the transmit to finish before it can service the next received char, as you have a blocking transmit routine.

Keep your ISR() short and fast, do not wait or delay.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I posted this already quite recently.

If you want a macro for calculating UBBR then why not just take the one form util/setbaud.h, which has rounding up/down.

(Or even you could actually use setbaud.h)

 

U2X = 0

#define UBRR_VALUE (((F_CPU) + 8UL * (BAUD)) / (16UL * (BAUD)) -1UL)

or U2X = 1

#define UBRR_VALUE (((F_CPU) + 4UL * (BAUD)) / (8UL * (BAUD)) -1UL)

 

Your  macro happens to give 8 (but can give non optimal value for other baud rates).

If you use F_CPU/16/BAUD0 - 1 (ie. withour rounding up/down) you get 7.

With rounding you get 8.

8 is the optimal value for UBBR but it's not very optimal for 16MHz without U2X, it has 3.5% systematic error, that's getting borderline.

With U2X and UBBR=16 the error is 2.1%, more likely to work.

This is all in the datasheet (I'm looking at mega328 datasheet, assuming it is the same for your chip).