carduinoavrarduino-unoatmega

Arduino UNO Hangs on USART0 RX ISR


I have a simple example where I configure the UART0 to interrupt on RX and TX.

The TX interrupt works great, but the RX interrupt handler hangs no matter what I put in it.

If I remove the interrupt handler then it correctly crashes. If I turn off the interrupts then it doesn't hang anymore. As soon as I turn on the interrupt and add an ISR, then it hangs when receiving a character. The watchdog then resets the device.

I examined the Arduino Core implementation and can't see anything they're doing that I'm not.

Any ideas?

/* USART Interrupts */
#define USART_RX_vect       __vector_18
#define USART_UDRE_vect     __vector_19
#define USART_TX_vect       __vector_20


void usart_init_hardware(USART_CONFIG_T cfg)
{
    usart_set_mode(cfg.mode);
    usart_set_baud(cfg.baud);
    usart_set_parity(cfg.parity);
    usart_set_stop_bits(cfg.stop_bits);
    usart_set_character_size(cfg.character_size);

    if (USART_MODE_SYNCHRONOUS == cfg.mode) {
        /* This bit is used for synchronous mode only.
         * - ATmega328P Datasheet page 162 */
        usart_set_clock_polarity(cfg.clock_polarity);
    }

    /* Enable interrupts on RX and TX */
    UCSR0B.bits.RXCIEn = TRUE;
    UCSR0B.bits.TXCIEn = TRUE;
    UCSR0B.bits.UDRIEn = FALSE;

    /* Enable TX and RX */
    UCSR0B.bits.RXENn = TRUE;
    UCSR0B.bits.TXENn = TRUE;
}


ISR(USART_RX_vect)
{
    volatile U8_T c;

    /* Clear interrupt flag */
    c = UDR0.byte;
}

I tried removing everything from the ISR, which should cause a hang as the interrupt gets called repeatedly.

I then added the UDR0 retrieval back into the ISR, which should cure the hang as it disables the interrupt flag.

I tried manually disabling the interrupt flag.

Nothing appears to cure the hang.

UDR0 is a union which is mapped to 0xC6

typedef union {
    struct {
        VBOOL_T bit0 : 1;
        VBOOL_T bit1 : 1;
        VBOOL_T bit2 : 1;
        VBOOL_T bit3 : 1;
        VBOOL_T bit4 : 1;
        VBOOL_T bit5 : 1;
        VBOOL_T bit6 : 1;
        VBOOL_T bit7 : 1;
    } bits;
    VU8_T byte;
} REGISTER_T;

extern volatile REGISTER_T UDR0;

and

SECTIONS
{
  /* USART */
  UCSR0A        = 0xC0;
  UCSR0B        = 0xC1;
  UCSR0C        = 0xC2;
  UBRR0         = 0xC4;
  UDR0          = 0xC6;
  ...

Solution

  • The problem was that I defined my ISR in terms of interrupt instead of signal. I'm still researching what the implications of this difference are, but as far as I can tell, it's related to disabling global interrupts on entry.

    #define ISR(vect) \
        void vect(void) __attribute__((interrupt, used, externally_visible)); \
        void vect(void)
    

    should have been

    #define ISR(vect) \
        void vect(void) __attribute__((signal, used, externally_visible)); \
        void vect(void)