1
\$\begingroup\$

If this question should be in StackExchange instead of here I can delete here and post there.

Using an Arduino Duemilanove (Atmega328) I am trying to generate a square wave (50% duty cycle) of frequency somewhere between 500 Hz and 200 KHz, as entered by a user. I do not need any variable PWM duty cycle, just a simple square wave. Also I can not use Timer0 since modification of that affects delay() and other time related functions of the Arduino.

The timer calculations for prescaler and top counter have me very confused, can someone point me to an algorithm to identify which combination of prescaler and top values will give the closest output frequency to the specified one? In many cases, two or more combinations of prescaler and top give similar output frequency results but neither is exact.

For example user enters value of 98350 Hz. How would my Arduino code find out what prescaler and top counter value to set the Timer 2 registers to, for the closest match to that frequency?

\$\endgroup\$
  • \$\begingroup\$ You might get responses in StackOverflow (not StackExchange) - or maybe not there either. \$\endgroup\$ – Anindo Ghosh Nov 14 '12 at 13:28
  • 2
    \$\begingroup\$ I think this is a legitimate question for here. However, it is very specific to the Atmel hardware, so only those people familiar with it can help. Enough people use Atmels that you should get some answer. \$\endgroup\$ – Olin Lathrop Nov 14 '12 at 13:38
  • \$\begingroup\$ @AnindoGhosh Yes, I mistook StackExchange for StackOverflow. Thanks. \$\endgroup\$ – ExcitingProjects Nov 14 '12 at 16:37
2
\$\begingroup\$

The Arduino Playground Timer1 library does what you describe: Set Timer1 to whichever period (i.e. 1/f) is specified. You can either examine the code which is on GitHub to build your own version, or use the library directly.

If using Timer1 directly, these lines will generate the 50% duty cycle square wave required:

#include <TimerOne.h>

setup() {
    pinMode(squareOut, OUTPUT);    // squareOut can be either 9 or 10 on Duemilanove
    Timer1.initialize();
    // Other set-up code
}

setFrequency(frequency) {
    Timer1.pwm(squareout, 512, (long) 1000000L / frequency); // Duty cycle = 0-1023
}

The library uses the method described by @JimParis in the answer above, to determine the prescaler and Top values within TimerOne::setPeriod.

If you generate a more optimized version of that bit of code from the library, please share, as I would like to use it.

\$\endgroup\$
  • \$\begingroup\$ Thank you sir for the library suggestion. I want to write my own code but want to see how experts did it. The library code already uses right-shift at each checking so I cannot think of more optimization, it is quite nice. I will try your code now and see if some problems remain. \$\endgroup\$ – ExcitingProjects Nov 15 '12 at 16:32
  • \$\begingroup\$ Yes it works and very stable and exact frequency too. Much better than using ISR. I wish I could accept both the answers. Thank you both! I now have my own version of the code written in Arduino rather than the CPP version but I could read the CPP code of library and understand it. Thank you, thank you! \$\endgroup\$ – ExcitingProjects Nov 15 '12 at 17:02
2
\$\begingroup\$

You will get the most accurate result by using the smallest prescaler you can. The only reason you can't always use the smallest prescaler is because then the TOP value won't fit into your 16 bit target register (like OCR1A). So the general algorithm is:

  • For each prescaler value N (1, 8, 64, 256, 1024):
    • Calculate TOP = (f_clk / (N * f_pwm)) - 1
    • If TOP <= 0xFFFF, you're done.

(You'll want to consider the extremes, too; TOP=0 or TOP=1 probably doesn't actually work for generating PWM, for example.)

\$\endgroup\$
  • \$\begingroup\$ Thank you, I will try coding this and see if some problems remain. \$\endgroup\$ – ExcitingProjects Nov 14 '12 at 16:36
  • \$\begingroup\$ Just to clarify, you probably want to use "Fast PWM" mode WGR13:10 = 14, in which case your frequency and TOP value set in ICR1 and your duty cycle would be set in OCR1A. \$\endgroup\$ – Jim Paris Nov 14 '12 at 16:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.