[RESOLVED] Timer2 - how to set the frequency

I'm trying to get my interrupt handler function to execute 9600 times per second. This frequency actually isn't perticularly critical, anything between 7000 and 10000 hertz will be perfectly fine.

What is the ABSOLUTE bare minimum code I need to do this? My googlings have given me a few code samples but none of them have been easy to understand (I'm a beginner arduino user).

The code I currently have (found elsewhere on this forum) is thus...

#include <avr/io.h>
#include <avr/interrupt.h>

int ledpin = 2;

void setup()
{
  TCCR2A = 0;
  // set the prescaler for timer0 to 1024
  TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20);
  // enable overflow intterupts for timer0
  TIMSK2 |= (1<<TOIE2);
  // initailize the counter
  TCNT2 = 0;

  pinMode(ledpin, OUTPUT);
}

void loop()
{
  // main code
}

volatile int value = 0;
ISR(TIMER2_OVF_vect)
{
  digitalWrite(ledpin, ((value & 1) == 1) ? HIGH : LOW);
  value++;
}

Which does actually work but the frequency is far too low (something like 30 hertz). Can someone explain how I should alter this code to get a frequency of 9600 (and the theory behind that)? Or am I barking up the wrong tree completely?

I haven't checked your bit patterns for the timer setup but

16,000,000 / 1024 / 256 = 61Hz

If you toggle an OP each interrupt you get 30Hz.

You will have to

a) Use a smaller prescaler and
b) Use the output capture interrupt instead of the overflow.


Rob

With a 16 MHz clock, if you prescale it by 8, you are clocking a timer at a rate of 2 MHz (0.5 microsecond count intervals). For Timer2 in CTC mode, you can generate frequencies from a couple of kHz to something in the neighborhood of 100 kHz

I won't make any claim for "absolute minimum" code but, if you leave out the print statements that I put in for verification, it's just about as low as I care to go:

//
// Use of timer2 to generate a signal for a particular frequency on any output pin
//
// davekw7x
//
#include <avr/io.h>
#include <avr/interrupt.h>

const int ledPin = 13;

// Constants are computed at compile time

// If you change the prescale value, it affects CS22, CS21, and CS20
// For a given prescale value, the eight-bit number that you
// load into OCR2A determines the frequency according to the
// following formulas:
// Note that, due to the time spent in the ISR, it's not practical to generate
// a waveform with period much less than 20 microseconds.  You can speed it up
// a little by using bit manipulation on the output port instead of digitalWrite.
//
// Better yet: Enable "Toggle Output on Compare Match" and don't do any port
// reading or writing in the ISR.  For this scheme, uou will have to use an
// Output Compare pin on the ATmega instead of an arbitrary output pin.
//
const int prescale  = 8;
const int ocr2aval  = 127;

// The following are scaled for convenient printing
//
// Interrupt interval in microseconds
const float iinterval = prescale * (ocr2aval+1) / (F_CPU / 1.0e6);

// Period in microseconds
const float period    = 2.0 * iinterval;

// Frequency in Hz
const float freq      = 1.0e6 / period;

void setup()
{
    pinMode(ledPin, OUTPUT);
    Serial.begin(9600);
    //
    // I like to disable global interrupts while initializing counter registers.
    // That may not be necessary, but...
    cli();

    // Set Timer 2 CTC mode and prescale by 8
    //
    // WGM22:0 = 010: CTC Mode
    // WGM2 bits 1 and 0 are in TCCR2A,
    // WGM2 bit 2 is in TCCR2B
    //
    TCCR2A = (1 << WGM21);

    // Set Timer 2  No prescaling
    //
    // CS22:0 = 010: prescale = 8
    // CS2 bits 2:0 are all in TCCR2B
    TCCR2B = (1 << CS21);

    // Enable Compare-match register A interrupt for timer2
    TIMSK2 = (1 << OCIE2A);

    // This value determines the interrupt interval
    OCR2A = ocr2aval;

    // Enable global interrupts: Ready to run!
    sei();

    Serial.print("Interrupt interval = ");
    Serial.print(iinterval);
    Serial.println(" microseconds");
    Serial.print("Period             = ");
    Serial.print(period); 
    Serial.println(" microseconds");
    Serial.print("Frequency          = ");
    Serial.print(freq); 
    Serial.println(" Hz");

}


void loop()
{
    // main code
}

// ISR For Timer 2 Compare-match overflow
volatile unsigned char value = 0;
ISR(TIMER2_COMPA_vect)
{
    digitalWrite(ledPin, (++value)&1);
}

Output on serial port from my 16 MHz ATmega328p board:


[color=#0000ff]Interrupt interval = 64.00 microseconds
Period             = 128.00 microseconds
Frequency          = 7812.50 Hz
[/color]

Period/Frequency verified with 'scope and counter.

Now, it "just turns out" that for this setup (16 MHz CPU clock prescaled by 8), the output period in microseconds is OCR2A + 1.

Is this some kind of amazing cosmic coincidence? No, it isn't. Think about it! (See Footnote.)

Anyhow...

By my reckoning, an OCR2A value of 103 should give an output period of 104 microseconds, which is going to be about as close to 9600 Hz that you are going to get with this scheme. I'll leave that verification up to you.

Regards,

Dave

Footnote:
Are you a Terry Pratchett fan, or is your choice of "wossname" as a screen ID just some cosmic coincidence?

Terry has tried to teach us through the years that the universe doesn't supply us with answers...
Just more questions.

Wow Dave, your reply was terrific. Is there a bit of documentation that explains what all the cryptic acronyms mean?

Mystery jargon aside, I'll try your suggestion tomorrow, I think you've just solved my problem. Thank you very much, sir.

Sometimes I wish there was a "Haynes" manual for the Arduino chips :slight_smile: Then wouldn't have to ask such embarrasing questions.

Anyway, for interest's sake I'll tell you a bit more about my silly little project...

I'm teaching myself electronics more or less from scratch, and the arduino (Uno) is bridging the huge gulf between my patchy computer programming background and my non-existent hardware engineering knowledge. I do like the Arduino platform, but I am constantly falling foul of it's documentation.

My project is to create a 8x24 LED display panel that is driven by an arduino. I'm doing all the IO multiplexing the hard way (lots of transistors :)) and using software to control the POV strobing. I've got everything planne out in my head and now I'm slowly getting it all committed to copper and solder! Fun.

Mega Pratchett fan actually. Got my username from "(Faust) Eric" many years ago. Have you got the latest book, "I Shall Wear Midnight"? It's a lot more dark and sinister than the other Aching titles.

Is there a bit of documentation that explains what all the cryptic acronyms mean?

The standard AVR docs, see here

http://atmel.com/dyn/products/product_card.asp?PN=ATmega168PA

The acronyms are a bit daunting at first, but it gets easier.


Rob

Is there a bit of documentation that explains what all the cryptic acronyms mean?

Atmel Data Sheet, Revision 8271C, updated August, 2010 The Timer/Counter2 stuff is in Chapter 17.

Have you got the latest book, "I Shall Wear Midnight"?

I'm a po-boy. I have to wait until the paperback editions get to the used book stores. I'm a Discworld fan, and I've just started Unseen Academicals (See Footnote), and the bit about the universe giving more and more questions instead of answers is from Glenda's inner thoughts.

Regards,

Dave

Footnote:
The nested footnotes on Page 1 are worth the price of all 37 Discworld books. You know: The comments on Tyrannies, Monarchies and Democracies as practical forms of government.

Thanks for that code Dave, it works beautifully. Testing with a single LED for brightness: on for 1 time unit, off for 191 time units gives a very usable 40 hertz frame rate! The brightness is good - not blinding but not dim either, no noticable flickering (unless you turn your head quickly).

In the final build, this interrupt will be responsible for mapping individual bits from a 24 byte memory buffer onto a 8x24 grid of high brightness blue LEDs.

I'm having to overdrive the LEDs in order to compensate for the loss in apparent brightness due to the low duty cycle (~0.5%). I'm using a 100[ch937] resistor in series with each of the 8 rows of resistors, this combined with the fairly long path the circuits use should (I hope) be a safe power draw from the arduino's DIO pins.

The best thing is that I can forget about the timing complexities now and just concentrate on having the arduino draw patterns onto the 24byte image buffer in memory, this will make the code a lot simpler and more robust. Should be fairly low power consumption too.

Thanks also for the links to the relevant pages in the datasheet.

I really appreciate your help, thank for your time. :slight_smile:

Adam.

relevant pages in the datasheet

To be honest, the first twenty or thirty times I went through that stuff I missed a few details, but I kept at it. (See Footnote.)

My hope is that, by seeing a complete working example (including what I hope are a few helpful comments), some people might actually make some sense of it.

Regards,

Dave

Footnote:
"When life hands you a mess of spaghetti,
just keep pulling until you find the meatball."
---Terry Pratchett
Making Money