How do I set TCNT2 for XXXXX Hz

I am trying to setup an interrupt on my Uno board that runs every 16,000Hz. I understand all the rest of the code, but I dont get:

TCNT2=455;    //455 outputs 1.007khz
TCCR2B = B00000010; //what does this do?

How does 455 = 1.007KHz? What is the math behind that?
The code I would like to build:

int hz_interrupt = 16000;
//code goes here to convert hz_interrupt to timer_set_value
TCNT2=timer_set_value;

I am trying to setup an interrupt on my Uno board that runs every 16,000Hz

Sounds good.

I understand all the rest of the code

What code? Did you forget to paste something?

but I dont get: TCNT2=455; //455 outputs 1.007khz

Setting the counter value effects just one "loop" of the counter. When the counter overflows or clears, it will start from zero. This will certainly not give you what you want.

TCCR2B = B00000010; //what does this do?

Sets the "prescaler" to eight and the WGM22 bit to zero.

How does 455 = 1.007KHz? What is the math behind that?

Hard to say without the rest of the code.

Full code

byte toggle = 0;

void setup()
{
  noInterrupts();
  pinMode(13, OUTPUT);

  TCNT2=455;    //455 outputs 1.007khz
  TCCR2B = B00000010;
  //Timer2 Overflow Interrupt Enable
  TIMSK2 = 1<<TOIE2;
  interrupts();
}

ISR(TIMER2_OVF_vect) 
{
  TCNT2=455;  
  toggle ^= 1;
  digitalWrite(13, toggle);
}

void loop()
{
  delay(50000);
}

I am not sure if I am reading my scope correctly, but I think I am currently getting a 2khz pulse.

First, some macros to (hopefully) make the code more readable...

#define MASK1(b1)                         ( (1<<b1) )
#define MASK2(b1,b2)                      ( (1<<b1) | (1<<b2) )
#define MASK3(b1,b2,b3)                   ( (1<<b1) | (1<<b2) | (1<<b3) )
#define MASK4(b1,b2,b3,b4)                ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) )
#define MASK5(b1,b2,b3,b4,b5)             ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) )
#define MASK6(b1,b2,b3,b4,b5,b6)          ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) | (1<<b6) )
#define MASK7(b1,b2,b3,b4,b5,b6,b7)       ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) | (1<<b6) | (1<<b7) )
#define MASK8(b1,b2,b3,b4,b5,b6,b7,b8)    ( (1<<b1) | (1<<b2) | (1<<b3) | (1<<b4) | (1<<b5) | (1<<b6) | (1<<b7) | (1<<b8) )

These are the steps to setting up a timer...

  1. Disable the timer while we muck with it. This amounts to setting the "Clock Select" to zero (set CS bits to zero)...

TCCR2B = TCCR2B & ~ MASK3( CS22, CS21, CS20 );

  1. Configure the waveform we want. To get a precise frequency, we need the timer to automatically clear at a particular value. CTC is the Waveform Generation Mode we want (set WGM bits to 010)...

TCCR2A = (TCCR2A & ~ MASK2( WGM21, WGM20 )) | MASK1( WGM21 );
TCCR2B = (TCCR2B & ~ MASK1( WGM22 ));

  1. Configure the action to perform whan a compare match occurs. We need the timer to be disconnected from the output pins (COM2A and COM2B set to zero)...

TCCR2A = (TCCR2A & ~ MASK4( COM2A1, COM2A0, COM2B1, COM2B0 ));

  1. We don't want an interrupt to occur until the timer has gone around at least once so we'll clear the interrupt flag...

TIFR2 = (TIFR2 & ~ MASK1( OCF2A ));

  1. At this point, we'll go ahead and enable the interrupt. The timer is stopped so an interrupt won't be generated until we've finished configuring the timer...

TIMSK2 |= MASK1( OCIE2A );

[edit]6. Ensure the counter starts from zero the first time...

TCNT2 = 0;[/edit]

  1. Now for the Clock Select (prescaler) and Output Compare values. The processor's clock (16MHz) is divided by these two values combined. The formula is...

16000000 / (prescaler * (output_compare + 1)) = frequency

We want a prescaler that gets us as close to the goal (16,000 Hz) as possible without going above the goal. Our choices for this timer are 1, 8, 32, 64, 128, 256, and 1024. A prescaler of 8 is the best we can do (7812.5 Hz). Next, we choose an output compare that gets us as close to the goal as possible. 124 is the value. So, by using a prescaler of 8 and an output compare of 124, an interrupt will be generated every...

16000000 / (8 * (124 + 1)) = 16000 times per second

OCR2A = 124;
TCCR2B = (TCCR2B & ~ MASK3( CS22, CS21, CS20 )) | MASK1( CS21 );

  1. As soon as we set the Clock Select, the timer starts running. There's just one more change: the interrupt generated is a Timer/Counter2 Compare Match A...

ISR(TIMER2_COMPA_vect)
{
// Put your code here.
}

I believe the code can be reduced to this...

TCCR2B = 0;
TCCR2A = _BV( WGM21 );
TIFR2  = 0;
TIMSK2 = _BV( OCIE2A );
OCR2A  = 124;
TCCR2B = _BV( CS21 );

If it works, please report back for the benefit of others who might find this thread.

That was EXACTLY what I was looking for. Thank you for the great write up. I will give it a test tonight, and upload my tested code and comments.

Perhaps this should become an article for the playground? Information on setting up timers is... lacking.

The long example did not work. The short example works great. Only one question left.

//Do not know what it does, but it speeds up the wave form
  TCCR2A = _BV( WGM21 );

Does any one have a functional oscilloscope to test if we are getting a 16KHZ square wave? If not, my new scope will be here in a week.

I added a little math to make setting the hz much simpler.
Here is what the code now looks like:

int i = 0;
byte toggle = 0;

//16000000 / (8 * (124 + 1)) = 16000 times per second
unsigned int intrupt_in_hz = 16000;
float set_timer = -(1)*(intrupt_in_hz - 2000000)/intrupt_in_hz;

void timer_setup()
{
  //Disable the timer while we muck with it.
  TCCR2B = 0;
  
  //Do not know what it does, but it speeds up the wave form
  TCCR2A = _BV( WGM21 );
  
  //Enable intrupt
  TIMSK2 = _BV( OCIE2A );
  
  //Make sure the timer starts at zero
  TIFR2  = 0;
  
  //Set the prescaler to 8
  TCCR2B = _BV( CS21 );
  
  //16000000 / (8 * (124 + 1)) = 16000 times per second
  OCR2A = set_timer;
}

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);

  timer_setup();
}

ISR(TIMER2_COMPA_vect)
{
  noInterrupts();
  toggle ^= 1;
  digitalWrite(13, toggle);
   
  i++;
  if(i==32)
    i=0;
  interrupts();
}

void loop()
{
  Serial.println(set_timer, BIN);

  delay(500);
}

Dingbat (you said it - not me):

For some insight into what is happening you might want to go to http://web.alfredstate.edu/weimandn. Scroll down to the Atmel ATmega Subsystem Diagrams and follow the Reference Diagrams link to the Timer/Counter diagrams.

Don

The long example did not work

It what way? Timer didn't run? Too slow?

The short example works great

That is strange. I'm glad it worked for you.

Does any one have a functional oscilloscope to test if we are getting a 16KHZ square wave? If not, my new scope will be here in a week.

Please report back what you find.

Coding Badly: Timer did not run. Got nothing on pin 13.

Floresta: Thanks for the link!

How odd. Once I corrected the syntax error (post also corrected), it worked fine. Oh well.

Here is my take on this.
16MHz clock to 16Khz means dividing by 1000.
Pick timer divider of 8. This means we need a further divider of 125.
I just add 125 to OCR2A every time an interrupt occurs.
Works fine.
Simple routine flashes led at 8.000KHz to show it works

Note to CB: I'm still thinking about 124 but see for yourselves it definitely needs to be 125 in this setup

/* SIMPLE INTERRURP AT 16KHZ

Platform: Arduino pro Mini 16MHz
IDE: Arduino 0021
V0.1 Nov 18 2010 Set up timer2 interrupt at 625nSec (16.000Khz)

*/
//--------------------------------------------------------------------------------------------
#include <EEPROM.h>
volatile int flashCounter;
volatile boolean doRoutinesFlag=0;
boolean ledState=1;
#define testLed 13

//------------------------------------ setup ------------------------------------------------
void setup(){
//set up timer 2 control registers here
OCR2A = 124; //initialise compare register for first interrupt
TCCR2A = B00000000; //select Normal Mode,freerunning counter, no connection to hardware pins
TCCR2B = B00000010; //normal, prescaler equals clock/8. With 16Mhz clock this gives an 2MHz clock to the TCNT2
TIMSK2 = B00000010; //generate interrupt on output compare match A
pinMode(testLed,OUTPUT);
pinMode(12,OUTPUT);
}
// --------------------------------- timer interrupt --------------------------------------
ISR (TIMER2_COMPA_vect){

OCR2A +=125; //this generate an interrupt every .

PORTB=PORTB | B00010000; // turn on pin 12 every interrupt
PORTB=PORTB &!B00010000; // turn it off. It's enough width to see on CRO and frequency counter

//PUT INTERRUPT ROUTINES HERE
flashLed();

}
// --------------- end timer interrupt ---------------

void loop(){
}

//-------------------------------------------------------
void flashLed() { // just flash led at 8.000Khz
ledState= !ledState;

if(ledState==1){
PORTB=PORTB | B00100000;
}
else{
PORTB=PORTB &!B00100000;
}

}

I just add 125 to OCR2A every time an interrupt occurs.

A more conventional approach would be to leave OCR2A alone and clear TCNT2. With this approach the reason for the CTC (Clear Timer on Compare Match) mode becomes evident. It looks like Mode 2 would be good for you.

Don

leolfs: Thanks for the code/different view point. I will be pulling some of your ideas in.

Time to post all the information. I am trying to building a metal detector. http://www.geotech1.com/forums/showthread.php?p=119001&posted=1

I need to generate a sine wave (Or some other funny wave form) at 4~16KHz. Here is the fun part. In order to generate a sine wave at 16KHz, I need to run at 512KHz (16hz*32points). I have 32 data points that i need to load into the DAC in order to generate the wave form at 16KHz. Something like this: http://blog.makezine.com/archive/2008/05/makeit_protodac_shield_fo.html

My code currently looks like this (See below), but it will change, lots! I cant really test any of it until my new scope comes in next week. So for now I am just trying to clean/speed things up.

byte wavetable[32] = {128, 154, 178, 201, 220, 236, 247, 254, 255, 251, 242, 228, 211, 190, 166, 141, 115, 90, 66, 45, 27, 14, 5, 1, 3, 9, 20, 36, 56, 78, 103, 128};
byte processed_wavetable[256];
byte i = 0;
byte toggle = 0;

//16000000 / (8 * (124 + 1)) = 16000 times per second
//unsigned int intrupt_in_hz = 16000;
unsigned int intrupt_in_hz = 512000;
float set_timer = -(1)*(intrupt_in_hz - 2000000)/intrupt_in_hz;

void decompile_wavetable()
{
  //Speed things up.  Take the compleate 
  //wavetable and breake it down.
  //No more need to do all the bit shifting in the
  //intrupt call.
  byte a, n;
  byte k = 0;
  for (byte y = 0; y <= 31; y++)
  {
    n=wavetable[y];
    for (byte z = 0; z <= 7; z++)
    {  
      //Bitshift and load into processed_wavetable.
      a = n;
      a = a << z;
      a = a >> 7;
      processed_wavetable[k] = a;
      k++;
    }
  }
}

void timer_setup()
{
  //Disable the timer while we muck with it.
  TCCR2B = 0;
  
  //Do not know what it does, but it speeds up the wave form
  TCCR2A = _BV( WGM21 );
  
  //Enable intrupt
  TIMSK2 = _BV( OCIE2A );
  
  //Make sure the timer starts at zero
  TIFR2  = 0;
  
  //Set the prescaler to 8
  TCCR2B = _BV( CS21 );
  
  //16000000 / (8 * (124 + 1)) = 16000 times per second
  OCR2A = set_timer;
}

void setup()
{
  Serial.begin(9600);
  
  //setup output pins 6~13
  for (byte z = 6; z <= 13; z++)
  {
    pinMode(z, OUTPUT);
  }
  decompile_wavetable();
  timer_setup();
}

ISR(TIMER2_COMPA_vect)
{
  noInterrupts();
  toggle ^= 1;
  digitalWrite(13, toggle);
  
  /*byte table_block=(i*8);
  
  //Set all the digital pins
  digitalWrite(6, (table_block + 0));
  digitalWrite(7, (table_block + 1));
  digitalWrite(8, (table_block + 2));
  digitalWrite(9, (table_block + 3));
  digitalWrite(10, (table_block + 4));
  digitalWrite(11, (table_block + 5));
  digitalWrite(12, (table_block + 6));
  digitalWrite(13, (table_block + 7));*/
   
  i++;
  if(i==32)
    i=0;
  interrupts();
}

void loop()
{  
  //Serial.readin stuff
  //Needs to watch for:
  //hz=1234
    //If new hz, call timer_setup()
  //wt=123,23,1,23,123
    //if new wavetable, call decompile_wavetable()
  //pt, print current hz, and wave table.
  
  delay(5000000);
}

TCNT2=455;

I think what the original code is trying to do is a relatively common way to generate frequencies with a "dumb" timer that only interrupts on "overflow."

Say you want a 50Hz waveform. That's 10ms on followed by 10ms off, so you need a 10ms interrupt. You adjust the timer prescaler to a convenient timebase; say 1ms per tick. Then, in your ISR (where the timer has just wrapped to 0), you reset the timer count to (overflow-10), so that your next interrupt will come after 10 more clock ticks.

Now, the original code is pretty suspect, not the least because it seems to be trying to set an 8 bit register (TCNT2) to a value that doesn't fit in 8 bits... And fancier timers give you more options, as other have described. But if you're porting code from elsewhere, this is probably the sort of thing they were trying to do...

16 000 000 / 512 000 = ~ 31.25 machine instructions per interrupt.

ISR(TIMER2_COMPA_vect)
{
  [glow]noInterrupts();  // This is not necessary.  Remove it.[/glow]
  toggle ^= 1;
  digitalWrite(13, toggle);
  
  /*byte table_block=(i*8);
  
  //Set all the digital pins
  digitalWrite(6, (table_block + 0));
  digitalWrite(7, (table_block + 1));
  digitalWrite(8, (table_block + 2));
  digitalWrite(9, (table_block + 3));
  digitalWrite(10, (table_block + 4));
  digitalWrite(11, (table_block + 5));
  digitalWrite(12, (table_block + 6));
  digitalWrite(13, (table_block + 7));*/
  
  i++;
  if(i==32)
    i=0;
  [glow]interrupts();  // This is a VERY BAD IDEA.  Remove it.[/glow]
}

Thre are considerably more than 32 machine instructions in your ISR. You're asking the processor to do too much. At a minimum, you're going to have to avoid digitalWrite / write directly to the port. Your best bet is to write an entire port in one operation.

The original code was just a hack based on what I could find with Google. I did not understand enough to setup the timers based on the data sheets. At that point I had to ask for help. I am very happy that so many people were here to help show how the timers work.

Leolfs has the cleanest timer setup code, with good comments.
Coding Badly has a great description of what is going on.
Floresta gave me a link to show how it all works.

At this point I have an understanding of how to get the timer to do what I want, now I need to figure out how to speed up the DAC process. :slight_smile:

Can I set the whole "PORTD" with something like this(See below)? I know I will loose pin 0, 1, serial IO. But I can move those function other places. Would this really be 2 clock cycle, instead of 18+?!?!

PORTD=wavetable[i];

//instead of

byte table_block=(i*8);
digitalWrite(6, processed_wavetable[table_block + 0]);
digitalWrite(7, processed_wavetable[table_block + 1]);
digitalWrite(8, processed_wavetable[table_block + 2]);
digitalWrite(9, processed_wavetable[table_block + 3]);
digitalWrite(10, processed_wavetable[table_block + 4]);
digitalWrite(11, processed_wavetable[table_block + 5]);
digitalWrite(12, processed_wavetable[table_block + 6]);
digitalWrite(13, processed_wavetable[table_block + 7]);

Can I set the whole "PORTD" with something like this(See below)?

Yes.

Would this really be 2 clock cycle

No. It will be a few more than 2 because you're referencing an array element.

instead of 18+?!?!

Each digitalWrite is indeed several machine instructions. A few instructions to setup the call. One to make and one to return from the call. Plus the many instructions in digitalWrite.

Great! Any clue how many clock cycles?

A lookup table. Much faster to find good values, and how many clock cycles we have to play with.

timer Hz KHz MHz 32 sample in Hz 32 sample in KHz 32 sample in MHz Machine instruction per interupt
1 1000000.0 1000.0 1.0 32000000.0 32000.0 32.0 0.5
3 500000.0 500.0 0.5 16000000.0 16000.0 16.0 1.0
4 400000.0 400.0 0.4 12800000.0 12800.0 12.8 1.3
7 250000.0 250.0 0.3 8000000.0 8000.0 8.0 2.0
9 200000.0 200.0 0.2 6400000.0 6400.0 6.4 2.5
15 125000.0 125.0 0.1 4000000.0 4000.0 4.0 4.0
19 100000.0 100.0 0.1 3200000.0 3200.0 3.2 5.0
24 80000.0 80.0 0.1 2560000.0 2560.0 2.6 6.3
31 62500.0 62.5 0.1 2000000.0 2000.0 2.0 8.0
39 50000.0 50.0 0.1 1600000.0 1600.0 1.6 10.0
49 40000.0 40.0 0.0 1280000.0 1280.0 1.3 12.5
63 31250.0 31.3 0.0 1000000.0 1000.0 1.0 16.0
79 25000.0 25.0 0.0 800000.0 800.0 0.8 20.0
99 20000.0 20.0 0.0 640000.0 640.0 0.6 25.0
124 16000.0 16.0 0.0 512000.0 512.0 0.5 31.3
127 15625.0 15.6 0.0 500000.0 500.0 0.5 32.0
159 12500.0 12.5 0.0 400000.0 400.0 0.4 40.0
199 10000.0 10.0 0.0 320000.0 320.0 0.3 50.0
249 8000.0 8.0 0.0 256000.0 256.0 0.3 62.5
319 6250.0 6.3 0.0 200000.0 200.0 0.2 80.0
399 5000.0 5.0 0.0 160000.0 160.0 0.2 100.0
499 4000.0 4.0 0.0 128000.0 128.0 0.1 125.0