Tone frequencies get rounded

Hello!

I am working on a project that will make stepper motors move at a certain frequency to generate sound.

I have the following two code snippets, both of which are functionally the same. One uses a class the other does not:

############NO CLASS###########

#include <Tone.h>
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, World!");
  pinMode(38,OUTPUT);
  pinMode(54,OUTPUT);
  pinMode(61,OUTPUT);
  
  digitalWrite(38,LOW);

  Tone voice;
  voice.begin(54);
  for(uint16_t i=100;i<1000;i+=100){
    voice.play(i);
    delay(100);
    voice.stop();
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

#############WITH CLASS############

#include <Tone.h>
class MotorChannel{
  public:
    Tone voice;
    
    MotorChannel(){
        pinMode(38,OUTPUT);
        pinMode(54,OUTPUT);
        pinMode(61,OUTPUT);
        
        digitalWrite(38,LOW);
        
        voice.begin(54);
    }
    void playFreq(uint16_t freq,uint16_t duration){
      voice.play(freq);
      delay(duration);
      voice.stop();
    }
};
MotorChannel lead(38,54,61);
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, World!");
  for(uint16_t i=100;i<1000;i+=100){
    lead.playFreq(i,100);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

*Please note the #include <Tone.h>. This is a library that has been discontinued, and will be attached below (hopefully).
*Hardware: ATMega2560 w/ RAMPS 1.4 board

The one with no classes runs fine. It plays a range of frequencies(expected).

The one that uses a class runs, but all frequencies get rounded. It ends up playing something like a low note twice, a mid note 7 times, then a high note. It should, however, play a range. Instead, it plays 3 frequencies.

What have I done wrong here? This is probably the weirdest issue I've ever had, and cannot wrap my head around it!

Thank you!

EDIT: I should note what the tone library does: It is a library that allows the use of multiple tones playing simultaneously using the different internal timers.

Tone.cpp (11.5 KB)

Tone.h (3.23 KB)

another classic case of trying to manipulte hardware before it is ready, I believe:

your constructor:

    MotorChannel(){
        pinMode(38,OUTPUT);
        pinMode(54,OUTPUT);
        pinMode(61,OUTPUT);
        
        digitalWrite(38,LOW);
        
        voice.begin(54);
    }

may get called before hardware is ready, thus the use of the begin() member function in the Tone library... which gets called in setup()!

Uh oh!
So, I should implement a "begin" in my class, and call it in setup?

I'll give that a go. Thanks!

Fuzzyzilla:
Uh oh!
So, I should implement a "begin" in my class, and call it in setup?

I'll give that a go. Thanks!

yep. Just like Tone and Serial... look at those implementations.

Consider also passing pin numbers into the object via the constructor vs hard coding them...

BulldogLowell:
Consider also passing pin numbers into the object via the constructor vs hard coding them...

I did that origianally, but I stripped it back as much as possible to see what could be the error. What I posted was the bare-bones, all-it-needs-to-compile version.

I implemented a "begin," and now it works great!

I had no idea that manipulating hardware before setup() was an issue!

Thank you so much!

Fuzzyzilla:
I had no idea that manipulating hardware before setup() was an issue!

if you look at Arduino's main() function:

#include <Arduino.h>

int main(void)
{
    init();

#if defined(USBCON)
    USBDevice.attach();
#endif
    
    setup();
    
    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }
        
    return 0;
}

you will notice init(), which does, well, stuff.

your Global constructor is likely to be called before init(), leaving your hardware configuration in no-man's land.

If you look at the Tone.cpp file you will see that it only recognizes three processors:
AVR_ATmega8
AVR_ATmega1280
and everything else.

You should try changing "#if defined(AVR_ATmega1280)" to "#if defined(AVR_ATmega1280) || defined(AVR_ATmega2560)

Ok. What'll that do though? Allow me to use more of the HW timers?