# AVR ISR causes unexpected behavior

Using avr-gcc 4.8.0 and avr-libc 1.8.0 on Arch Linux. Trying to get UART RX interrupts working on an ATtiny2313 with this simple code which should echo the received byte:

#define F_CPU 14745600

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

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

int main(){
DDRD |= _BV(PD6);

UCSRB = (1 << RXEN) | (1 << TXEN);
UCSRC = (1 << UCSZ0) | (1 << UCSZ1);

UBRRH = (BAUD_PRESCALE >> 8);
UBRRL = BAUD_PRESCALE;

UCSRB |= (1 << RXCIE);
sei();

for (;;){
PORTD ^= _BV(PD6);
_delay_ms(50);
}

return 0;
}

ISR(USART_RX_vect){
char b;
b = UDR;
UDR = b;
}


I upload this with avrdude and the LED fails to pulse. If I comment out the ISR, it works as I'd expect it to. UART polling also works normally, so I'm quite sure that it is configured correctly. I've been able to reproduce this with an ATmega162 and an ATmega328p.

Other strange behavior: if I compile without optimization, the LED will pulse until I send a byte, then it stops pulsing.

I've really got no idea where the problem lies as every tutorial I've seen uses almost this exact code.

• Do you need to clear the interrupt flag inside the ISR? You might be stuck in an interrupt loop. – Samuel Oct 16 '13 at 2:45
• @Samuel: The interrupt flag is automatically cleared when UDR is read. However, maybe something with the optimization goes wrong and UDR is not read? Andrew: What happens if you comment out "UDR = b"? – Rev1.0 Oct 16 '13 at 7:47
• Check avr-gcc version 4.7.2. I have trouble finding a reference for latest stable version of avr-gcc, but most sources I checked are still on 4.7.2. – jippie Oct 16 '13 at 8:44
• @Rev1.0: Same thing. I can comment out all three lines inside the ISR, but as long as there's an ISR defined, the problem still occurs. – Andrew Oct 16 '13 at 12:50
• @jippie: I was afraid of that. Gonna try to compile 4.7.2 and see what happens. – Andrew Oct 16 '13 at 12:51

I hate myself for this. How I was compiling:

avr-gcc -o build/src/main.o -c -Wall -g -Os -mmcu=attiny2313 src/main.c
avr-gcc -o build/main.elf build/src/main.o


How I should have been:

avr-gcc -o build/src/main.o -c -Wall -Os -mmcu=attiny2313 -std=c99 -ffunction-sections -fdata-sections src/main.c
avr-gcc -o build/main.elf -mmcu=attiny2313 build/src/main.o


The actual culprit was not passing the -mmcu flag to the linker, but adding -ffunction-sections and -fdata-sections also cured some weird problems I was having when using external header files.

• GLad to hear you fixed it. But I would investigate why using "-ffunction-sections and -fdata-sections" just "cured some weird [other] problems". Maybe due to code size? Because the linker is able to remove unused functions (sections) this way. – Rev1.0 Oct 16 '13 at 19:14