LPC2294 Uart interrupt problem

Hello there,

I am having this strange problem where once an Interrrupt happens, interrupts are disabled forever. After the first occurance of the interrupt, even though interrupts are happening and the processor knows about it, because interrupts stays disabled in CPSR, it never goes back to the ISR. I DO NOT need to nest the interrupts. I actually prefer not to do that. I have found some example interrupt nesting code in assembly. I updated my crt.s file with those updates and that didn’t help either.

Ok… the basic thing I am trying to achieve is the following. I have a main loop that’s being triggered by a timer interrupt. I have configured Timer1 to interrupt at every 10msec for this purpose. Then I am receiving data in Uart0. Uart0 raises an interrupt after it receives one byte of data. My plan was to send this byte out through Uart1. But for now I am just doing some dummy reads to clear the interrupt.

I have a very simple main loop where it toggles a bit to let me know if it’s running. Now if I am not sending any data to the uart, the main loop is running smoothly which tells me that timer1 interrupt is happening periodically. But the moment I start sending data, I see that the timer1 interrupt has stopped working. After stepping through the code what I have noticed is that when it comes out of the Uart interrupt, for some reason it leaves the IRQ bit in the CPSR register disabled. How do I stop this from happening? I don’t think I am doing anything special with Uart interrupt. It’s working fine with the timer interrupt, then why does it mess up in case of timer interrupt.

void Uart0_ISR(void)
{
	if(toggle) // toggle a bit 
		IOSET0 = 0x00000008;
	else
		IOCLR0 = 0x00000008;
	toggle = ~toggle;
	while(!(U0LSR & 0x01));
       data = U0RBR; // dummy read
       data = U0IIR;   // dummy read
}	

void Uart0_Driver_init(Uart0_Driver *this)//, FrequencyFormatter *FF_handle)
{
    //this->rx_buffer = rx_buffer;
    //this->rx_buffer_size = rx_buffer_size;
    Uint8 q;
    // set the pin
//    PINSEL0 = PINSEL0 | PINSEL0_08_UART1_TX |
//              PINSEL0_09_UART1_RX;

    PINSEL0 = PINSEL0 | PINSEL0_00_UART0_TX |
              PINSEL0_01_UART0_RX ;
    
    // Set the line control registers
    U0LCR = U0LCR_WORD_LENGTH_SELECT_8  |
            U0LCR_1_STOP_BIT            |
            U0LCR_PARITY_DISABLE        | // Parity = None.
            U0LCR_BREAK_CONTROL_DISABLE |
            U0LCR_DIVISOR_LATCH_ACCESS_ENABLE;

    // Now set the baud rate.
    U0DLL = (60000000/9600/16) & 0xFF;             // should be 0x86 for 9600
    U0DLM = ((60000000/9600/16)>>8) & 0xFF;        // should be 0x01 for 9600
    
    //Configure the FIFO first
    U0FCR = U0FCR_FIFO_ENABLE       |
            U0FCR_RX_FIFO_RESET     |
            U0FCR_RX_TRIGGER_LEVEL_01;


    //Disable the divisor latch access
    U0LCR = U0LCR_WORD_LENGTH_SELECT_8  |
            U0LCR_1_STOP_BIT            |
            U0LCR_PARITY_DISABLE        | // Parity = None.
            U0LCR_BREAK_CONTROL_DISABLE |
            U0LCR_DIVISOR_LATCH_ACCESS_DISABLE;

    q = U0RBR; 
    
    VICIntEnable = ENABLE_UART0_INTERRUPT;
    VICVectCntl1 = THIS_VIRQ_IS_ENABLED |
                   UART0_INTERRUPT;
    VICVectAddr1 = (unsigned long)Uart0_ISR;

    
    // UART1 interrupt handling
    U0IER = ENABLE_U0IER_RDA_INTERRUPT;
  
    return;
}

Code for another ISR is as follows:

void mainLoopTimerInit(void)
{
    // Configure for Timer 0 Match 0
    PINSEL0 = PINSEL0 | PINSEL0_12_MAT_1_0;

    // Timer Control Register
    T1TCR = TCR_COUNTER_RESET;

    // Match register values
    T1MR0 = 600000;
    //    T0MR1 = 0;
    //    T0MR2 = 0;
    //    T0MR3 = 0;

    // Match control register
    T1MCR = MCR_INTERRUPT_MR0 |
            MCR_RESET_MR0;

    // Capture control register
    //T1CCR = CCR_CAPTURE_ON_CAP0_FALLING_EDGE |
    //        CCR_CAPTURE_ON_CAP0_INTERRRUPT;

    VICVectAddr2 = (unsigned long)timer_1_match_0_ISR;
    VICVectCntl2 = THIS_VIRQ_IS_ENABLED |
                   TIMER1_INTERRUPT;
    VICIntEnable = ENABLE_TIMER1_INTERRUPT;

    T1TCR = TCR_COUNTER_ENABLE;
}


void __attribute__((interrupt("IRQ"))) timer_1_match_0_ISR(void)
{
	main_loop_enable = 1;
       T1IR        = IR_RESET_MR0_INTERRUPT  ;     // Clear interrupt flag
       VICVectAddr = 0;                            // Acknowledge Interrupt 
}

Don’t you need

__attribute__((interrupt("IRQ")))
``` and ```
VICVectAddr = 0;

in your Uart0_ISR as well?

A past post from somebody named Magnus helped me resolve this issue.

When compiler sees attribute ((interrupt(“IRQ”))) specifier, it adds appropriate to handle the entering and exiting the interrupt. It seemed like in my case, for some reason, the compiler wasn’t generating the proper code. I removed this specifier and replaced it with attribute ((naked)). If used this attribute, the compiler doesn’t add code to handle entering the exiting the interrupt. I used the following code in my crt.s file for interrupt handing.

IRQHandler:
/* Adjust link register and save r0 and lr on IRQ stack */
                sub      lr, lr, #4
                stmfd    sp!, {r0, lr}
/*− SPSR needs to be saved on IRQ stack for nested interrupt */
                mrs      r14, SPSR
                stmfd    sp!, {r14}
/* Read the AIC Interrupt Vector register to clear the interrupt and fetch IRQ handler address */
                ldr      r14, =VICVECTADDR
                ldr      r0 , [r14]
/* Enable IRQ interrupts and switch to Supervisor mode */
                msr      CPSR_c, #ARM_MODE_SVC
/* Save scratch/used registers and LR in Supervisor Stack */
                stmfd    sp!, {r0-r12, r14} 
/* Branch to the C language routine pointed by the AIC_IVR */
                mov      r14, pc
                bx       r0
/* Restore scratch/used registers and LR from Supervisor Stack */
                ldmia    sp!, {r0-r12,r14}
/* Disable IRQ interrupts and switch back to IRQ mode */
                msr      CPSR_c, #ARM_MODE_IRQ | I_BIT
/* Mark the End of Interrupt on the AIC */
                ldr      r14, =VICVECTADDR
                str      r14, [r14, #0]
/* Restore SPSR_irq and r0 from IRQ stack */
                ldmia    sp!, {r14}
                msr      SPSR_cxsf, r14
/* pop r0 and lr from IRQ stack and restore the SPSR_irq; this returns from IRQ interrupt */
                ldmia    sp!, {r0, pc}^

This took care of my problem.