...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*/
}Message
Undocumented Timer Interrupt Feature, or Silicon Bug...
2004-10-11 by vaughan_anztec
Attachments
- No local attachments were found for this message.