Arduino R2R Digital Audio

This project shows an example of digital to analogue synthesis.  Technically this is what is called direct digital synthesis, but in its most basic form.

This uses a resistor ladder to perform the digital to analogue conversion, so that ultimately a waveform can be generated by getting the Arduino to put different numbers between 0 and 255 on 8 of its output pins, which the circuit then converts to a voltage level that can be used as a simple audio output.

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

  • Arduino Uno
  • 11x 20k resistors
  • 9x 10k resistors
  • 10k potentiometer (optional)
  • Stripboard or proto shield and jumper wires

The Circuit

Note that there is an error in the circuit from the “proto DAC shield” tutorial – the last resistor to GND should be a “2R” resistor, so in this case a 20k resistor.  This is also not really allowing for inaccuracies in resistors – the key concept is that the smaller resistor value is half the larger value, but with even 1% tolerance that will add up across the ladder.  But this is fine for a “bit of fun”.  You can find a full discussion of the R2R ladder and its use for audio in “Arduino for Musicians” and “Arduino Music and Audio Projects”.

The above shows an example design in stripboard (using the trick I mentioned before to make this an Arduino shield). In the end I put mine on a “proto shield” as shown below.

The easiest thing to have done in terms of both the circuit and programming it, would have been to use the Arduino D0 to D7 pins as the 8 parts of the digital representation of the value to be converted.

But I’ve always found that if it is possible to avoid using D0 and D1 (which are used for the serial connections) then it makes things generally simpler.  This means splitting the value between two Arduino ports (D and B), which means making two writes to the I/O ports rather than a single atomic write.  This means there is a slight delay between setting PORTD and PORTB which could make the signal a little noisy.  In practice, for this demonstration I was incrementing the value by 8 or 16 each time anyway, which means the bottom pins (including 8 and 9) are always zero anyway.

I used a set of male jumpers for the output, but it isn’t really loud enough to go straight into a loudspeaker.  This is one case where it is much better to use a jack socket and plug it into an amplifier or a set of active speakers (like the kind you get for a computer).

As an enhancement you can wire up a 10k potentiometer to the A0 pin for a pitch control.  The two “ends” of the potentiometer need to be wired to 5V and GND, and the “wiper” (middle pin) is wired to A0.

The Code

This is about as simple as it can get for direct digital synthesis – I just increment a counter (“wave”) within the Arduino’s loop by a specific step value (“wave_step”) and write that value out to the I/O port.  If I let it free run, then it would be way too fast to give a useful output, so I include a short delay using the Arduino’s delayMicroseconds() function.  This is typically good for delays of between 3 microseconds and tens of thousands (tens of milliseconds).  The comments explain how much delay is required for a specific frequency – the bigger the delay, the longer it takes to output the complete wave, so the lower the note.

As mentioned above, I’ve avoiding using D0 and D1, using D8 and D9 instead, so I need to do some bit manipulation to get the right parts of the value to the right bits of PORTB and PORTD.  This happens in the ddsOutput() function.  I’ve made sure the two PORT operations happen right next to each other with minimum delay between them.

If you’ve wired up a potentiometer to A0 then uncomment the USE_POT 1 line to allow the delay value to come from reading A0.  Note that this adds a fair bit of time to the loop, so the delays would need to be shorter to get the same frequency as when analogRead() isn’t called – or in my case I increased the wave_step value.

With a wave_step of 16, it is possible to get a frequency range of around 50Hz up to 550Hz using the potentiometer.

Find it on GitHub here.

Closing Thoughts

It is possible to create different waveform shapes using a different calculation.  One option is to step up, then back down to create a triangle wave. If it alternates between fixed high and low values, it can output a square wave (but that is basically what the tone() function does, which is much simpler to use!).

Sine waves are more complex to calculate, so typically it would be pre-calculated and stored in a table and read out of the table directly when needed, but that is starting to get into proper direct digital synthesis and there are much better ways to use an Arduino to do that rather than using a delay() in the main loop.

2020-06-20 12.41.19

Kevin

Leave a comment