Arduino Four-tone Multi-tones

I had reason recently to take a look at the ATmega32U4 microcontroller again.  This is used on the Arduino Leonardo and the Sparkfun Pro Micro and includes built-in USB functionality.  But it also has four hardware timers, so I thought I’d try to get them working with the three-timer Tone library.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

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 Leonardo or Pro Micro
  • 4x 8 ohm speakers or old headphone speakers
  • 4x 220Ω resistors
  • Breadboard and jumper wires

The Circuit

ArduinoFourToneMultiTone_bb

This is using a Sparkfun Pro Micro, which is a 5V/16MHz ATmega32U4. It has four speakers connected, each via a 220Ω (or higher) resistor, to one of the digital output pins.  In this, I’m using pins 2 to 5.

If you only had one speaker, you could possibly create a “passive mixer” circuit using four resistors to connect all four pins to the same speaker.

Note that I cheated a little here – I hooked the board into four of the speakers I was using for my tone fanfares!

IMG_5465

The Code

I wanted to use Brett Hagman’s Tone Library which will let you run a tone() using every timer available on your microcontroller.  So for an Arduino Uno, with an ATmega328 there are three timers, so you can play three tones.  Unfortunately the library doesn’t support the ATmega32U4 used on the Pro Micro or Arduino Leonardo, so I set out to see if I could add the support.

The short version, is I have.  I have a fork of the original library that now supports all four timers on an ATmega32U4 giving us four tones to play with.  You can download it here: https://github.com/diyelectromusic/Tone.

I’ve added a new example to give us four tones.  Find it here: https://github.com/diyelectromusic/Tone/tree/master/examples/ToneChords

Be warned though, as this is using all timers on the ATmega32U4 this includes Timer 0 which is used for the delay() function.  This is the last timer to be used so if you are only using between 1 and 3 tones then delay() will still work.  But if you enable that fourth tone then any call to delay() in your code will simply hang forever.

Advanced Discussion

The timers on the ATmega32U4 appeared initially to be similar to those on the ATmega328, so the first go at supporting it was simply to add some conditional compilation to the library to turn on or off the right timers when being built for the ATmega32U4, for example the following tells the library how many tones are supported and using which timers:

#if defined(__AVR_ATmega32U4__)
#define AVAILABLE_TONE_PINS 3
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 3, 1, 0};
#endif

The 32U4 has no timer 2, but timers 0, 1 and 3 are effectively the same as on the 328 so all that code can be re-used “as is”.

But timer 4 is quite different.  It is a 10-bit timer with many more options for some quite sophisticated modes and functionality.  But I wanted to replicate the comparatively simple functionality required by the Tone library.  It turns out it is quite possible – full details can be found here.

So with some additional conditional compilation I now have a version of Tone that also supports timer 4 on the 32U4.

Closing Thoughts

The ATmega32U4 gives a pretty good illusion of being a “standard Arduino with USB” but there are certainly nuances when you get down to the hardware level.

Working out how the additional timer worked was actually more involved than I anticipated.  At one point I was getting it all running at half-frequency for example, but that turned out to be solved by properly initialising all the control registers for the timer (there are five!).

Once TImer 4 was working, I had everything lock up everytime I tried to use Timer 0… that turned out to be a stray delay() call in my test code!  I was staring at it for hours without realising what it was and why it was a problem.  But I got there in the end.

The next step will be seeing if the original author would like me to push the changes up to the original library, but anyone who would like to play can find them in my own fork of the code.

Kevin

Leave a comment