Forums

LPC2106 unable to get Timer Interrupt

Started by abufadel September 8, 2007
Greetings,
I am trying to get the Timers 0 and 1 to work with interrupts using
the Olimex 2106 board, but cannot get the interrupts to work. I am
following the example code given in the CrossStudio help, which I
provide below. I seem to be unable to get an interrupt? Is there a
special way or trick for debugging with interrupts? How can I see
what the Timer Counter is?

Thank you in advance for your time and help.



static void timer1ISR(void) __attribute__ ((interrupt ("IRQ")));

static
void
timer1ISR(void)
{
++timer1Count;
/* Clear the timer 1 interrupt */
T1IR = 0xFF;
/* Update VIC priorities */
VICVectAddr = 0;
}

int
main(void)
{

/* Timer 1 interrupt is an IRQ interrupt */
VICIntSelect &= ~0x20;
/* Enable timer 1 interrupt */
VICIntEnable = 0x20;
/* Use slot 1 for timer 1 interrupt */
VICVectCntl1 = 0x25;
/* Set the address of ISR for slot 1 */
VICVectAddr1 = (unsigned long)timer1ISR;

/* Reset timer 1 */
T1TCR = 0;
/* Set the timer 1 prescale counter */
T1PR = 2500;
/* Set timer 1 match register */
T1MR0 = 1000;
/* Generate interrupt and reset counter on match */
T1MCR = 3;
/* Start timer 1 */
T1TCR = 1;

/* Enable Interrupts */
__ARMLIB_enableIRQ(); /*CrossStudio specific*/
/*I also tried using VICINTEN = 0x20*/

return 0;
}

An Engineer's Guide to the LPC2100 Series

--- In l..., "abufadel" wrote:
>
> Greetings,
> I am trying to get the Timers 0 and 1 to work with interrupts
using
> the Olimex 2106 board, but cannot get the interrupts to work. I am
> following the example code given in the CrossStudio help, which I
> provide below. I seem to be unable to get an interrupt? Is there a
> special way or trick for debugging with interrupts? How can I see
> what the Timer Counter is?
>
> Thank you in advance for your time and help.
>
> static void timer1ISR(void) __attribute__ ((interrupt ("IRQ")));
>
> static
> void
> timer1ISR(void)
> {
> ++timer1Count;
> /* Clear the timer 1 interrupt */
> T1IR = 0xFF;
> /* Update VIC priorities */
> VICVectAddr = 0;
> }
>
> int
> main(void)
> {
>
> /* Timer 1 interrupt is an IRQ interrupt */
> VICIntSelect &= ~0x20;
> /* Enable timer 1 interrupt */
> VICIntEnable = 0x20;
> /* Use slot 1 for timer 1 interrupt */
> VICVectCntl1 = 0x25;
> /* Set the address of ISR for slot 1 */
> VICVectAddr1 = (unsigned long)timer1ISR;
>
> /* Reset timer 1 */
> T1TCR = 0;
> /* Set the timer 1 prescale counter */
> T1PR = 2500;
> /* Set timer 1 match register */
> T1MR0 = 1000;
> /* Generate interrupt and reset counter on match */
> T1MCR = 3;
> /* Start timer 1 */
> T1TCR = 1;
>
> /* Enable Interrupts */
> __ARMLIB_enableIRQ(); /*CrossStudio specific*/
> /*I also tried using VICINTEN = 0x20*/
>
> return 0;
> }
>

And what happens when "main()" returns?
--- In l..., "abufadel" wrote:

> /* Timer 1 interrupt is an IRQ interrupt */
> VICIntSelect &= ~0x20;
> /* Enable timer 1 interrupt */
> VICIntEnable = 0x20;
> /* Use slot 1 for timer 1 interrupt */
> VICVectCntl1 = 0x25;
> /* Set the address of ISR for slot 1 */
> VICVectAddr1 = (unsigned long)timer1ISR;
>

The above code assumes that timer1 is connected to VIC channel 1,
however timer1 is connected to VIC channel 5.

See table table 44 in the LPC2106 user manual from NXP
(http://www.standardics.nxp.com/support/documents/microcontrollers/pdf/user.manual.lpc2104.lpc2105.lpc2106.pdf)

Gert
--- In l..., "gert_vervoort" wrote:
>
> --- In l..., "abufadel" wrote:
>
> > /* Timer 1 interrupt is an IRQ interrupt */
> > VICIntSelect &= ~0x20;
> > /* Enable timer 1 interrupt */
> > VICIntEnable = 0x20;
> > /* Use slot 1 for timer 1 interrupt */
> > VICVectCntl1 = 0x25;
> > /* Set the address of ISR for slot 1 */
> > VICVectAddr1 = (unsigned long)timer1ISR;
> > The above code assumes that timer1 is connected to VIC channel 1,
> however timer1 is connected to VIC channel 5.
>
> See table table 44 in the LPC2106 user manual from NXP
>
(http://www.standardics.nxp.com/support/documents/microcontrollers/pdf
/user.manual.lpc2104.lpc2105.lpc2106.pdf)
>
> Gert
>

Gert,

There's nothing wrong with the code above.

Timer 1 is indeed channel 5: this is used in the following registers:

- VICIntEnable (to enable channel) set to 0x20 (i.e. bit position 5)
- VICIntSelect (to select IRQ/FIQ) bit 5 cleared: &= ~0x20
- value written to VICVectCntl1: 0x20 to enable the slot, and 0x05 to
select the channel for the slot (i.e. the priority of the interrupt:
in this case 1, the 2nd highest available)

To the OP: are you enabling interrupts in the CPSR as well?

Brendan
--- In l..., "Brendan Murphy"
wrote:
>
> --- In l..., "abufadel" wrote:
> >
> > Greetings,
> > I am trying to get the Timers 0 and 1 to work with interrupts
> using
> > the Olimex 2106 board, but cannot get the interrupts to work. I am
> > following the example code given in the CrossStudio help, which I
> > provide below. I seem to be unable to get an interrupt? Is there a
> > special way or trick for debugging with interrupts? How can I see
> > what the Timer Counter is?
> >
> > Thank you in advance for your time and help.
> >
> >
> >
> > static void timer1ISR(void) __attribute__ ((interrupt ("IRQ")));
> >
> > static
> > void
> > timer1ISR(void)
> > {
> > ++timer1Count;
> > /* Clear the timer 1 interrupt */
> > T1IR = 0xFF;
> > /* Update VIC priorities */
> > VICVectAddr = 0;
> > }
> >
> > int
> > main(void)
> > {
> >
> > /* Timer 1 interrupt is an IRQ interrupt */
> > VICIntSelect &= ~0x20;
> > /* Enable timer 1 interrupt */
> > VICIntEnable = 0x20;
> > /* Use slot 1 for timer 1 interrupt */
> > VICVectCntl1 = 0x25;
> > /* Set the address of ISR for slot 1 */
> > VICVectAddr1 = (unsigned long)timer1ISR;
> >
> > /* Reset timer 1 */
> > T1TCR = 0;
> > /* Set the timer 1 prescale counter */
> > T1PR = 2500;
> > /* Set timer 1 match register */
> > T1MR0 = 1000;
> > /* Generate interrupt and reset counter on match */
> > T1MCR = 3;
> > /* Start timer 1 */
> > T1TCR = 1;
> >
> > /* Enable Interrupts */
> > __ARMLIB_enableIRQ(); /*CrossStudio specific*/
> > /*I also tried using VICINTEN = 0x20*/
> >
> > return 0;
> > }
> > And what happens when "main()" returns?
>

I must have skipped a line while copying.

while (timer1Count < 5);

comes right before return 0; Essentially, timer1 isr increments the
counter and toggles the led.

Thank you.

-- A
> I must have skipped a line while copying.
>
> while (timer1Count < 5);
>
> comes right before return 0; Essentially, timer1 isr increments the
> counter and toggles the led.
>
> Thank you.
>
> -- A
>

For embedded systems, it is unusual for a main() function to return.
Sometimes the startup code can handle this because it calls the main
function and has a loop that traps the CPU if main returns. This
isn't always the case, sometimes the startup code branches to main and
doesn't provide the loop.

So, always put 'while(1) ;' at the end of your main function to
prevent a return.

As Brendan pointed out, your startup code might not have enabled the
interrupts in CPSR. Just before the branch to main, look for this:
MSR CPSR_c,#MODE_SVC /* enable interrupts */
MSR CPSR_c,#MODE_USR

/* Enter the C code */
b main

Notice the branch to main. It better not return or we'll be joining
Tinkerbell in Neverland.

Richard
--- In l..., "abufadel" wrote:
> >
> > And what happens when "main()" returns?
> > I must have skipped a line while copying.
>
> while (timer1Count < 5);
>
> comes right before return 0; Essentially, timer1 isr increments the
> counter and toggles the led.
>
> Thank you.

Make sure you declare any variable shared between an interrupt and
background function as "volatile", i.e.

volatile int timer1Count;
--- In l..., "Brendan Murphy"
wrote:
>
> --- In l..., "gert_vervoort" wrote:
> >
> > --- In l..., "abufadel" wrote:
> >
> > > /* Timer 1 interrupt is an IRQ interrupt */
> > > VICIntSelect &= ~0x20;
> > > /* Enable timer 1 interrupt */
> > > VICIntEnable = 0x20;
> > > /* Use slot 1 for timer 1 interrupt */
> > > VICVectCntl1 = 0x25;
> > > /* Set the address of ISR for slot 1 */
> > > VICVectAddr1 = (unsigned long)timer1ISR;
> > >
> >
> > The above code assumes that timer1 is connected to VIC channel 1,
> > however timer1 is connected to VIC channel 5.
> >
> > See table table 44 in the LPC2106 user manual from NXP
> (http://www.standardics.nxp.com/support/documents/microcontrollers/pdf
> /user.manual.lpc2104.lpc2105.lpc2106.pdf)
> >
> > Gert
> > Gert,
>
> There's nothing wrong with the code above.
>
> Timer 1 is indeed channel 5: this is used in the following
registers:
>
> - VICIntEnable (to enable channel) set to 0x20 (i.e. bit position 5)
> - VICIntSelect (to select IRQ/FIQ) bit 5 cleared: &= ~0x20
> - value written to VICVectCntl1: 0x20 to enable the slot, and 0x05
to
> select the channel for the slot (i.e. the priority of the
interrupt:
> in this case 1, the 2nd highest available)
>
> To the OP: are you enabling interrupts in the CPSR as well?
>
> Brendan
>

Shouldn't the write to VICIntEnable be the last statement (set
everything up then enable it)?

Also, shouldn't the write to VICIntEnable be preceeded by a write to
the timer 1 register that clears it's interrupt (to make sure it is
cleared before enabling it)?

TC
This code for Timer 0 is known to work:

-------
timer.h
-------

#ifndef _TIMER_H_
#define _TIMER_H_

void T0_init(void);
void T0ISR(void) __attribute__ ((interrupt));

#endif //_TIMER_H_

-------
timer.c
-------

volatile unsigned int CurrentTime = 0;

void T0_init(void)
{
T0TC = 0; // clear the timer count
T0PR = 1; // prescale divide by 2
T0MR0 = 7500; // divide by 7500 to get approx 1000 ints per second
T0MCR = 0x03; // interrupt on match, reset on match
T0TCR = 0x01; // start the timer
VICVectCntl3 = 0x00000024; // set priority 3 for Timer 0
VICVectAddr3 = (unsigned long) T0ISR; // pass the address of the ISR
VICIntEnable = 0x00000010; // enable interrupt
T0EMR = 0x30; // toggle external match pin
}

void T0ISR(void) // interrupt handler - see timer.h
{
CurrentTime++;
T0IR = 0x01;
VICVectAddr = 0xFF;
}

It depends on the following in crt.s:

MSR CPSR_c,#MODE_SVC /* enable interrupts */
MSR CPSR_c,#MODE_USR

/* Enter the C code */
b main

All with the GNU toolchain.

Richard