5
\$\begingroup\$

I need to be able to play two tones at once and i have the arduino uni with a small 8ohm piezo speaker. Is it possible to do so?

Sample Code

#define  c     3830    // 261 Hz 

int tone1 = 10; // digital pin10
int tone2 = 9 // digital pin9
void setup() {
    pinMode(tone1, OUTPUT);    // pin as output
    pinMode(tone2, OUTPUT);    // pin as output
} 

void loop() {
   playTone(1000,500,700)
} 

void playTone(long duration, int freq, int freq2) {
    duration *= 1000;
    int period = (1.0 / freq) * 1000000;
    long elapsed_time = 0;
    while (elapsed_time < duration) {
        digitalWrite(tone1,HIGH);
        digitalWrite(tone2,HIGH);
        delayMicroseconds(period / 2);
        digitalWrite(tone1,LOW);
        digitalWrite(tone2,LOW);
        delayMicroseconds(period / 2);
        elapsed_time += (period);
    }
}
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Should be doable. Have a sine lookup table. Calculate the phase difference between successive samples for both signals at the used sample frequency. Upon a timer tick (at your sample frequency) add those phases to two phase accumulators, look up their values in the sine table, add them and set a PWM output to that value.
Use a low-pass filter to filter an analog voltage from the PWM.

For example, suppose you want a 2kHz signal with a 3kHz signal, and your sample frequency is 10kHz. Your sine table has a value for every degree. Your 2kHz signal will step 72° per sample, your 3kHz signal 108°. So for the first signal you read 72 values further in the sine table, for the other you go 108 values further at each sample.

This also works for mixing more than two signals.

The main differences with Telaclavo's solution are that his signals are square waves, whereas mine are sines, whichever you prefer. My solution is more CPU-intensive, while his uses more I/O and external components (voltage adder).

\$\endgroup\$
4
  • \$\begingroup\$ Do you know any links or anywhere i can learn how to write the code for this? Its very confusing for me. \$\endgroup\$
    – h00j
    Commented May 6, 2012 at 14:23
  • \$\begingroup\$ @Henry - Sorry if it's confusing, I try to be clear. Your code doesn't work because both your signals do exactly the same: between two writes they change 180° in phase. What my solution does is change this 180° to a smaller value, depending on the ratio between your sound signal's frequency and the sample frequency. If the sample frequency is ten times your signal's, then there are 10 samples for every period. So on each sample you move 36° (360°/10) on your sine wave. \$\endgroup\$
    – stevenvh
    Commented May 6, 2012 at 14:49
  • \$\begingroup\$ Is there any chance you can show a sample for any two frequencies? :) \$\endgroup\$
    – h00j
    Commented May 6, 2012 at 15:12
  • \$\begingroup\$ It's worth noting that many piezo transducers sound really horrible when driven at anything near full volume with more than one tone simultaneously. Even severe distortion is often not objectionable when all frequencies are harmonics of a single tone, because all sum and difference frequencies will be harmonics of that tone as well. If, however, one tries to generate has e.g. 440Hz and 554Hz simultaneously, distortion may produce all sorts of other weird frequencies like 114Hz, 326Hz, etc. \$\endgroup\$
    – supercat
    Commented Sep 17, 2013 at 0:30
3
\$\begingroup\$

If the maximum number of tones that you need to play simultaneously is only two (or even three or four), I would use two output pins, configured to deliver PWM, and add their contributions externally, either with two resistors (if you don't need much volume), or with an opamp as an adder.

If you use a single PWM signal to recreate the sum of the two tones, you will need CPU attention for each sample of your total signal. The ATmega328 does not have DMA.

With the two pins, you don't hog the CPU. In fact, during the time intervals when none of the two frequencies change, the CPU will not need to do anything.

Another advantage is that you can change any of the frequencies, in real time. You don't need a lookup table for each combination of possible frequencies.

\$\endgroup\$
2
  • \$\begingroup\$ Would it be something like the code i put? (Except the frequency for each would be different) \$\endgroup\$
    – h00j
    Commented May 6, 2012 at 14:23
  • 1
    \$\begingroup\$ Could you give a pseudo code example and a pseudo schematic to demonstrate that? I am really interested. \$\endgroup\$ Commented May 6, 2012 at 17:42

Not the answer you're looking for? Browse other questions tagged or ask your own question.