0
\$\begingroup\$

I am trying to work out USART ISR's on the ATmega 328P. So far I have got the USART rx and tx working. I have come across a strange issue when it comes to the UART ISR's, though. The ISR seems to override the delays, causing the code in my main loop to run without a pause.

Consider the following code (which is complete and functioning for easy troubleshooting):

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

#define FOSC            16000000
#define BAUD            9600
#define BAUD_GEN_VAL    (((FOSC/BAUD)/16)-1)

void uart_tx( unsigned char data );
unsigned char uart_rx( void );

void main(void)
{   
    sei();  //enable interrupts globally
    UBRR0H = (BAUD_GEN_VAL>>8);
    UBRR0L = BAUD_GEN_VAL;                  //set baud rate
    UCSR0B |= (1<<TXEN0) | (1<<RXEN0);    //enable transmitter and receiver
    //UCSR0B |= (1<<TXCIE0);              //Enable interrupt on TX (serial)
    UCSR0C |= (1<<UCSZ00) | (1<<UCSZ01);  //8 bit data

    while (1)
    {
        uart_tx('A');
        _delay_ms(2500);
    }
}

ISR(__vector_USART_TXC)
{
     //blink an led here or something similar
}

void uart_tx( unsigned char data )
{
    while ( !( UCSR0A & (1<<UDRE0)) );   //Wait for empty transmit buffer

    UDR0 = data;    //Put data into buffer, sends the data
}

This code works perfectly, printing "A" to a serial monitor every 2.5 seconds. However, if I un-comment the line UCSR0B |= (1<<TXCIE0); — which enables the interrupt on transmission completion — the microcontroller spews "A"s out so fast that the serial monitor can't even keep up.

I can't understand how this is happening, however. It is as if the ISR did nothing but override the delay! Can anyone shed light on this?

\$\endgroup\$
  • \$\begingroup\$ I can just guess that the AVR makes resets, can you add toggling a PORT pin before entering main? \$\endgroup\$ – Andy Jan 30 at 14:49
  • \$\begingroup\$ Btw, how have you defined __vector_USART_TXC ? \$\endgroup\$ – Andy Jan 30 at 14:52
  • \$\begingroup\$ With a recent avr-libc, the ISRs should be USART_RX_vect and USART_TX_vect if you enable the interrupt but don't provide the appropriate ISR, the AVR makes a reset \$\endgroup\$ – Andy Jan 30 at 14:56
  • \$\begingroup\$ @Andy I tried your first suggestion, and I didn't notice any glow on the LED, but it may be too fast to even produce a faded glow. On your second comment: I didn't define __vector_USART_TXC. I'll definitely try that.. \$\endgroup\$ – hat Jan 30 at 14:59
  • \$\begingroup\$ How do you clear the TXC flag? Is it done automatically by the AVR itself? \$\endgroup\$ – Rohat Kılıç Jan 30 at 15:00
-1
\$\begingroup\$

The ISR is incorrectly defined, the ISR names must be identical how AVR-LIBC defines them. So as it is now, every time your code sends the letter 'A' to data register, it is transmitted out and will cause a transmit complete interrupt, which is undefined and AVR ends up running main again to send another letter and will loop this forever.

Another issue might be that you define the FOSC after including delay.h, so it is possible that delay.h does not know the clock rate so delay times are incorrect.

\$\endgroup\$
  • \$\begingroup\$ Fosc is passed to avr-gcc as a switch in the compile and link commands. Don't ask me how it works, I'm still using someone else's build template :). One day I'll learn avr-gcc properly. \$\endgroup\$ – hat Jan 30 at 19:35
  • \$\begingroup\$ Anything new in this answer to a already solved question (See comments) \$\endgroup\$ – Andy Feb 5 at 6:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.