Category Archives: software

STM32F4 DAC multiplexing with sample and hold

The STM32F4 ARM microcontroller (as used on the cheap-as-chips Discovery board from ST, who make the microcontroller) includes two 12-bit DAC. If this isn’t enough outputs, you can either pile on the external SPI DACs, or use simple sample-and-hold buffers for multiplexing. Basically, instead of outputting a single stream of values on each DAC, you send out the samples for each channel sequentially, so first a sample from channel 1, then a sample from channel 2, etc., etc. These are then sent through a 1-to-n-channel switch whose state is changed in time with the stream so that the samples from channel 1 go to the switch’s first output, channel 2 to the second output, etc. After each output, the demultiplexed voltage goes over the top of a capacitor with its other pin tied to ground through to a normal non-inverting opamp buffer:


I’ve used smallish ceramic disc capacitors of in the nanofarad range for this. If you know about such things you can probably calculate an optimal value for the capacitor; the crucial thing is that capacitors take some time to charge and lose some charge over time, so for the output voltage to remain stable, you have to pick the right value.

The number of channels you can get out of a single DAC in this manner depends on a number of factors: primarily, the desired sampling rate and how long it takes for your DAC to settle. Say you want a 50khz signal, for one channel that’s a sample every 20 microseconds. If you want four channels, your system needs to be able to output four samples in that timeframe. Note that the voltage needs to be steady for some time (depending, no doubt, on the capacitor) for the capacitor to charge.

The 4051, ’52, and ’53 CMOS switch ICs have a number of inputs and ouputs, as well as up to three address pins for selecting which output is connected to the input, and an enable pin to select whether the switch is open or closed. The crucial element, then, is outputting a set of control signals in time with the DAC output.

DACs don’t output a voltage instantaneously (stupid physics making everything difficult as usual), so between two consecutive samples there’s a phase where the output voltage is in some indeterminate, intermediate state. You use the enable pin on the switch to make sure its closed while the DAC settles to avoid glitches on your final output.

This basic technique works for any system (whether you have a microcontroller or not). You can obviously drive the DAC and switch with logic ICs if that’s what makes sense in your context. The only important thing is that you’re able to maintain the correct timing relations between the DAC output and the control signals sent to the switch; note that this might make working with serial input DACs difficult. I doubt this technique works at all with I2S (audio) DACs or codecs that expect a steady stereo stream at a fixed rate and have integrated digital filtering and such.

This technique was often used for both control and audio signals in early synthesizers and samplers, because DACs were so expensive — the Emu SP12, for instance, has only two DACs in the entire system, one for converting the audio signal itself and the other used as a digital volume control, and the eight audio channels were derived by a scheme like this one. The D50 uses a similar technique.

Here I’ll describe how I’ve done this with the STM32F4 microcontroller in particular.   This references code that’ll be on Github shortly. I started out wanting to port the Sonic Potions LXR code to a board with six outputs, but when I got the output code working I decided to try getting some synth code of my own design I wrote last summer to run on the microcontroller. It’s basically a simple box of 4-op FM percussion sounds, and I’ve only looked at the output on a scope, not listened to it. But the principle stands, I can get six outputs out of two DACs.

The built-in DACs of the STM32F4 have a settling time of 3µs. I’ve tested with three output channels per DAC channel at approximately 50khz, and this wasn’t a problem, since this gives several microseconds for the capacitor to charge.

For my application, I wanted a steadily changing voltage (an audio stream to be output at 12 bits), so I set up a timer for running at the desired sample rate multiplied by the number of channels. I’m using TIM4, since this is able to trigger the DACs (not all timers are; consult the godawful documentation, namely the STM32F4 reference manual page 314). That is, whenever TIM4 is set to some value that divides the main clock down to the desired sampling rate. When it overflows and triggers the Update event, the DACs output a value. Also, in the interrupt service routine for this event (TIM4_IRQHandler), I set the address pins on the chip by writing the value of a simple software counter to a GPIO block (this is basically some stored number of ticks modulo the number of outputs for each DAC). I’m using DMA streams to fill the DAC, but I could easily pick the value out of a buffer and trigger the DACs from software in the interrupt service routine.

A second timer, TIM5, is also triggered by the main timer. This runs in one-shot mode, outputting a single, slightly delayed, pulse that on the pin hooked to the enable-pin on the switch IC. It is triggered by TIM4, the same that triggers the DAC.

To recap, every time TIM4 triggers an update event, a few things happen:

  • The DACs start converting the value held in their output register
  • The software counter that controls the pin adressing is updated and its value put to the adress pins
  • TIM5 is started, first holding its output pin high, disabling the switch IC while the DAC settles, then pulling it low to enabling the switch to let the sample and hold capacitor charge.

Only the setting of adress pins needs to be explicitly handled in the ISR, the other two things happen simply by setting the correct triggering for the given peripherals.

I’ll add another post describing the DMA code that fills the buffers.

Sampler issues: Code and hardware

So while I’ve been waiting for the components for the sequencers (which are here today) I’ve been thinking about all the lovely code I’ll get to write for the sampler.

And I’m not really loving it: Embedded programming means C or C++, and to me that’s like having the choice between the plague and cholera. Most of my previous programming endeavours have been in either Scheme or Lua, in a predominantly functional style. I know my way around both C and C++ well enough to probably do it, but it won’t be very enjoyable for me. I’ve recently read K&R and gone through some of the exercises to get back up to speed on some of the things that were a little hazy, and it’s just reaffirmed my impression: imperative programming is like talking to a particularly retarded child. On top of this, C has some warts that don’t sit very well with me: unbounded arrays, anaemic enums, and the blurry mess that is the pointer/array ‘distinction’ (or lack thereof), in addition to the lack of support for anonymous functions, closures, and tail-call optimization that I’m used to. The explicit memory management is probably more suited to resource restricted applications with rather strict timing demands, such as embedded systems which generate sound, than a garbage collected language, but at least in Scheme there should be ways around this.

There are two projects that I’ll be at least evaluating before writing anything in C: ELua and Armpit Scheme. They’re basically embedded versions of my two favourite untyped languages. Both support the STM32F4 Discovery board, which is based around an ARM M4F microcontroller from ST Microelectronics..

The reason I’m planning on using this microcontroller is that iẗ́’s available in a package with 2MB of Flash. This is attractive, because it should provide me with enough sample memory for what I’m tagetting without adding off-chip memory. The MIDIBox sample player project gets 4-8 sample voices streaming from an SD-card, and apparently the limiting factor is the speed of the card. The on-chip Flash should be significantly faster, allowing a guaranteed 8 voices or more.

Like said, this first project (there might be a second, more fully-featured sampler) is mostly aimed at playback of short, percussive samples, so more a sampling drum machine than a real sampler. It will have 8 outputs, and probably operate at 16 bits or less internally. D/A conversion will most likely be 24 bit, though, as audio-rate DACs are basically only available in that range now. TI make some cheapish eight-channel DACs that should be easy enough to interface with. I should have the STM discovery board in a week or two and be able to start coding, first using its internal audio and later with an external DAC. Other planned features are analogue filters for each output, a resampling circuit with distortion and a bucket brigade delay, and a built-in coffee maker. We’ll see how it goes.