is this code the way to hack the lower than 31hz tone issue?

would division work? i hear feedback on my weather band so it might be working. but without an oscope im not sure. im using a mega 2650

//126.22Hz the frequency of the Sun. Schumann Resonances: 7.83, 14.1, 20.3, 26.4, 32.5, 38.8, 45Hz. dogs hear from 67Hz, humans from 31hz.
int Quartz1 = 2; // try to obtain 7.83hz on pin2.
void setup() {
  randomSeed(analogRead(0));   Serial.begin(9600);
  pinMode(Quartz1, OUTPUT); pinMode(Quartz2, OUTPUT);
}//end setup
void loop() {
tone(2,31.32/4,4); delay(1000);//Pin2,31.32/4=7.83hz,4seconds
}

if someone could please check with their oscope id give you some karma

TrailerTrash:
would division work?

Work for what?

Working around the lower limit of the tone library (32 Hz)?

Or working to provide a floating point number as a parameter for a function that only takes integers as paremeters?

You'd better use timer interrupts to create your frequency.

Here is some programming example for Timer1, that sets the pin-13 LED to LOW for 63.856 ms, then to HIGH for 63.856 ms, total period = 2*63.856 ms = 127.712 ms
Square wave frequency = 1000ms/127.712ms = 7.8301 Hz

void InitTimer1()
{
  //Timer1 Prescaler = 64; Preload = 15963; 
  // Actual Interrupt Time = 63.856 ms
  noInterrupts();
  TCCR1A = 0x80;
  TCCR1B = 0x0B;
  OCR1AH = 0x3E; 
  OCR1AL = 0x5B; 
  TIMSK1 = (1<<OCIE1A); // enable Timer 1 interrupts
  interrupts();
}


ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine
{
  digitalWrite(13,!digitalRead(13));
}

void setup() 
{
  pinMode(13,OUTPUT);
  InitTimer1();
}

void loop() 
{
}

thanks for that is a pretty sweet idea jurs. so that code makes a continuous frequency?
i guess its working. i will test it and show my results here soon.
it would be interesting to figure out how to make it thump like a slow heartbeat.

TrailerTrash:
thanks for that is a pretty sweet idea jurs. so that code makes a continuous frequency?
i guess its working. i will test it and show my results here soon.

The code creates a continous square wave frequency by programming the Timer1 hardware. So after activating the Timer1 interrupt, there will be a continuous frequency, no matter what other code you are running in the loop() function. The example blinks the pin-13 LED, you easily can see that. Or instead create the square wave on another pin instead by changing pin-13 to something else.

To stop the frequency generation you must disable the Timer1 interrupt.

The two PWM pins assigned to Timer1 will be not usable for PWM output or other things while using Timer1 for the frequency generation.

TrailerTrash:
it would be interesting to figure out how to make it thump like a slow heartbeat.

If you want different frequencies, you will need different timer setup.
For a "heartbeat" simulation the frequency of 7.83 Hz is a bit fast.
7.83 Hz = 60 * 7.83 BPM = 470 BPM.
That's faster than the heatbeat of a robin, I think.

The way to handle such low frequencies is to use blink-without-delay.

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()
{
}

=========================================================================

  1. 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!
  2. 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
  3. 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!!
  4. 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.
2 Likes

It was already answered 5 years ago, in reply #5.