Interrupt timer

I am trying to get an interrupt timer to work so that I can multitask. I have a robot that drives pretty fast but also needs to sense objects and control a servo if a threshold is met. I know I could just use an external interrupt controlled by a comparator but I would like to avoid electrical components if possible, plus, this way I can have a calibration period so that the threshold doesn't need to be manually set by a potentiometer.

Anyway, I found this code here http://popdevelop.com/2010/04/mastering-timer-interrupts-on-the-arduino/

/*
 * author: Sebastian Wallin
 * description:
 * Example on how to configure the periodical execution of a user
 * defined function (Interrupt service routine) using Timer2. This
 * example will run the function every 1ms.
 *
 * For detailed information on Timer2 configuration see chapter 17 in
 * ATMEGA328 datasheet.
 */

/* Timer2 reload value, globally available */
unsigned int tcnt2;

/* Toggle HIGH or LOW digital write */
int toggle = 0;

/* Setup phase: configure and enable timer2 overflow interrupt */
void setup() {
  /* Configure the test pin as output */
  pinMode(2, OUTPUT); 

   /* First disable the timer overflow interrupt while we're configuring */
  TIMSK2 &= ~(1<<TOIE2);

  /* Configure timer2 in normal mode (pure counting, no PWM etc.) */
  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);

  /* Select clock source: internal I/O clock */
  ASSR &= ~(1<<AS2);

  /* Disable Compare Match A interrupt enable (only want overflow) */
  TIMSK2 &= ~(1<<OCIE2A);

  /* Now configure the prescaler to CPU clock divided by 128 */
  TCCR2B |= (1<<CS22)  | (1<<CS20); // Set bits
  TCCR2B &= ~(1<<CS21);             // Clear bit

  /* We need to calculate a proper value to load the timer counter.
   * The following loads the value 131 into the Timer 2 counter register
   * The math behind this is:
   * (CPU frequency) / (prescaler value) = 125000 Hz = 8us.
   * (desired period) / 8us = 125.
   * MAX(uint8) + 1 - 125 = 131;
   */
  /* Save value globally for later reload in ISR */
  tcnt2 = 131; 

  /* Finally load end enable the timer */
  TCNT2 = tcnt2;
  TIMSK2 |= (1<<TOIE2);
}

/*
 * Install the Interrupt Service Routine (ISR) for Timer2 overflow.
 * This is normally done by writing the address of the ISR in the
 * interrupt vector table but conveniently done by using ISR()  */
ISR(TIMER2_OVF_vect) {
  /* Reload the timer */
  TCNT2 = tcnt2;
  /* Write to a digital pin so that we can confirm our timer */
  digitalWrite(2, toggle == 0 ? HIGH : LOW);
  toggle = ~toggle;
}

/* Main loop. Empty, but needed to avoid linker errors */
void loop() {
}

When I compile, it gives me an error saying that amp was not declared. Honestly this is a little over my head, I understand how it works by reading the code but I'm not sure what amp is and how to get this code to compile.

That's because the site you copy/pasta'd that from used a botched HTML presentation system, and screwed up all your ">" (> - "Greater Than"), "<" (< - "Less Than"), and "&" (& - "Ampersand") characters. Here, a quick search-and-replace, and this should work...

/*
 * author: Sebastian Wallin
 * description:
 * Example on how to configure the periodical execution of a user
 * defined function (Interrupt service routine) using Timer2. This
 * example will run the function every 1ms.
 *
 * For detailed information on Timer2 configuration see chapter 17 in
 * ATMEGA328 datasheet.
 */

/* Timer2 reload value, globally available */
unsigned int tcnt2;

/* Toggle HIGH or LOW digital write */
int toggle = 0;

/* Setup phase: configure and enable timer2 overflow interrupt */
void setup() {
  /* Configure the test pin as output */
  pinMode(2, OUTPUT); 

   /* First disable the timer overflow interrupt while we're configuring */
  TIMSK2 &= ~(1<<TOIE2);

  /* Configure timer2 in normal mode (pure counting, no PWM etc.) */
  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);

  /* Select clock source: internal I/O clock */
  ASSR &= ~(1<<AS2);

  /* Disable Compare Match A interrupt enable (only want overflow) */
  TIMSK2 &= ~(1<<OCIE2A);

  /* Now configure the prescaler to CPU clock divided by 128 */
  TCCR2B |= (1<<CS22)  | (1<<CS20); // Set bits
  TCCR2B &= ~(1<<CS21);             // Clear bit

  /* We need to calculate a proper value to load the timer counter.
   * The following loads the value 131 into the Timer 2 counter register
   * The math behind this is:
   * (CPU frequency) / (prescaler value) = 125000 Hz = 8us.
   * (desired period) / 8us = 125.
   * MAX(uint8) + 1 - 125 = 131;
   */
  /* Save value globally for later reload in ISR */
  tcnt2 = 131; 

  /* Finally load end enable the timer */
  TCNT2 = tcnt2;
  TIMSK2 |= (1<<TOIE2);
}

/*
 * Install the Interrupt Service Routine (ISR) for Timer2 overflow.
 * This is normally done by writing the address of the ISR in the
 * interrupt vector table but conveniently done by using ISR()  */
ISR(TIMER2_OVF_vect) {
  /* Reload the timer */
  TCNT2 = tcnt2;
  /* Write to a digital pin so that we can confirm our timer */
  digitalWrite(2, toggle == 0 ? HIGH : LOW);
  toggle = ~toggle;
}

/* Main loop. Empty, but needed to avoid linker errors */
void loop() {
}

LOL, I have lost it, I just came back to post how stupid I was and apologize for this post but you got me before I could. Thanks for this.

I did have one other question though, while the thread title is still relevant.

If I have to do some servo stuff that takes up to 250ms, is the interrupt still going to get called during that 250ms or will it wait until the current "thread" is completed?

I assume, if it is going to continue to be called then I would need to set a flag to return if the servo is moving so it doesn't read the sensor and try to move the servo while it's already moving.

If I have to do some servo stuff that takes up to 250ms, is the interrupt still going to get called during that 250ms

That depends how well you write your thread handling.
Servos (I assume you're talking about R/C servos) operate on 20ms frames, so 250ms is an awful lot of processing.

Well, it's not necessarily the servo moving the entire time, I have to move it, wait 200ms and move again. But okay, say I want to put a delay in the interrupt, (not using delay() of course) but what happens during that delay, is the interrupt called again and again or does it wait for my current interrupt routine to finish?

say I want to put a delay in the interrupt

Why would you ever want to do that?

ribbs2521:
Well, it's not necessarily the servo moving the entire time, I have to move it, wait 200ms and move again. But okay, say I want to put a delay in the interrupt, (not using delay() of course) but what happens during that delay, is the interrupt called again and again or does it wait for my current interrupt routine to finish?

Yeppers. It'll get called within the current (delay) execution and pretty much crash the CPU if we're talking on the millisecond scale of interrupt timing. It's extremely important that interrupt routines "return" as fast as possible so two ISRs don't overlap. The AVR docs say there's at least one full instruction cycle between one interrupt returning (to the main program) and the next one getting called, but if the ISR never returns, it could crap out the stack in a matter of milliseconds.

Best practice is just to use an interrupt to set a ("volatile") global variable value*, and just have your program loop checking this value on a regular basis. Sorta like this (pseudocode):

volatile boolean makeStuffHappen1, makeStuffHappen2;
void interruptTrigger1() {
  makeStuffHappen1 = true;
}
void interruptTrigger2() {
  makeStuffHappen2 = true;
}
void setup() {
  attachInterrupt(0,interruptTrigger1,RISING);
  attachInterrupt(1,interruptTrigger2,FALLING);
}
void loop() {
  if (makeStuffHappen1) {
    doStuff(abc, zyx);
    makeStuffHappen1 = FALSE;
  }
  if (makeStuffHappen2) {
    doStuff(def, wvu);
    makeStuffHappen2 = FALSE;
  }
}

(some may argue that, being an Arduino structure, that's pseudo-pseudocode... :sweat_smile:)

Basically, just "watch" that variable to change on its own, and act accordingly if it changes, on your own timing terms. That way, delay() works and all other related timings work as well. Maybe even use a one-liner, "if (makeStuffHappen1) doMakeStuffHappen();" which also sets makeStuffHappen1 back to false (otherwise it'll get triggered every loop). :slight_smile:

    • "volatile" instructs the assembler, "don't assume this is in the state you last remembered it, it may change while you're not looking". Since the variables have to be loaded out of RAM and into registers in order to work with them (read, write, etc), the compiler uses a known state of the registers to figure out where to shuffle data around. "volatile" tells it to always double-check before reading or writing it, so working with these variables can be somewhat resource-consuming... if you're going to store a value in there, I'd suggest transferring it to a more "temporary" location after retrieving it, then you can play with the value (unless you need it to be bi-directional, i.e. that value is read by the ISR as well)