Arduino R2R Digital Audio – Part 2

Building on the Arduino R2R Digital Audio project, this uses the Arduino Timer functions to run our code in a more accurate and useful way.  I’ve now also added some potentiometers to my shield and a jack output socket.

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

The Circuit

This is the same circuit as before, but I’ve added the following:

  • 10k potentiometer connected to 5V, GND and A0.
  • 10k potentiometer connected to 5V, GND and A1 (although this isn’t used yet).
  • Mono jack socket connected to the audio output and GND.

The Code

The problem with my previous code was that the audio output was very dependent on the speed that the Arduino’s loop() function would run.  This meant that even adding a single analogRead() call slowed it all down quite a lot.

The way to get around this on the Arduino is to use code that is triggered by one of the Arduino’s internal timers. These are special parts of the ATmega328 chip that automatically handle counting up or down to a set frequency and can do something in response to that counting. In our case we use the timer to run our code at a specific frequency.

Programming the timers “by hand” is very flexible but can be quite complicated.  But for what we need to do there is a library that gives us access to a timer and allows us to attach code to it.

The ATmega328, as used on an Arduino Uno, has three timers.  Timer 0 is used for built-in timing functions such as the delay() function we used last time.  Timer 2 is used by the tone() function, again that we’ve used already.  Timer 1 is available for use.  Different microcontrollers have different numbers of timers and they are used for different things, so this code will not work on the Wemos D1 Mini for example.

The way to think about this code now, is to imagine two computers running together.  After the setup() function has run, then you can think of the first computer as running the the loop() function repeatedly and the second computer running our ddsOutput() function every TICK microseconds.  It doesn’t matter how long the first computer takes to do something, the second one will always run on every TICK.  It isn’t really two computers of course, in this case our single processor will interrupt itself every TICK to run our ddsOutput() function, then go back to whatever it was doing before hand, in our case running the loop() function.  Interrupts are massively useful in this way, but you have to be careful of a few things, specifically:

  • Be wary of interactions between the two parts – the interrupt can come in and change something that loop() was depending on if you’re not careful.
  • You need to make sure that the interrupt code doesn’t take over – if it is running code that takes longer than the TICK, then the next TICK will have arrived before it finished the code from the first, and it will carry straight on again – the loop() won’t get a look in.
  • Be careful of taking over interrupts or timers that the Arduino environment thinks it “owns”. In this case that isn’t a problem as we know Timer1 is free. I seem to recall Timer1 is used by the Servo library though, so you can’t use this and Servos at the same time.

The split of functionality in this code is as follows:

  • Use the loop() to read the potentiometers.
  • Use the TICK ddsOutput() function to drive the direct digital synthesis.

Note that my board now has two potentiometers, but I’m only using one in this project.

Find it on GitHub here.

Closing Thoughts

Now that I can read potentiometers without affecting the performance of the synthesis, I plan to use that second one as a control knob.  I’d like to use it to choose a range of waveforms.

Also, if you try this project, you’ll notice that the frequency isn’t continuous – it steps as the value changes.  This is related to how the waveform is generated and put out, so it would be nice to do something fancier here.

Also, in music, to go up an octave you double the frequency, but right now the frequency is changing in a linear manner.  It would be good to be more natural with how the frequency is controlled too.

Kevin

Leave a comment