Tone Generator

 

In this section we are going to build a tone generator using the Arduino Due, most of this comes directly from the Arduino site. We are also going to cover some basic circuit theory, and how to build a volume control circuit.


We will hook up a speaker to the Arduino Due, and make some noise using one of the DAC ports. The sketch we are using here can be found on the official Arduino site. It this just a way of making a simple function generator using the Arduino Due, you can choose between three wave types, a sine wave, a square wave or a triangle wave. I have not modified the sketch for this example, but we do need to modify the circuit. The DAC ports on the Arduino Due are not capable of driving a speaker, in fact you will most likely destroy the DAC if you try it. I recommend reading this forum post about damaging the Due. If you have an oscilloscope, you could test it with that, but I’m assuming you don’t.

We will need to hook up an amplifier circuit to drive the speaker. Later we can use this amplifier to build the theremin. This is also the best place to cover volume control. Volume control is a key part of a theremin, and its not as simple as it first sounds. Lets build the circuit and play with it a little, then we can discuss how it all works. First hook up a potentiometer ( something like 10k is good), hook up one leg to 3.3V, the other leg to GND, and the wiper to Analog Input 0. Then add a push-button as we did before when adding hardware interrupts in the LCD tutorial. Connect the button to port 4.

Here is the circuit diagram for the amplifier section, it is exactly the same as the Arduino page. I’ll explain a little more about the amplifier when we get to the section on volume control.

Next, hook up the 2nd potentiometer. Here I have connected PWR (pin 6) and GND (pin 4) for the LM386. Also I added the second potentiometer (10k), one leg is connected to GND, and the wiper is connected to the amplifier input (pin 3).

Next I added the 3 remaining capacitors. The 10 microfarad between pins 1 and 8 is what sets the gain to 200. 2 capacitors connect to the amplifier output (pin 5), one is a .047 microfarad ( I did not have a .05), this connects to ground through a 10 ohm resistor. The other capacitor is a 250 microfarad which will connect to the speaker.

Finally we need to connect DAC1 to the remaining leg of the potentiometer, and connect the speaker to the negative side of the 250 microfarad capacitor.


Now that the circuit is built, we need to upload the sketch, however we will need to create a library in order to use the “Waveforms.h” file. Actually you don’t need to do this, you can just copy the contents of “Waveforms.h” and paste them at the beginning of the sketch file if you want to. I have not yet covered how to make a library though and it is super easy. All you need to do is copy the contents of the “Waveforms.h” file and paste them into a simple text file (e.g. .txt), you don’t want to use the Arduino IDE for this, but rather something like Notepad or Wordpad ( if you are using windows). Then place your “Waveforms.h” file into a new folder named “Waveforms”, and place this folder in the libraries directory that is in your Arduino install. Mine is “C:\Program Files (x86)\Arduino\libraries”. Now paste the “Simple Waveform Generator” sketch into the Arduino IDE, then click on

Sketch->Import Library->Add Library

then navigate to the Waveforms folder, and click Open. Then click on

Sketch->Import Library->Waveforms.

You will see it add an include statement at the top of your file. ( you might have two includes for “Waveforms.h”, you can delete one of them). Here is the “Simple Waveform Generator” sketch

/*
  Simple Waveform generator with Arduino Due

  * connect two push buttons to the digital pins 2 and 3
    with a 10 kilohm pulldown resistor to choose the waveform
    to send to the DAC0 and DAC1 channels
  * connect a 10 kilohm potentiometer to A0 to control the
    signal frequency

 */

#include "Waveforms.h"

#define oneHzSample 1000000/maxSamplesNum  // sample for the 1Hz signal expressed in microseconds

const int button0 = 2, button1 = 3;
volatile int wave0 = 0, wave1 = 0;

int i = 0;
int sample;

void setup() {
  analogWriteResolution(12);  // set the analog output resolution to 12 bit (4096 levels)
  analogReadResolution(12);   // set the analog input resolution to 12 bit

  attachInterrupt(button0, wave0Select, RISING);  // Interrupt attached to the button connected to pin 2
  attachInterrupt(button1, wave1Select, RISING);  // Interrupt attached to the button connected to pin 3
}

void loop() {
  // Read the the potentiometer and map the value  between the maximum and the minimum sample available
  // 1 Hz is the minimum freq for the complete wave
  // 170 Hz is the maximum freq for the complete wave. Measured considering the loop and the analogRead() time
  sample = map(analogRead(A0), 0, 4095, 0, oneHzSample);
  sample = constrain(sample, 0, oneHzSample);

  analogWrite(DAC0, waveformsTable[wave0][i]);  // write the selected waveform on DAC0
  analogWrite(DAC1, waveformsTable[wave1][i]);  // write the selected waveform on DAC1

  i++;
  if(i == maxSamplesNum)  // Reset the counter to repeat the wave
    i = 0;

  delayMicroseconds(sample);  // Hold the sample value for the sample time
}

// function hooked to the interrupt on digital pin 2
void wave0Select() {
  wave0++;
  if(wave0 == 4)
    wave0 = 0;
}

// function hooked to the interrupt on digital pin 3
void wave1Select() {
  wave1++;
  if(wave1 == 4)
    wave1 = 0;
}

Here is the header file “Waveforms.h”

#ifndef _Waveforms_h_
#define _Waveforms_h_

#define maxWaveform 4
#define maxSamplesNum 120

static int waveformsTable[maxWaveform][maxSamplesNum] = {
  // Sin wave
  {
    0x7ff, 0x86a, 0x8d5, 0x93f, 0x9a9, 0xa11, 0xa78, 0xadd, 0xb40, 0xba1,
    0xbff, 0xc5a, 0xcb2, 0xd08, 0xd59, 0xda7, 0xdf1, 0xe36, 0xe77, 0xeb4,
    0xeec, 0xf1f, 0xf4d, 0xf77, 0xf9a, 0xfb9, 0xfd2, 0xfe5, 0xff3, 0xffc,
    0xfff, 0xffc, 0xff3, 0xfe5, 0xfd2, 0xfb9, 0xf9a, 0xf77, 0xf4d, 0xf1f,
    0xeec, 0xeb4, 0xe77, 0xe36, 0xdf1, 0xda7, 0xd59, 0xd08, 0xcb2, 0xc5a,
    0xbff, 0xba1, 0xb40, 0xadd, 0xa78, 0xa11, 0x9a9, 0x93f, 0x8d5, 0x86a,
    0x7ff, 0x794, 0x729, 0x6bf, 0x655, 0x5ed, 0x586, 0x521, 0x4be, 0x45d,
    0x3ff, 0x3a4, 0x34c, 0x2f6, 0x2a5, 0x257, 0x20d, 0x1c8, 0x187, 0x14a,
    0x112, 0xdf, 0xb1, 0x87, 0x64, 0x45, 0x2c, 0x19, 0xb, 0x2,
    0x0, 0x2, 0xb, 0x19, 0x2c, 0x45, 0x64, 0x87, 0xb1, 0xdf,
    0x112, 0x14a, 0x187, 0x1c8, 0x20d, 0x257, 0x2a5, 0x2f6, 0x34c, 0x3a4,
    0x3ff, 0x45d, 0x4be, 0x521, 0x586, 0x5ed, 0x655, 0x6bf, 0x729, 0x794
  }
  ,

  // Triangular wave
  {
    0x44, 0x88, 0xcc, 0x110, 0x154, 0x198, 0x1dc, 0x220, 0x264, 0x2a8,
    0x2ec, 0x330, 0x374, 0x3b8, 0x3fc, 0x440, 0x484, 0x4c8, 0x50c, 0x550,
    0x594, 0x5d8, 0x61c, 0x660, 0x6a4, 0x6e8, 0x72c, 0x770, 0x7b4, 0x7f8,
    0x83c, 0x880, 0x8c4, 0x908, 0x94c, 0x990, 0x9d4, 0xa18, 0xa5c, 0xaa0,
    0xae4, 0xb28, 0xb6c, 0xbb0, 0xbf4, 0xc38, 0xc7c, 0xcc0, 0xd04, 0xd48,
    0xd8c, 0xdd0, 0xe14, 0xe58, 0xe9c, 0xee0, 0xf24, 0xf68, 0xfac, 0xff0,
    0xfac, 0xf68, 0xf24, 0xee0, 0xe9c, 0xe58, 0xe14, 0xdd0, 0xd8c, 0xd48,
    0xd04, 0xcc0, 0xc7c, 0xc38, 0xbf4, 0xbb0, 0xb6c, 0xb28, 0xae4, 0xaa0,
    0xa5c, 0xa18, 0x9d4, 0x990, 0x94c, 0x908, 0x8c4, 0x880, 0x83c, 0x7f8,
    0x7b4, 0x770, 0x72c, 0x6e8, 0x6a4, 0x660, 0x61c, 0x5d8, 0x594, 0x550,
    0x50c, 0x4c8, 0x484, 0x440, 0x3fc, 0x3b8, 0x374, 0x330, 0x2ec, 0x2a8,
    0x264, 0x220, 0x1dc, 0x198, 0x154, 0x110, 0xcc, 0x88, 0x44, 0x0
  }
  ,

  // Sawtooth wave
  {
    0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0x110, 0x132, 0x154,
    0x176, 0x198, 0x1ba, 0x1dc, 0x1fe, 0x220, 0x242, 0x264, 0x286, 0x2a8,
    0x2ca, 0x2ec, 0x30e, 0x330, 0x352, 0x374, 0x396, 0x3b8, 0x3da, 0x3fc,
    0x41e, 0x440, 0x462, 0x484, 0x4a6, 0x4c8, 0x4ea, 0x50c, 0x52e, 0x550,
    0x572, 0x594, 0x5b6, 0x5d8, 0x5fa, 0x61c, 0x63e, 0x660, 0x682, 0x6a4,
    0x6c6, 0x6e8, 0x70a, 0x72c, 0x74e, 0x770, 0x792, 0x7b4, 0x7d6, 0x7f8,
    0x81a, 0x83c, 0x85e, 0x880, 0x8a2, 0x8c4, 0x8e6, 0x908, 0x92a, 0x94c,
    0x96e, 0x990, 0x9b2, 0x9d4, 0x9f6, 0xa18, 0xa3a, 0xa5c, 0xa7e, 0xaa0,
    0xac2, 0xae4, 0xb06, 0xb28, 0xb4a, 0xb6c, 0xb8e, 0xbb0, 0xbd2, 0xbf4,
    0xc16, 0xc38, 0xc5a, 0xc7c, 0xc9e, 0xcc0, 0xce2, 0xd04, 0xd26, 0xd48,
    0xd6a, 0xd8c, 0xdae, 0xdd0, 0xdf2, 0xe14, 0xe36, 0xe58, 0xe7a, 0xe9c,
    0xebe, 0xee0, 0xf02, 0xf24, 0xf46, 0xf68, 0xf8a, 0xfac, 0xfce, 0xff0
  }
  ,

  // Square wave
  {
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
  }

};

#endif

The main difference between this sketch and using an Arduino UNO or similar device to play a tone is that we are not using one of the digital outputs, but rather one of the analog outputs (DAC1, DAC2), which currently only the Arduino Due has. The way this example works is by using a hard-coded table to represent your waveform ( sine, square..). The tone is then played by running a loop which steps through the table, writing the value to the analog output, then pausing briefly.

analogWrite(DAC0, waveformsTable[wave0][i]); // write the selected waveform on DAC0
analogWrite(DAC1, waveformsTable[wave1][i]); // write the selected waveform on DAC1

When we get to building the theremin, we will also use a table of values to represent our waveform, but we will use a more sophisticated technique called DDS ( direct digital synthesis). I only used one of the outputs, this sketch can play two waves at once, and also allows you to change between four waveforms.

The other half of this circuit is the amplifier, we will use this same circuit for building the theremin. You can find this circuit several places on the web ( e.g. here). It uses the LM386 analog amplifier, which is a very inexpensive low power audio amplifier. The LM386 is ideally suited for small audio applications. The second potentiometer in this circuit controls the volume.

This volume control works by using the potentiometer as a voltage divider on the amplifier input. You hook one leg of the potentiometer up to the Arduino DAC output, and the other leg to GND, then hook the middle pin (a.k.a the wiper) to the amplifier input. Its exactly the same as when you hook up a potentiometer to one of the analog inputs to control something. This is easiest to explain with a small circuit that is equivalent to the potentiometer. As you turn the potentiometer. you are in effect changing the relative sizes of R1 and R2.

This is the perfect place to discuss how will we implement volume control for actually building the theremin, this is not as easy as it sounds, but first I want to discuss 3 general ways to control the volume with an amplifier circuit.

The first is the one we just did, place a voltage divider at the input of the amplifier, this allows you to control the amplitude of the amplifier input. A second way is to place a voltage divider at the amplifier output, so we can directly control the amplitude of the signal sent to the speaker. The third way is vary the gain of the amplifier circuit, the datasheet for the LM386 explains that you can vary the gain of the amplifier from 20 to 200 by placing a capacitor and resistor between pins 1 and 8. Which do we want? I decided not to use the third approach because the minimum gain is 20, so we won’t be able to reach zero volume just by use of the potentiometer. The first and second approach sound very similar, would there even be a difference? It turns out there is a pretty big difference, but its tricky to explain. I think its a great example though, since the concepts we need to understand are fundamental and you will use them over and over when tinkering with electronics.

First I need to mention a few disclaimers, we are working with a time varying audio signal, but all these calculations are for a DC signal, so technically these calculations are not correct, but I’m trying to convey some concepts, not exact numbers. Also when calculating current or power for a circuit, its good if your calculations are on the conservative side (i.e. overestimate the power you are actually supplying, and then building a circuit that can handle it). Having the DAC output a DC signal would be more power than you would ever get with an audio signal of the same amplitude.

First a little overview of current calculations. If you have two resistances in parallel like this

what is the equivalent resistance? A handy shortcut is “product over sum” so the equivalent resistance is

(10*15)/(10+15) = 150/25 = 8

in general

What if you want to know the current through the two resistors in parallel? If you know the voltage, then its just ohms law I = V/R. What if you only know the total current? Then its we need the following formula, this is commonly referred to a “current divider”.

Ok, now that we have a few basic tools lets take a closer look at manipulating the amplifier input vs manipulating the amplifier output. Imagine that you are really small and you are standing on top of the wiper of the potentiometer on the input of the amplifier.

pretend you can see the current flowing by. Say you look at R1 and see something like 4K, then you look at R2 see something like 6K, what kind of resistance do you see if you look towards the amplifier? It depends on the type of amplifier, and it can vary quite a bit from chip to chip, but it will be quite large. We say that the amplifier has a very large input impedance, this is why the rather feeble DAC output is able to drive the amp (i.e. a device with a large input impedance is easy to “drive”). The output of the DAC can not get higher than about 2.25 volts, and the datasheet says a typical input resistance for the LM386 will be 50k, so if we have the wiper all the way to one side ( assume a 10k potentiometer) so that R1 has zero volts and R2 has all 2.25 volts,then we have 50k in parallel with 10k, which represents an equivalent resistance of

(50k*10k) / (50k+10k) = 8.33 k

with 2.25 volts output, using ohms law we’ll have a total current of

2.25 / 8333 = .000270 A = 270 micro amps (uA)

now using the current divider equation, the amplifier will get

270 uA * ( 10k / 10k + 50k) = 45 uA

What if we turn the potentiometer so that R1 = 5k and R2 = 5k?

Now we have 5k in parallel with 50k which gives an equivalent of

(50 * 5)/(50 + 5) = 4.5k

This 4.5k (R2 in parallel with Ramp ) is in series with the other 5k (R1), so we get a total resistance of 9.5k.

Thus the total current is

2.25 / 9500 = 230 uA

and using the current divider equation, the current to the amplifier input is

230 * 5 / (50 + 5) = 20.9 uA

I hope that explanation was helpful, all I want to convince you of is that the current that is drawn by the amplifier is very small. Lets now consider the same scenario, but on the output side of the amp.

We have R1 and R2 ( assume a 10k potentiometer as before), but what do we see ( this is the key difference) when we look at the speaker? Most likely it will be 8 ohms, it could be 4 or maybe something else, but its going to be tiny compared to the amplifier’s input impedance. We do have a higher voltage since we have stepped up to a 5V supply (the amplifier is powered at 5V). We won’t actually get 5V, but like I said, its good to overestimate a little. What does our current divider look like now? Lets say the potentiometer is turned all the way to one side so that R1=0, R2=5k, and the speaker is 8 ohms. we can use the product over sum calculation from above and get the equivalent resistance.

(10000*8) / 10008 = 7.993 ohms

The voltage across the speaker will be the full 5V, and the total current is

5/7.993 = .625 A or 625 mA.

How much of this current goes to the speaker?

I = (.625) * 10000/10008 ~= .624 A, or more that 99% of the current.

That sounds ok as the Arduino Due can supply 800 mA through the 5V port. ( remember this is a DC calculation, the actual current will be much less).

Now lets turn the potentiometer a little more so R1 = 1K, R2 = 9K.

Using the product over sum formula, the equivalent resistance is 1k plus the parallel combination of 9k and 8 ohms.

72000 / 9008 = 1008 ohms + 1000 = 2008 ohms,

again the max voltage across the speaker will be on the 5V level ( its being amplified), and we have the following so the total current is

5/2008 = .0024 A or 2.4 mA.

We turned the potentiometer less than 1/10 of the way from full volume and the current has dropped by a factor of 260 . This is because our potentiometer is 10k, and the speaker is only 8 ohms.

One way around this is to use a much smaller potentiometer, I looked briefly and the smallest I could find was a 5 ohm potentiometer and it was over 10 times the cost of the small 10k ones that I already own. Even at 5 ohms this approach is not as good, at half volume, we’ll be dumping 40% of the amplifier’s output power into the potentiometer. There are some specialized applications where you would put the potentiometer on the output, ( e.g. very lower power device like some headphones) but it is far less common.

I said that the volume control for the theremin was not easy, but everything so far just amounts to putting a small potentiometer somewhere in the circuit, sounds easy! Its not so simple because we need to be able to run the instrument in “Training Mode”, and that means being able to control the volume with the Arduino, in other words make the Arduino turn the potentiometer.

Remember how we hacked the Servo so that it acts like a potentiometer, all we should have to do is take the wire we soldered to the servo’s potentiometer, and connect it to our amplifier circuit, then we can use the Arduino to write to the servo which will in effect move the potentiometer, and control the volume. In this approach the Arduino does not directly control the volume, but rather sets a servo position that is part of an external volume control circuit.

In theory this works just fine, but in reality we are using a hacked potentiometer, and don’t get full control of it, it only goes from .29V to 2.06V. So again we may not be able to reach zero volume with the potentiometer, and calibrating it might be difficult.

I know from experience that its hard to calculate exactly what the volume will be for a device that is cobbled together from various parts, it could end up being very faint or uncomfortably loud. I ended up taking a two part approach, the first part is to place a potentiometer in the input stage of the amplifier. The Arduino is completely unaware of this potentiometer, think of it as a “master volume” control. The second part is to use software to control the volume by varying the amplitude of the DAC output, this turned out to be very doable as the DAC has 12 bits of resolution or 4096 values, if we allow for 10 volume levels, that gives us 409.6 levels for the waveform. I’m getting ahead of myself though, we need to cover how DDS works before we go much further.

Intro->SOS->LCD Tutorial->Servo Tutorial->Timer Interrupts->Hacking Servos->Tone generator->DDS Tone Generator->Theremin 1->Theremin 2->RTTL Songs

 Posted by at 3:54 pm

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)


Time limit is exhausted. Please reload CAPTCHA.