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?

user174174
  • 189
  • 2
  • 9
  • 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

2 Answers2

5

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.

Nick Gammon
  • 38,184
  • 13
  • 65
  • 124
  • 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 09: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 09: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
  • I had a situation where calling tone() "broke" my IR Receiver. (Specifically, after calling tone(), the IR Receiver viewed all incoming signals as 0s.) This code fixed it! Thanks! – user3810626 Feb 07 '22 at 06:50