I landed on this thread because I, too, wanted to know how to program an Arduino to output "tones" lower than 31Hz1 [quotes because, much lower than 31Hz and it's no longer a "tone" ;)]. Jurs was kind enough to provide a hint, but that "kindness" didn't extend to an explanation of the hows & whys, leaving us with a way to only generate 7.8301 Hz, which, in most cases, will have NO real value!
So, below is the knowledge necessary to have a go at making this work for OTHER sub-31Hz frequencies, in case anyone else shows up, here, wanting a frequency other than 7.8301 Hz.
=========================================================================
First, I verified that the jurs code does, indeed, produce a freq of 7.83Hz2.
Next, after pursuing the Atmega328P datasheet [http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf], and determining that this is a Classic Output Compare method of generating a frequency output3, I proceeded to work out the relationship between those numbers, being loaded into those OCR1A registers, and the frequency being generated.
So, here's how it works. Let's start with Timer1. The MCU Clock Frequency, for an official Arduino UNO is 16MHz. Which, in the case of the configuration the Arduino uses, is also the clkI/O frequency [the frequency used to clock Timer1]. Also, the Timer 1 Prescale is set to 8, thus the reason for the tone() function minimum frequency:
16MHz / 8 / 65535 = [b]30.52Hz[/b]
!
So, with this under my belt, I can begin to reverse engineer the jurs code.
Near the end of in the datasheets section 16 [16-bit Timer/Counter1 with PWM], is a description of the various registers used to configure Timer1. They are:
The TCCR1 trio [TCCR1A, TCCR1B, TCCR1C], which are used to configure Timer1 [TCCR1C not used in this context--the Arduino configuration doesn't need to change]. The OCR1A pair [OCR1AH, OCR1AL], that make up the high and low 8-Bit parts of what is actually a 16-Bit register. The OCR1A 16-bit register is the "Output Compare" value. Timer1 begins counting at 0, and counts up until it either overflows [counts from 65535 to 0], or in this case, until the count matches the value stored in the OCR1A register. That's the key to understanding how to generate a particular frequency.
So, how do we go below 31Hz? By lowering the frequency that clocks Timer1. How is that achieved? By increasing the Prescale, so the clock frequency is divided down even more. The default setting is 8, which is the Prescale value used by the tone() library function.
The next largest Prescale value is 64. And, that is set by the following bit of code:
TCCR1B = 0x0B;
This sets the three least significant bits [CS12, CS11 & CS10] to: 011
It also, sets the WGM12 bit, for reasons I'm too lazy to verify, but it has to do with configuring the Waveform Generation Mode. The way jurs set it, it works, so what the heck!
Now that the Prescale is set to "divide by 64", the available range of frequencies are now:
Highest: 16MHz / 64 / 1 / 2 = [b]125kHz[/b]
[theoretically -- I tried it and got 33.87kHz]
Lowest: 16MHz / 64 / 65535 / 2 = [b]1.907Hz[/b]
Why that last divide by 2? Because, in the jurs code, each Output Compare corresponds to a half-cycle. So, dividing it by 2 is akin to adding together the two half-cycles, to arrive at the actual whole frequency.
If you want to go lower, there are two more prescale values: 256:
TCCR1B = 0x0C;
and, 1024:
TCCR1B = 0x0D;
And, finally, how to calculate an Output Compare value, to set a target frequency [i.e. the desired output "tone" frequency]:
OC[sub]Value[/sub] = 16MHz / (Prescale * Freq[sub]OUT[/sub] * 2) = 16x10[sup]6[/sup] / (64 * Freq[sub]OUT[/sub] * 2)
So, for an output frequency of, say, 5Hz:
16x10[sup]6[/sup] / (64 * 5 * 2) = 25000 = 0x61A8
Plug that into the code like so [assigning the upper 8-Bits to the "H" register, and the lower 8-Bits to the "L" register]:
OCR1AH = 0x61;
OCR1AL = 0xA8;
and bingo! 5.000Hz!!
And, here's the code, again, with some added niceties:
// Parameters
int targ_freq = 5; // Target output "tone" frequency in Hz
// Configuration
int Port__low_tone = 13; // The port on which to output the "tone".
volatile int OC = 0; // The Output Compare value that will be nicely calculated for you, below!
void InitTimer1()
{
noInterrupts();
TCCR1A = 0x80;
TCCR1B = 0x0B;
OCR1AH = OC >> 8;
OCR1AL = OC;
TIMSK1 = (1<<OCIE1A); // enable Timer 1 interrupts
interrupts();
}
ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
digitalWrite(Port__low_tone,!digitalRead(Port__low_tone));
}
void setup()
{
pinMode(Port__low_tone,OUTPUT);
// Calculate the Output Compare value that will generate the given Target Frequency. This
// value will be used by the InitTimer1() function.
OC = 125000/targ_freq; // Simplified 16000000/64/targ_freq/2;
InitTimer1();
}
void loop()
{
}
=========================================================================
- According to the official Arduino tone() function reference, the lower limit is 31Hz, not "32Hz", as jurs stated. And, I verified this with an actual UNO: "31" produces tone at 30.843Hz , "30" produces tone at 1.943kHz. BTW: This reminds me of college Physic Lab, where we always had to account for a variation between theoretical and actual. The math says the frequency should be: 30.52Hz. Yet, my scope shows 30.843Hz! So, I don't know...maybe the crystal is off on my Arduino, or my scope isn't calibrated?!? When I measure, using the Freq Counter on my multimeter, the 1kHz test signal on my scope, I get 1.000kHz. And the counter on my scope reads 1.00002, so I'm leaning towards an imprecise crystal... but, actually, I doubt that, because of how dead-on the other results are -- I'm not in school, anymore, so leave me alone!
- The frequency counter, on my scope, doesn't work with frequencies below 15Hz, so I used the "Cursor" feature, and got a very rough measure of 7.81Hz
- Ooh, listen to me, sounding all experty. I friggin' just learned about "Output Compare" a few days ago, as an alternative to the PIC "Numerically Controlled Oscillator" [NCO], a feature annoyingly absent from their 16-Bit line! So, I posted a question about this, on the Microchip Forum, and was tutored on "Output Compare" as an alternative. But, ennnh!... not quite the same! On the 8-Bit MCUs that offer NCO, 20-Bit registers are used, so you can get a very wide frequency sweep, which, BTW, is a requirement for the project I'm working on [MIDI Controlled Oscillators for a Digitally Configurable Analog Music Synthesizer]. Oh, sure, you can cascade the critters, and get that large register effect, but for frik-sake, I need three wide sweep range NCOs, and after cascading, I would be left with only ONE, so ennnh! NOT THE SAME!!
- In other words, as indicated in Table 16-5 [in the datasheet], CSn2 & CSn1 reset to 0, and CSn0 set to 1. The 'n' refers to the Timer number. For Timer1 n=1. For Timer2, n=2.