5

I was trying to use the tone function for another project but I found that it also generates an undesired frequency (not the harmonics above of square wave it generates) but a lower frq square wave. Is this normal or somethings wrong with my implementation? the code is pretty simple:

void setup()
{
    pinMode(7,OUTPUT); //set the pin as output
    tone(7,10000); //start a 10KHz tone
    while(true); //stay here
}

void loop()
{ }

hardware was an Uno also tried different pins above but same results. first I tried a 470R and an 8R speaker. then guess maybe the inductance, or something else causing it so I used a piezo buzzer as seen below:

Pin(x)--->10KR--->piezo--->GND

however it was the same. I measured the lower frequency as 19.55Hz for a tone(10000) and 39.05Hz for tone(40000); both are pretty annoying. what causes this and whats the workaround?

  • What happens if you remove "while(true);" from the sketch? – VE7JRO Jan 25 '18 at 23:03
  • @VE7JRO I just did that but nothing changed. guess the empty loop() replaces my while. however I moved the the tone() AND the while(true) to the loop() and I still get the same result. but with tone() only in loop() the noise is more stressed I think because it repeatedly calls the function and tone sets the frq although it's the same as previous call. – user174174 Jan 25 '18 at 23:41
  • or just a short or long jump to tone() needs more clocks. but that's not a good reason for noise when I call it once. – user174174 Jan 25 '18 at 23:54
4

I could reproduce the problem with the posted code. This will be caused by the fact that the tone library uses interrupts and has to manually toggle pins, which will always have a bit of jitter. You are better off setting up a hardware timer to generate the tones, this does not require interrupts.

For example:

#include <TonePlayer.h>

TonePlayer tone1 (TCCR1A, TCCR1B, OCR1AH, OCR1AL, TCNT1H, TCNT1L);  // pin D9 (Uno), D11 (Mega)

void setup() 
  {
  pinMode (9, OUTPUT);  // output pin is fixed (OC1A)

  tone1.tone (10000);  // 10 kHz
  }

void loop() { }

This uses a fairly small library I wrote, which you can download from http://www.gammon.com.au/Arduino/TonePlayer.zip


Library code

If you don't want to trust some zip file from a random web site, here is TonePlayer.h:

/*
 Play tones with hardware timers
 Author: Nick Gammon
 Date:   1 April 2013
*/     

#include <Arduino.h>

class TonePlayer
  {
  // addresses of output ports - NULL if not applicable
  volatile byte * const timerRegA_;
  volatile byte * const timerRegB_;
  volatile byte * const timerOCRH_;
  volatile byte * const timerOCRL_;
  volatile byte * const timerTCNTH_;
  volatile byte * const timerTCNTL_;

  public:
    // constructor
    TonePlayer (
          // ports
          volatile byte & timerRegA, 
          volatile byte & timerRegB, 
          volatile byte & timerOCRH,
          volatile byte & timerOCRL, 
          volatile byte & timerTCNTH, 
          volatile byte & timerTCNTL)
       : 
         timerRegA_  (&timerRegA), 
         timerRegB_  (&timerRegB),
         timerOCRH_  (&timerOCRH), 
         timerOCRL_  (&timerOCRL), 
         timerTCNTH_ (&timerTCNTH), 
         timerTCNTL_ (&timerTCNTH)
  { }

    void tone (const unsigned int Hz);
    void noTone ();

  };  // end of TonePlayer

/*

 PERMISSION TO DISTRIBUTE

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 and associated documentation files (the "Software"), to deal in the Software without restriction, 
 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
 subject to the following conditions:

 The above copyright notice and this permission notice shall be included in 
 all copies or substantial portions of the Software.


 LIMITATION OF LIABILITY

 The software is provided "as is", without warranty of any kind, express or implied, 
 including but not limited to the warranties of merchantability, fitness for a particular 
 purpose and noninfringement. In no event shall the authors or copyright holders be liable 
 for any claim, damages or other liability, whether in an action of contract, 
 tort or otherwise, arising from, out of or in connection with the software 
 or the use or other dealings in the software. 

 */

And here is TonePlayer.cpp:

/*
 Play tones with hardware timers
 Author: Nick Gammon
 Date:   1 April 2013
 */

#include <TonePlayer.h>

void TonePlayer::tone (const unsigned int Hz)
{
  // it takes two toggles for one "cycle"
  unsigned long ocr = F_CPU / Hz / 2;
  byte prescaler = _BV (CS10);  // start with prescaler of 1  (bits are the same for all timers)

  // too large? prescale it
  if (ocr > 0xFFFF)
    {
    prescaler |= _BV (CS11);    // now prescaler of 64
    ocr /= 64;
    }

  // stop timer
  *timerRegA_ = 0;
  *timerRegB_ = 0;

  // reset counter
  *timerTCNTH_ = 0;
  *timerTCNTL_ = 0;

  // what to count up to
  *timerOCRH_ = highByte (ocr);
  *timerOCRL_ = lowByte (ocr);

  *timerRegA_ = _BV (COM1A0);             // toggle output pin
  *timerRegB_ = _BV (WGM12) | prescaler;  // CTC
  }  // end of TonePlayer::tone

void TonePlayer::noTone ()
  {
  // stop timer
  *timerRegA_ = 0;
  *timerRegB_ = 0;  
  } // end of TonePlayer::noTone

/*
 PERMISSION TO DISTRIBUTE

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 and associated documentation files (the "Software"), to deal in the Software without restriction, 
 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
 subject to the following conditions:

 The above copyright notice and this permission notice shall be included in 
 all copies or substantial portions of the Software.


 LIMITATION OF LIABILITY

 The software is provided "as is", without warranty of any kind, express or implied, 
 including but not limited to the warranties of merchantability, fitness for a particular 
 purpose and noninfringement. In no event shall the authors or copyright holders be liable 
 for any claim, damages or other liability, whether in an action of contract, 
 tort or otherwise, arising from, out of or in connection with the software 
 or the use or other dealings in the software. 


 Example of use:

 #include <TonePlayer.h>

 ...

 TonePlayer tone1 (TCCR1A, TCCR1B, OCR1AH, OCR1AL, TCNT1H, TCNT1L);  // pin D9 (Uno), D11 (Mega)

 or, on the Mega 2560:

 TonePlayer tone3 (TCCR3A, TCCR3B, OCR3AH, OCR3AL, TCNT3H, TCNT3L);  // pin D5
 TonePlayer tone4 (TCCR4A, TCCR4B, OCR4AH, OCR4AL, TCNT4H, TCNT4L);  // pin D6
 TonePlayer tone5 (TCCR5A, TCCR5B, OCR5AH, OCR5AL, TCNT5H, TCNT5L);  // pin D46

 ...

 pinMode (9, OUTPUT);  // D9 on Uno, D11 on Mega 2560
 tone1.tone (440);     // play 440 Hz
 delay (500);          // wait half a second
 tone1.noTone ();      // stop playing

 or, on the Mega 2560:

 pinMode (46, OUTPUT);  
 tone5.tone (440);

*/

Just put those two files inside a folder "TonePlayer" inside the libraries folder inside your sketches folder, and it should all work.

The library is written for the 16-bit timers, and supports Timer 1 on the Atmega328, and Timers 1, 3, 4, 5 on the Atmega2560. Comments inside the library show how to set up the other timers.

  • 2
    1. You should probably warn that this library only works with 16-bit timers. 2. No need to split 16-bit writes into bytes, you can write *timerOCR_ = highByte(ocr);. This would save 4 bytes in the class. Since March 2005, gcc knows it must write the high byte first. – Edgar Bonet Jan 26 '18 at 10:56
  • I got that code from my page about timers which had an extra sentence mentioning the 16-bit timers, which I have edited into my reply. You are right about the 16-bit writes, but to change that now would alter the call sequence for the constructors which could break existing code, so I'll leave it and take the 4-byte penalty. :) – Nick Gammon Jan 26 '18 at 21:32
  • @edgarbonet thanks for the clarification and detailed tweaks. – user174174 Jan 27 '18 at 9:40
  • @nick thanks for providing the code, it works great. To correct myself I found that there are jitters or spikes with the tone() which increases with the frq. And a series of continius noise of with much less and constant amplitude which i couldn't track down the source. I may add a photo later. – user174174 Jan 27 '18 at 9:51
  • @user174174, when the standard Arduino tone() function changes to a new frequency, it does that in a nasty way. When creating a sound (for example a siren) it can be heard. That was also improved in the toneAC library. – Jot Jan 27 '18 at 11:24
1

Did you measure it with good equipment? How much dB is the lower frequency? Can you show a spectrum plot?

It could be the software pin toggle. The tone selects a hardware timer, which is used to generate an interrupt. The output pin is toggled in that interrupt. It is in the file Tone.cpp. That interrupt could be delayed by a timer0 interrupt (for millis).
The timer0 runs at 16MHz/(64*256). With those numbers I can not get to the interference frequency that you mentioned.
You could try to use the output of a hardware timer. For example the toneAC library generates a tone at pin 9 and 10. That is as steady as the 16 MHz crystal (very steady).

It could be the equipment that you use to test.

It could be the speaker. Some speakers generate other tones. I had a ultrasound speaker which I could hear squeeking at a much lower frequency. After a search on the internet, I found that such a speaker is known to create lower frequency whistling noises.

It could be the 5V voltage is changed somehow by the circuit.

  • 3
    You wrote: “With those numbers I can not get to the interference frequency that you mentioned.” At 10 kHz, (41×f(timer 0) − 2×f(timer 2))/2 = 19.53125 Hz. At 40 kHz, (82×f(timer 0) − f(timer 2))/2 = 39.0625 Hz. The factor 2 in the denominator comes from the fact that interrupt collisions lengthen alternatively the HIGH level (when the fall is delayed) and the LOW level (when the rise is delayed). – Edgar Bonet Jan 26 '18 at 9:37
  • 1
    @EdgarBonet, thank you. That is amazing that you could analyze that. – Jot Jan 26 '18 at 9:47
  • @jot thanks for your help, sadly I don't have any fancy equipment but I managed to record the signal with my laptop. – user174174 Jan 27 '18 at 9:57

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.