Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Message

Undocumented Timer Interrupt Feature, or Silicon Bug...

2004-10-11 by vaughan_anztec

...Or am I doing something wrong?
 
I've been implementing a bit bashed serial port on an LPC2114. The 
code relied upon the timer module's input capture facility to 
generate an interrupt whenever there was a 1->0 or 0->1 transition on 
the RS232 receive line (buffered by  a Max232 equivalent). This 
loaded a match register (MR1) with a value equal to half a bit time 
so the incoming bit could be sampled at its mid-point. Transmission 
used another match register (MR2) to generate appropriately timed 
interrupts to send bits as required. Another match register (MR0) in 
the same timer was also used to generate a 1ms tick. 
 
When I was testing the code's full duplex operation, I found that the 
1ms tick or the transmission tick interrupt would sometimes be missed 
(refer to posts 3510 and 3514). Robert Adsett suggested not servicing 
multiple events in the ISR. This did not fix the problem. The only 
way I've found to work around the problem of missing interrupts is to 
stop the counter while I process any pending match or capture 
interrupts. Is this an undocumented feature or am I doing something 
wrong? 
 
Here's some sample code that demonstrates the problem: 
 
Code to initialise the timer and input capture pin (timer register 
access macros etc. taken straight from Keil's lpc21xx.h header file. 
Compiler used is ARM's RVCT 2.1):
void InitTimer0(void)
{
    /*Set T0 up to use the FIQ*/
    VICIntSelect = 
      VICIntSelect | 1 << TIMER0_ID; /*FIQ interrupt source selected*/
    VICIntEnable = 
      VICIntEnable | 1 << TIMER0_ID; /*Interrupt enabled*/
 
    /*Set up T0*/
    TIMER0_TCR = 0x0UL;       /*Disable T0*/
    TIMER0_TC  = 0x0UL;       /*Clear current counter value*/
    TIMER0_PR  = 59UL;        /*Set counter to have 1us resolution*/
    TIMER0_PC  = 0UL;         /*Clear precount value*/
    TIMER0_MR0 = 1000UL;      /*First MR0 match @ 1ms*/
    TIMER0_MR2 = 52UL;        /*First MR2 match @ 52us*/
 
    TIMER0_MCR = 0x49UL;      /*Interrupt on MR0, MR1 and MR2 match*/ 
    
 
    /*Set up T0 input capture*/
    PINSEL1 |= 0x00800000UL;  /*Pin 0.27 = capture input 0.1*/
    TIMER0_CCR |= 0x0038UL;   /*Capture and interrupt on falling
                                and rising edge*/ 
    
    /*Finally, enable T0*/
    TIMER0_TCR = 0x01UL;
}    
 
 
 
Timer 0 interrupt code that misses MR0 or MR2 matches when there are 
many bit transitions on P0.27. 
#define TIMER0_IR_CR1 0x20
__irq void TIMER0_IRQHandler( void )
{
    /*Look at one source of interrupt at a time as per Robert
      Adsett's suggestion*/
    if((TIMER0_IR & TIMER0_IR_CR1) != 0)
    {
        /*Capture interrupt - generated on positive or negative 
          edge of P0.27
          Whenever an edge occurs, set MR1 to interrupt in 26us*/
        TIMER0_IR = TIMER0_IR_CR1;
        TIMER0_MR1 =  TIMER0_CR1 + 26UL;
    }
    else if((TIMER0_IR & TIMER0_IR_MR1) != 0)
    {
        /*MR1 occurs 26us after CR1 edge, or if no edges detected, 
          every 52us*/
        TIMER0_IR = TIMER0_IR_MR1;
        TIMER0_MR1 = TIMER0_TC + 52UL;
    }
    else if((TIMER0_IR & TIMER0_IR_MR2) != 0)
    {
        /*MR2 is a free running 52us tick
          Clear current interrupt flag and set when next match is 
          to occur*/
        TIMER0_IR = TIMER0_IR_MR2;
        /*Wiggle a bit so DSO can be used to seen when match 
          interrupt is lost (no output for 2^32us!)*/
        IOSET1 = 1UL << 22;
        TIMER0_MR2 = TIMER0_TC + 52UL;
        IOCLR1 = 1UL << 22;
    }
    else if((TIMER0_IR & TIMER0_IR_MR0) != 0)
    {
        /*MR0 is a free running 1ms tick
          Clear current interrupt flag and set when next match 
          is to occur*/
        TIMER0_IR = TIMER0_IR_MR0;
        /*Wiggle a bit so DSO can be used to seen when match 
          interrupt is lost (no output for 2^32us!)*/
        IOSET0 = 1UL << 15;
        TIMER0_MR0 = TIMER0_TC + 1000UL;
        IOCLR0 = 1UL << 15;
    }
}
 

Timer 0 interrupt code showing work-around to prevent loss of match 
register interrupts
#define TIMER0_IR_CR1 0x20
__irq void TIMER0_IRQHandler( void )
{
    unsigned long TimerIR;
    
    TIMER0_TCR = 0; /*Stop the clock before doing anything*/
    while((TimerIR = TIMER0_IR) != 0)
    {
        if((TimerIR & TIMER0_IR_CR1) != 0)
        {
            /*Capture interrupt - generated on positive or negative 
              edge of P0.27
              Whenever an edge occurs, set MR1 to interrupt in 26us*/
            TIMER0_IR = TIMER0_IR_CR1;
            TIMER0_MR1 =  TIMER0_CR1 + 26UL;
        }
        if((TimerIR & TIMER0_IR_MR1) != 0)
        {
            /*MR1 occurs 26us after CR1 edge, or if no edges 
              detected, every 52us*/
            TIMER0_IR = TIMER0_IR_MR1;
            TIMER0_MR1 = TIMER0_TC + 52UL;
        }
        if((TimerIR & TIMER0_IR_MR2) != 0)
        {
            TIMER0_IR = TIMER0_IR_MR2;
            /*MR2 is a free running 52us tick
              Clear current interrupt flag and set when next match 
              is to occur*/
            TIMER0_IR = TIMER0_IR_MR2;
            /*Wiggle a bit so DSO can be used to seen if match 
              interrupt is lost*/
            IOSET1 = 1UL << 22;
            TIMER0_MR2 = TIMER0_TC + 52UL;
            IOCLR1 = 1UL << 22;
        }
        if((TimerIR & TIMER0_IR_MR0) != 0)
        {
            /*MR0 is a free running 1ms tick
              Clear current interrupt flag and set when next match 
              is to occur*/
            TIMER0_IR = TIMER0_IR_MR0;
            /*Wiggle a bit so DSO can be used to seen if match 
              interrupt is lost*/
            IOSET0 = 1UL << 15;
            TIMER0_MR0 = TIMER0_TC + 1000UL;
            IOCLR0 = 1UL << 15;
        }
    }
    
    /*When we get here, all interrupt sources have been cleared*/
    TIMER0_TC++;    /*Advance clock to stop double firing of 
                      previous match(es)*/
    TIMER0_TCR = 1; /*Restart the clock*/
}

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.