Reputation: 376
Trying to get an ATmega162 USART up and running. This code does exactly what I expect it to:
#define F_CPU 14745600UL
#define UBRR_1 F_CPU / 16 / 9600 - 1
#define UBRR_2 F_CPU / 16 / 31250 - 1
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
int main(){
uint16_t ubrr1 = UBRR_1;
UBRR0H = (uint8_t)(ubrr1 >> 8);
UBRR0L = (uint8_t)ubrr1;
UCSR0B = _BV(TXEN0);
UCSR0C = _BV(URSEL0) | _BV(UCSZ00) | _BV(UCSZ01);
uint16_t ubrr2 = UBRR_2;
UBRR1H = (uint8_t)(ubrr2 >> 8);
UBRR1L = (uint8_t)ubrr2;
UCSR1B = _BV(RXEN1);
UCSR1C = _BV(URSEL1) | _BV(UCSZ10) | _BV(UCSZ11);
DDRB = _BV(PB0) | _BV(PB1);
PORTB |= _BV(PB0);
while (1){
PORTB ^= _BV(PB0);
_delay_ms(50);
// byte received on usart 1
if ((UCSR1A & _BV(RXC1)) != 0){
// usart 0 ready to write
if ((UCSR0A & _BV(UDRE0)) != 0){
uint8_t b = UDR1;
UDR0 = b;
}
}
}
return 0;
}
That is, initializes the two USARTs at different baud rates, reads from USART1 and writes to USART0. Works great. Yes, I know that _delay_ms() is messing with the timing, but it works fine for this example. Now, as soon as I enable the RX interrupt on USART1 and add the appropriate vector, the main loop stops running (the LED isn't blinking, at least):
#define F_CPU 14745600UL
#define UBRR_1 F_CPU / 16 / 9600 - 1
#define UBRR_2 F_CPU / 16 / 31250 - 1
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
int main(){
uint16_t ubrr1 = UBRR_1;
UBRR0H = (uint8_t)(ubrr1 >> 8);
UBRR0L = (uint8_t)ubrr1;
UCSR0B = _BV(TXEN0);
UCSR0C = _BV(URSEL0) | _BV(UCSZ00) | _BV(UCSZ01);
uint16_t ubrr2 = UBRR_2;
UBRR1H = (uint8_t)(ubrr2 >> 8);
UBRR1L = (uint8_t)ubrr2;
UCSR1B = _BV(RXEN1);
UCSR1C = _BV(URSEL1) | _BV(UCSZ10) | _BV(UCSZ11);
DDRB = _BV(PB0) | _BV(PB1);
// enable usart1 rx interrupt
UCSR1B |= _BV(RXCIE1);
PORTB |= _BV(PB0);
// enable interrupts
sei();
while (1){
PORTB ^= _BV(PB0);
_delay_ms(50);
}
return 0;
}
ISR(USART1_RXC_vect){
uint8_t byte = UDR1;
if ((UCSR0A & _BV(UDRE0)) != 0){
UDR0 = byte;
}
}
The weirdest part is that it's not the sei();
and UCSR1B |= _BV(RXCIE1);
lines that make the program stop working -- it's the existence of the ISR. As soon as I comment out that function, the main loop executes normally. Did I miss a flag somewhere?
Upvotes: 1
Views: 1925
Reputation: 328
It's possible this has been caused by the M161C fuse bit (in the extended fuse byte) becoming programmed. This puts the ATmega162 into ATmega161 compatibility mode which causes the device to have a different layout of the interrupt vector table (which the compiler won't know about.) See enter link description here Page 57 for the details. You could test this by compiling with -mmcu=atmega161 and seeing if it fixes the problem.
The other thing which would cause similar behaviour is if this code is run on an (almost identical-looking) ATmega16 instead of an ATmega162 as the UDR1 register is in a different place in the IO register map, meaning that the RXC interrupt flag would never get cleared and the handler would re-enter forever. You can check the register values the compiler is using by disassembling with avr-objdump.
Upvotes: 0