Using Timer1-library to create one-shot timer

Hi,
I am using an Arduino Uno and I want to be able to dynamically create timers.
I don't want to use the delay-function mainly because I want to understand how timers works and also I need to react to some user input buttons.

I found a library called Timer1 which seems to be the way to go.
Basically in the loop-function I want to react to some user input and activate a motor either going backward or forward and then stop the motor after a certain period of time (lets say 5 seconds).
I create a timer when the motor is started and then wait for the interrupt to be called so I can turn the motor off.

Below is my code: (I removed most of the unnecessary code)

#include "TimerOne.h"

#define BAUD_RATE                       115200

const long DURATION_MOTOR_RUNNING = 5000000; // 5 seconds

void setup() {
    ...
    Serial.begin(BAUD_RATE);
    Serial.println("Ready!");
}


void loop() {
    if ( ... ) {
        if ( ... ) {
            openSunroof();
        } else { 
            closeSunroof();
        }
    }
}

void stopSunroof() {
    Serial.println("STOP SUNROOF ISR");
  
    Timer1.stop();
    Timer1.detachInterrupt();
}

void openSunroof() {
    Serial.println("SUNROOF OPEN");

    Timer1.initialize(DURATION_MOTOR_RUNNING); //
    Timer1.attachInterrupt(stopSunroof);
    Timer1.restart();
}

void closeSunroof() {
    Serial.println("SUNROOF CLOSE");

    Timer1.initialize(DURATION_MOTOR_RUNNING);
    Timer1.attachInterrupt(stopSunroof);
    Timer1.restart();
}
}

but what ever I do, the interrupt-function is called immediately and turn the motor off just milliseconds after it has been turned on.

The output is basically this (3 tries):

SUNROOF CLOSE
STOP SUNROOF ISR

SUNROOF CLOSE
STOP SUNROOF ISR

SUNROOF OPEN
STOP SUNROOF ISR

Please help me understand what I am doing wrong as there are not so many example online that uses the Timer1 library.

Never call any method on the Serial object inside an interrupt service routine.

Use a flag variable which you set in the ISR and react on it in the main loop. See the ReadReciver.pde example of the TimerOne library.

You know, you can create a timer class that you can start() and then in your loop() ask if it’s expired(). Being a class you can create as many different ones as you want. (This is what I do.)

-jim lee

alborz:
I want to understand how timers works

I found a library called Timer1 which seems to be the way to go.

While Timer1 might be an easy way to set up a timer, if you really want to learn how they work, go read in the data sheet and bare metal it for a sketch or two. Once you have that, you can write something a lot better than Timer1 library. Or maybe you'll still use Timer1, but you'll know what it is doing under the hood and it will be a lot easier to actually do what you want with the timers. Plus you can also run timers 0 and 2.

There is a potential problem with the timer.start() function. It is noted in the Timer 1 library comments.
The overflow interrupt gets triggered when TCNT1 is reset to 0.

From the TimerOne library code

 //****************************
    //  Run Control
    //****************************
    void start() __attribute__((always_inline)) {
	TCCR1B = 0;
	TCNT1 = 0;		// TODO: does this cause an undesired interrupt?
	resume();
    }
    void stop() __attribute__((always_inline)) {
	TCCR1B = _BV(WGM13);
    }
    void restart() __attribute__((always_inline)) {
	start();
    }
    void resume() __attribute__((always_inline)) {
	TCCR1B = _BV(WGM13) | clockSelectBits;
    }

There is a work around which sets TCNT1 =1. You can modify the library code.

I was also surprised to find out that you can actually leave the library alone, and change the sketch. I don't quite understand why it works, but I think that's because of the large prescaler used for the 5 second period so that you can increment TCNT1 before the timer actually starts and sets the interrupt.

#include "TimerOne.h"

#define BAUD_RATE                       115200

const long DURATION_MOTOR_RUNNING = 5000000; // 5 seconds

void setup() {
  // ...
  Serial.begin(BAUD_RATE);
  Serial.println("Ready!");
}


void loop() {
  // if ( ... ) {
  //  if ( ... ) {
  openSunroof();
  delay(7000);
  //  } else {
  closeSunroof();
  delay(7000);
  //  }
  // }
}

void stopSunroof() {
  Serial.println("STOP SUNROOF ISR");
  Timer1.stop();
  Timer1.detachInterrupt();
 
}

void openSunroof() {
  Serial.println("SUNROOF OPEN");
  Timer1.initialize(DURATION_MOTOR_RUNNING);
  Timer1.attachInterrupt(stopSunroof);
  Timer1.restart();
  TCNT1 = 1;
  
}

void closeSunroof() {
  Serial.println("SUNROOF CLOSE");
  Timer1.initialize(DURATION_MOTOR_RUNNING);
  Timer1.attachInterrupt(stopSunroof);
  Timer1.restart();
  TCNT1 = 1;
 
}