MCP4725 and Mozzi

I’m going to start this project with at disclaimer – don’t bother doing it this way!  It was really more of a learning exercise for me – the result of a bit of a “I’m going to push it about as far as I can” obsession this past weekend just to see how far it could go and to understand more about the I2C protocol itself.

There are examples in the Mozzi forums of using the Adafruit library and some non-blocking TWI code to drive the MCP4725.  With some simple experimenting, that does actually seem to work fine. My initial thinking that it would need a different track turned out to be wrong, but as you can see in the video below, some results are indeed possible.

I’ll write up the simple version shortly, but if you want to follow this dead end through, here are the details.

This builds on the following previous projects:

If you are new to Arduino, I’d strongly recommend looking at some of the alternative projects rather than this one, although if you wish to simply load up the code and run with it – go for it!

Parts list

  • Arduino Uno
  • 2x 10k potentiometers
  • MCP4725 breakout board (I’ve used the Sparkfun board as described in the previous tutorials)
  • Breadboard and jumper wires

The Circuit

My initial plan was to use the circuit from MCP4725 Digital to Analog Converter – Part 2 but in the end I went back to the just using the MCP4725 board on its own.

In order to adjust some of the parameters for the synthesis engine, two potentiometers were linked up to A0 and A1.

The Code

I wanted to update the Arduino Multi-pot Mozzi FM Synthesis to use the DAC, so that was my starting point.

I found out that Mozzi includes a non-blocking version of the I2C TwoWire library for use with sensors if you are prepared to put in a bit of effort.  There is an example with Mozzi showing how to read a three axis accelerometer.  However the example shows a CONTROL_RATE speed of updates for the sensor and I’m going to want to be sending samples over I2C at the AUDIO_RATE and I wasn’t sure it would be quick enough.  In the end it turns out that the interrupt handling isn’t the issue, it is the waiting around for the TWI hardware to respond, but that is another story…

So I stuck with what I thought was a simpler implementation of I2C master functionality used in MCP4725 Digital to Analog Converter – Part 3.

There are a number of key points of optimisation in order to attempt to keep the I2C bus running as quick as possible and minimise the overhead within the code writing out to I2C during the audio interrupt, which is running at 16kHz or thereabouts.

Before anything else can happen though the Mozzi library itself must be told to use a user-provided audioOutput function rather than the built-in function for PWM.  This involved changing the mozzi_config.h file provided as part of the Mozzi library.  Uncomment out the following line near the end of the file.

#define EXTERNAL_AUDIO_OUTPUT true

Unfortunately this changes it for every build of the library from this point on, so all the previous projects and most of the Mozzi examples will now fail to build.  But by changing this, we can now provide our own audioOutput function to write to the MCP4725.

You can scan over the code on GitHub but there are a few specific considerations to be aware of.

  1. As I’m using my Sparkfun MCP4725 board as per MCP4725 Digital to Analog Converter, the board is plugged into A2 to A5 meaning these are not available for controlling the synth, so those potentiometers were commented out in the code.
  2. The I2C bus must run in fast mode, so SCL_CLOCK in twimaster.c was updated.  Actually I pushed my luck and found that I could increase performance of the bus by setting it to 800000 which isn’t an official I2C bus speed – but my MCP4725 seemed to cope.
  3. I changed the key i2c functions that will be used all the time to “inline” functions, which means that each function will be expanded out as raw code rather than being called as a function, potentially saving the overheads of several function calls.  This applied to i2c_start, i2c_write and i2c_stop from twimaster.c.
  4. I also “spelt out” the ADDR<<1 + I2C_WRITE calculation and hardcoded the address as a pre-processed value, so my 0x60 address became 0xC0.  I suspect the compiler would have done this anyway as ADDR was constant, but anyhow.

I started using my output filter circuit but the output was tiny, so after some investigation it turns out that Mozzi is creating a sample value +/- 128, so first it needs a bias adding to keep it positive to send it to the DAC.  Also, being a 12-bit DAC it can accept a value between 0 and 4095 for the full 0 to 5V output range so only sending it 0 to 255 explains why the voltage is so small – especially after going through my potential divider circuit.  To solve this issue, I first tried adding a scaling factor to multiply the value before sending it to the DAC.  This worked, but was extra work for the microcontroller on every sample.

In the end I abandoned my output circuit which was designed to take a 0 to 5V signal and get it into an audio friendly +- 0.5V signal, and just went back to using the raw board.  Having a 0 to 255 signal sent to the DAC puts the output signal in the right kind of range already.

Audio quality wise, the sound isn’t great – there is some interference – but actually considering I wasn’t expecting it to be possible at all, I’m quite pleased with the results.  One of the real pleasing results was actually being able to see the synthesis being applied clearly to the waveforms on the oscilloscope.

Download the I2C library from here: http://www.peterfleury.epizy.com/avr-software.html then perform the updates as described in MCP4725 Digital to Analog Converter – Part 3 and above.

Then find my code on GitHub here.

Closing Thoughts

There are plenty of opportunities for improvements.  First off, the signal is 0 to 1V so could be AC coupled to make it +/- 0.5V.

Secondly, the board is using A2 and A3 for power and ground which is very wasteful so I might make a shield supporting one of the MCP4725 modules which allows for four potentiometers.

I might also try this now with the Arduino Mozzi String Synth too.  Although possibly not for a while, as I’m a bit “I2C’d out” right now.

As I mentioned at the start there was some discussion on the Mozzi forums about how to use the built-in non-blocking I2C code with the Adafruit library, and after some quick experimentation actually you don’t need the Adafruit library at all and the end result is actually a lot simpler!  I’ll write that up next.

Most discussions of using Mozzi with an external DAC suggest you’d get better performance either with SPI or a parallel DAC, but I wasn’t able to find any breakout boards so would be finding specific chips and wiring them up.  But to be honest at this point it is now probably easier to just get a microcontroller with a built-in DAC in the first place like the Teensy.

Kevin

Leave a comment