Arduino Playground is read-only starting December 31st, 2018. For more info please look at this Forum Post

The FrequencyTimer2 library steals the hardware timer2 from the PWMs on pin 11 and 3(on a atmega168) and reprograms it to generate a frequency of your choosing. It can toggle pin11 if you wish or call a function you supply each cycle.

Updated Version 2

Version 2 adds support for newer boards, including Arduino Uno (and other '328-based boards), Arduino Mega, Teensy++ and Sanguino. It also updates the library for compatibility with Arduino 1.0.

Download version 2 here

The Methods

all methods are static, call them with a FrequencyTimer2:: in front.

setPeriod(unsigned long microseconds)
Set the period to the specified number of microseconds. The lower bound is 1, the upper bound is a function of your clock rate, 2^18 clocks. It works out to about 30Hz on a 16MHz clock. If you think in Hertz, then pass in 1000000L/hertz.
unsigned long getPeriod()
Return the actual period. This may be different because you asked for one out of bounds, or the code had to use a clock prescaler to keep the counter in range. Unless you went out of bounds you should expect this to be within 0.5% of your requested value.
enable()
Start toggling the output pin. You still need to do a pinMode(11,OUTPUT) if you wish to actively pull up, otherwise it will just use the pullup resistor.
disable()
Stop toggling the output pin. Return it to general use. (Not PWM.)
setOnOverflow( void (*func)() )
Set the overflow function. This will be called at the end of each cycle. Set it to 0 to disable the interrupt. Note well: If your period is too fast you will lock up just handling interrupts. A simple counter incrementor will lock up somewhere under 15uS. gcc is forced to generate a lot of register moves to support C and C++ functions as interrupt handlers. More complicated functions will have higher failure periods. Remember these words when your sketch locks up.

Advantages

  • You get a stable output signal, no jitter.
  • No timing constraints in your code.
  • You can easily hook code into a periodic interrupt with a time interval of your choosing.

Disadvantages

  • It costs you timer2 and its associated PWM outputs, 11 and 3(on a atmega168).

Size

The library is about 600 bytes plus 4 bytes of SRAM.

The Code

You can retrieve this library from frequencytimer2.zip. Unpack it in your lib/targets/libraries/ directory and include it in your sketch.

FYI: You may have to remove the #if defined() statements from the CPP library for the FrequencyTimer2 to work with boards using the ATMEGA 328.

An Example

Following is my little test program. You can issue commands like

  • 12345p - set period to 12345 microseconds
  • o - turn on a simple counter as the overflow function
  • n - turn off the overflow function
  • b - print the counter from the oveflow function

#include <FrequencyTimer2.h>

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

  Serial.begin(19200);
  delay(2);
  Serial.print("Ready");

  FrequencyTimer2::setPeriod(200);
  FrequencyTimer2::enable();
}

long burpCount = 0;
extern "C" void Burp(void) {
  burpCount++;
}

void loop() {
  static unsigned long v = 0;
  if ( Serial.available()) {
    char ch = Serial.read();
    switch(ch) {
      case '0'...'9':
        v = v * 10 + ch - '0';
        break;
      case 'p':
        FrequencyTimer2::setPeriod(v);
        Serial.print("set ");
        Serial.print((long)v, DEC);
        Serial.print(" = ");
        Serial.print((long)FrequencyTimer2::getPeriod(), DEC);
        Serial.println();
        v = 0;
        break;
      case 'e':
        FrequencyTimer2::enable();
        break;
      case 'd':
        FrequencyTimer2::disable();
        break;
      case 'o':
        FrequencyTimer2::setOnOverflow( Burp);
        break;
      case 'n':
        FrequencyTimer2::setOnOverflow(0);
        break;
      case 'b':
        Serial.println(burpCount,DEC);
        break;
      case 'x':
#if defined(__AVR_ATmega168__)
        Serial.print("TCCR2A:");
        Serial.println(TCCR2A,HEX);        
        Serial.print("TCCR2B:");
        Serial.println(TCCR2B,HEX);
        Serial.print("OCR2A:");
        Serial.println(OCR2A,HEX);
#else
        Serial.print("TCCR2:");
        Serial.println(TCCR2,HEX);
        Serial.print("OCR2:");
        Serial.println(OCR2,HEX);
#endif
        Serial.print("TCNT2:");
        Serial.println(TCNT2,HEX);

        break;
    }
  }
}