ARM Technical Support Knowledge Articles

SPORADIC INTERRUPT PROBLEMS

Applies to: ARM Development Tools

Answer


Information in this article applies to:


SYMPTOM

The interrupt code on my ARM system seems to fail sporadically. Similar code was working fine for years on an 8051 target.

Below is the specific code portion extracted into a test program written with the Keil CARM Compiler for the Keil MCB2100 Evaluation Board:

#include <LPC21xx.h>

unsigned long volatile counter;

/* Timer 0 Interrupt Function */
void tc0 (void) __irq  {
  IOCLR1 = 0xFFFFFFFF;
  counter = 0;
  T0IR        = 1;                         // Clear interrupt flag
  VICVectAddr = 0;                         // Acknowledge Interrupt
}

/* Setup the Timer Counter 0 Interrupt */
void init_timer (void)  {
  T0MR0 = 149999;                          // 10mSec = 150000-1 counts
  T0MCR = 3;                               // Interrupt and Reset on MR0
  T0TCR = 1;                               // Timer0 Enable
  VICVectAddr0 = (unsigned long)tc0;       // set interrupt vector in 0
  VICVectCntl0 = 0x20 | 4;                 // use it for Timer 0 Interrupt
  VICIntEnable = 0x00000010;               // Enable Timer0 Interrupt
}

void main (void) {
  int v;

  IODIR1 = 0x00FF0000;
  IOCLR1 = 0x00FF0000;
  init_timer();

  while(1)  {
    v = (counter << 8) & 0xFF0000;         // the true I/O pins
    counter++;

    IOSET1 = v;                            // turn on LED
    IOCLR1 = ~v;                           // turn off LED
  }
}

CAUSE

In contrast to 8051 or C166, the C statement counter++ does not translate into a single CPU instruction. Therefore, this C statement is not atomic and interrupts may happen in the middle of the increment. As a result, the statement counter = 0; in your interrupt code may not really reset the counter value.

RESOLUTION

Disable the timer interrupt before you update the counter variable in your main application. This is shown in the program sequence below:

/* Default Interrupt Function: may be called when timer ISR is disabled */
void DefISR (void) __irq  {
  ;
}

/* Timer 0 Interrupt Function: called when timer ISR is enabled */
void tc0 (void) __irq  {
  IOCLR1 = 0xFFFFFFFF;
  counter = 0;
  T0IR        = 1;                         // Clear interrupt flag
  VICVectAddr = 0;                         // Acknowledge Interrupt
}

/* Setup the Timer Counter 0 Interrupt */
void init_timer (void)  {
  T0MR0 = 149999;                          // 10mSec = 150000-1 counts
  T0MCR = 3;                               // Interrupt and Reset on MR0
  T0TCR = 1;                               // Timer0 Enable
  VICVectAddr0 = (unsigned long)tc0;       // set interrupt vector in 0
  VICVectCntl0 = 0x20 | 4;                 // use it for Timer 0 Interrupt
  VICIntEnable = 0x00000010;               // Enable Timer0 Interrupt
}


void main (void) {
  int v;

  IODIR1 = 0x00FF0000;
  IOCLR1 = 0x00FF0000;
  init_timer();
    VICDefVectAddr = (unsigned long) DefISR;   // ISR for un-assigned VIC interrupts

  while(1)  {
    VICIntEnClr |= 0x00000010;             // Disable Timer0 Interrupt
// put some instructions to ensure that the interrupt is blocked (pipeline effects)!
    v = (counter << 8) & 0xFF0000;         // the true I/O pins
    counter++;
    VICIntEnable |= 0x00000010;            // Enable Timer0 Interrupt

    IOSET1 = v;                            // turn on LED
    IOCLR1 = ~v;                           // turn off LED
  }
}

The VIC (Vectored Interrupt Controller) of the ARM device may still accept interrupts even when they are disabled. Therefore you must implement a default interrupt service routine as shown above.

MORE INFORMATION

SEE ALSO

Article last edited on: 2006-07-27 08:21:30

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential