Starting a timer inside an ISR...?

mepler

Member
Summary
My use-case is a film scanner. Film is constantly moving and, upon detecting one of the holes at the edges, I need to flash LEDs and take a picture to capture the film frame. In this use-case, the timing of each exposure should be as precise as possible. Checking elapsed micros in the main loop reduces my "clock" resolution to the time it takes the loop to complete. I tried the solution below...but couldn't get it to behave as expected. I suspect my shallow knowledge of timers and interrupts is preventing me from seeing a better option.

Requirements
- LEDs should turn on as soon as a hole is detected (and other logic conditions are met) [solution = hardware interrupt runs ISR where I turn the lights on]
- Duration of LED flash should be highly consistent. [solution = use a timer]

Logic pseudo-code
Code:
// On hole detection (rising edge)...
//  Check hole counter (some types of film have one hole per frame, others four, so you don't always take a picture here)
//    If value of hole counter is less than the number of holes per frame, increment it
//    If equal (or greater), turn on lights, start timer
//
//  When timer is done...
//    Turn off lights, reset counter

Actual code
C++:
// NOTE: a button is used to simulate high/low signals from the hole sensor,
//       and a standard LED is used in place of the full-power LED driver.


#include <TimerOne.h>


const int holeInterruptPin = 2;
volatile int holeCounter = 0;
const int holesPerFrame = 4;
const int LEDpin = 5;


void holeDetected(void) {
  if (holeCounter < holesPerFrame) {
    holeCounter = holeCounter +1;
} else {
    digitalWrite(LEDpin, HIGH);
    Timer1.start();
  }
}


void LEDoff(void) {
  digitalWrite(LEDpin, LOW);
  holeCounter = 0;
  Timer1.stop();
}


void setup() {
  pinMode(LEDpin, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(holeInterruptPin), holeDetected, RISING);
  Timer1.initialize(3000000);
  Timer1.attachInterrupt(LEDoff);
  Timer1.stop();
}


void loop() {
  delay(10); // could be causing issues? But same result without this. 
}
 
Which Teensy are you using? I suggest you try using IntervalTimer, which is built into the T3 and T4 cores, rather than TimerOne, which is a very old library going back to AVR. TimerOne in TeensyDuino does support T3 and T4, but it may not behave as expected in all respects. For example, on T4, TimerOne uses a 16-bit FlexPWM timer, and the longest period you can define is about 55 ms. IntervalTimer, on the other hand, uses the 32-bit PIT built into T4 and will support a 3-second timer.
 
Thank you @Paul. That did the trick. Thank you for all you do for this community.

For those who end up here with a similar question, here's how I implemented Paul's suggestion...


C++:
#define EXPOSURE_TIME 100000   // duration in microseconds for LEDs to be on
#define HOLE_FREQ 500000         // used with "signalTimer" to imitate pulses from the hole sensor on the film scanner
#define HOLES_PER_FRAME 4       // 35mm film has four holes for every frame

IntervalTimer ledTimer;
IntervalTimer signalTimer; 

volatile unsigned int holeCounter = 0;
const int ledPin = 5;


void setup() {
  pinMode(ledPin, OUTPUT);

  signalTimer.begin(incrementHoleCount, HOLE_FREQ);
 
  Serial.begin(9600);
}

void loop() {
  unsigned int counterCopy;

  noInterrupts();
  counterCopy = holeCounter;
  interrupts();

  Serial.print(counterCopy);
  Serial.println();
  delay(10);
}

void lightsOff() {
  digitalWrite(ledPin, LOW);
  ledTimer.end();
}

void incrementHoleCount() {
  holeCounter = holeCounter + 1;
  if (holeCounter < HOLES_PER_FRAME) {
      return;
   } else {
      digitalWrite(ledPin, HIGH);
      ledTimer.begin(lightsOff, EXPOSURE_TIME);
      holeCounter = 0;
   }
}
 
Back
Top