cancel
Showing results for 
Search instead for 
Did you mean: 

Simple and accurate delay inside an Interrupt

dgleeson2
Associate II
Posted on August 11, 2010 at 19:32

Simple and accurate delay inside an Interrupt

8 REPLIES 8
rcummings
Associate II
Posted on May 17, 2011 at 14:01

Denis,

   The simplest Timer on the chip is the SysTick.  See the information on it in the ARM Cortex-M3 programming manual rather than the specific reference manual for the chip that you're using. 

My setup code for it:

//Configure the SysTick Timer for Main Loop Timing.  At 4.0MHz, HCLK is

   //needed to get to 15.625ms.  If using the /8 the math will be off.

   SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);

   //Place N-1 in the SysTick Load register to set the interrupt rate.

   SysTick->LOAD = CLOCKS_PER_CYCLE-1;

   //The SysTick timer should be disabled and not have the interrupt on by

   //default from the reset condition.  Enabling will cause the VAL register to

   //get populated with the value in the LOAD register.

   //Enable the SysTick interrupt and start the clock.  The act of enabling also

   //takes care of the initial load to the value in SysTick->LOAD.

   SysTick->CTRL |= (SysTick_CTRL_ENABLE | SysTick_CTRL_TICKINT);

with the interrupt code being:

void SysTick_Handler(void)

{

   //disable interrupts until the processor is awake again.

   //Clear the Interrupt Count Flag

   SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG;

   //Set the Cycle Time Flag

   stBaseTimer.bCycleTimeFlag = TRUE;

}

That sets a flag every CLOCKS_PER_CYCLE to allow me to do a heartbeat loop.

regards,

RonCK

swhite2
Associate III
Posted on May 17, 2011 at 14:01

I would NOT recommend using interrupts for switch inputs. Instead I'd poll them via a timer ISR say every 1mS or 10mS depending on your requirements. I  use the SysTick for this purpose as Ron suggested.

Port pin interrupts are edge triggered and since switches etc bounce you'll end up with multiple interrupts. That can be problematic.

An advantage of polling via an ISR is that you already have a periodic interrupt so the debouncing can be done with counters inside the ISR. There's very little overhead doing this (i.e. interrupt processing is very fast).

Delaying/looping inside ISR's is bad practice IMO.

John F.
Senior
Posted on May 17, 2011 at 14:01

Stuart is right. An easy way to debounce keys is to read the GPIO state periodically with an interrupt and store the value in a short FIFO array. Then you can scan the array for a continuous keydown state (eg 5 contiguous keydowns) and set a flag ''KeyPressed'' (one for each key) which the main program can sense and clear in its own time. This method lends itself to both click and double click sensing and although it sounds a bit overcomplex actually makes handling the keypad very easy.

Interrupts should always complete and return as quickly as possible.

Posted on May 17, 2011 at 14:01

''Im relatively new to STM32 land.''

 

Would it be a fair guess that you're also new to embedded microcontrollers in general?

As already noted, delays inside interrupt handlers are generally a (very) Bad Thing!

And high-level language (HLL) loops are never good for accurate delays - see: 

http://www.8052.com/forum/read/162556

dgleeson2
Associate II
Posted on May 17, 2011 at 14:01

Hi Guys

Many thanks!

Of course polling is better.

I started with someone else's mistake and am trying to patch it instead of doing it correctly. Ill start with polling today.

In embedded development for 20 years at this stage. Still make mistakes though.

Thanks for your advice.

Denis

___________________________

http://www.CentronSolutions.com

dgleeson2
Associate II
Posted on May 17, 2011 at 14:01

John

just so I can check my new polling code. Do you have C code handy that uses your technique.

Thanks

Denis

________________________

http://www.CentronSolutions.com

John F.
Senior
Posted on May 17, 2011 at 14:01

Hi Denis,

You're going to have to tease out the useful bits of this ... but as a guide :-

#define ENTER     (1<<17)                 //keys are sensed on P0.17..20 as zero when pressed

#define UP        (1<<18)

#define DOWN      (1<<19)

#define MENU      (1<<20)

#define ANY       (ENTER | UP | DOWN | MENU)

#define NKEYSAMPLES 10

//Periodically in the timer interrupt service routine

  static unsigned int KeySamples[NKEYSAMPLES];

  static unsigned int Index;              //values maintained between calls

  unsigned int Sample;                    //general access index to KeySamples[]

  if(TickCount == 0)                      //periodically eg every 5 millisecs)

  {

    //read in keypad lines and sense and store keystates and keypresses

    //for keypad debounce, new data replaces old in circular store, keep index in range

    if(Index >= NKEYSAMPLES) Index = 0;

    KeySamples[Index] = FIO0PIN;            //read port 0

    KeySamples[Index] = ~KeySamples[Index]; //keys pressed stored as 1

    PreviousKeys = CurrentKeys;             //save current key values

    CurrentKeys = KeySamples[Index++];      //fetch modified port values just read

    //then look for bits set in all the KeySamples array

    for(Sample=0; Sample<NKEYSAMPLES; Sample++)

      CurrentKeys = CurrentKeys & KeySamples[Sample];

    //bits will be set in CurrentKeys if they are present in all keysamples

    //set a bit in KeysPressed if a key has transitioned from unpressed to pressed

    //the programmer must clear Keypressed bits once actioned

    KeysPressed = KeysPressed | (CurrentKeys & ~PreviousKeys);

  }

 

//in the main program

//these functions return keystates

unsigned int KeyDown(unsigned int KeyMask)

{

  return (CurrentKeys & KeyMask);

}

unsigned int KeyPressed(unsigned int KeyMask)

{

  return (KeysPressed & KeyMask);

}

void KeyClear(unsigned int KeyMask)

{

  KeysPressed = KeysPressed & ~KeyMask; //clear the flag for argument KeyMask

}

//typically used as

if    (KeyPressed(UP))

{

  KeyClear(UP);

  //do something

}

//or

KeyClear(ANY);

//or

while(!KeyPressed(ENTER));  //wait until user presses ''enter'' key

Hope that helps.

picguy2
Associate II
Posted on May 17, 2011 at 14:01

Never put a delay loop in an ISR.

Use 2 bytes/key.  

Byte 0: state of key for publication.  I.e. use this byte in the rest of your program and not the GPIO state of the key.  

Byte 1: Millisecond timer.

One kHz ISR:

If timer byte != 0 decrement the byte.  (Don’t look at GPIO key state.)

Else...

If GPIO state != byte 0 then toggle its state and set the timer = 35 or the timeout of your choosing.  

Simpler ISR: only decrement timer byte if it’s non-zero.  Mainline only looks at GPIO key state when timer byte is zero.  Update byte 0 when GPIO state != byte 0 and set timer byte.

Either way it’s a simple digital filter.  Key changes are reported immediately except for some number of milliseconds after a state change.

Or even simpler, only look at your keys every 50 milliseconds.