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:

2014-03-18-161639_1280x800_scrot

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.

Advertisements

Constructing a sampler, pt. 1

Starting way at the bottom, a digital sampler is basically a computer system with a few basic features:

  • Memory for storing sounds
  • An analogue-to-digital converter for getting sounds from the real world into memory
  • A digital-to-analogue converter for getting sounds from the memory into the real world
  • A method of playing back sound from the memory at will in a musically useful way (ie., triggers, MIDI notes)

(You can of course bypass the ADC and just pre-load the memory with sounds, in which case you get a sample-based synth or ROMpler)

This is very similar to a digital delay or looper such as Clueles, of course, which also stores sounds in memory and plays them back again, only such a system dispenses with the ‘at will’ part and just plays back everything in the order it came in.

If we modify the basic algorithm described in the Clueless document, instead of the simple three-step sequence, we need some state to know if we’re recording, playing back, or not doing anything. Ie.,

repeat: {
  if(RUNNING) {
    if(RECORDING) {
      write(buffer[addr++]);
    }
    else {
      read(buffer[addr++]);
    }
  }
}

This obviously doesn’t have a user interface to change the state so it’s not much fun if you run it on a PC, but if we make an implementation with digital logic, we can implement the state by reading from e.g. a pair of flip-flops controlled by simple buttons. By ANDing the sample clock with the flip-flop which represents the RUNNING variable in the code above, the counters are only incremented when the flip-flop is on, We probably only want to fill or play the buffer once on every trigger, so we connect the overflow from the address counters to the reset of the flipflops, meaning that when we reach the end of the buffer, playback or recording stops. In code, this would mean resetting addr to 0 once it reaches the end of the buffer.


repeat: {
  if(RUNNING) {
    if(RECORDING) {
      write(buffer[addr++]);
    }
    else {
      read(buffer[addr++]);
    }
    if (addr >= LENGTH) {
       addr = 0;
       RUNNING = false;
    }
  }
} 

This sampler has some serious limitations, of course: It can only store one sample and it can only play it back from the very beginning to the very end. A variable clock would allow for simple repitching, much like varying the playback speed on a delay or tape recorder.

The addition of a trigger circuit so you could enable recording but only start it after the input reaches a certain threshold would arguably make it useful for sampling percussive noises. A use case for this is making drums with monophonic synths without using a whole synth for a single drum sound; it’s also possible (and easy) to add battery-backup to SRAM, meaning that you could have non-volatile sample storage.

I’ve attached a schematic sketching the above design, minus this trigger circuit. Note that it is untested not all signals are properly connected and that buttons, clock, analogue circuitry, etc., are not shown. The Clueless schematic is more complete if you want to try to build this, but most things should be obvious.

I’ve used a 112 dual J/K flip-flop for the state, push buttons could obvious be attached more or less directly to this. The ‘112 has two outputs per flip-flop: one that’s high when the flip-flop is on, and one that’s high when it’s off. Since the DAC write strobe is derived from the high-when-on output, we can use the other output for the chip select of the DAC, and conversely for the ADC.

It seems that the sequencing parts of Clueless can be entirely foregone since the DAC and ADC aren’t sharing the memory bus, By NAND’ing the clock with the output from the flip-flops, we offset the sample counting pulse (triggered on a rising edge) and the R/W pulses (falling edge). Once again, this is untested, no fitness implied, etc., etc. Do let me know if you try it, though.

bang02

Clueless delay

Over the past few months, I’ve been improving my non-existent knowledge of electronics through a project I call Clueless. It’s basically a very simple digital delay.

DIY delays seem to fall into two groups: those based on analogue bucket brigade chips, and those based on the PT2399 IC, which was originally meant for karaoke applications but has also found use in a number of guitar pedals and eurorack modules. These both have limitations in terms of delay length and (for the PT2399) the sample rate.

The present design is based on a few chips of 74xx-series logic and an SRAM IC and can scale to ludicrous delay lengths, since simple cascadable counters are used for addressing. The quality is (probably) hampered by the fact that it uses 8-bit DACs and ADCs, but when run at high sample rates it doesn’t seem to exhibit much of the crunchiness normally associated with older designs.

The design is described in further detail at my hackerspace Labitat’s wiki, because that’s where I’ve been working on it. In fact, it’s fair to say that none of what I do with electronics would be possible without Labitat. It’s such a great environment for being creative with technology, and there are so many helpful people. I basically showed up a little over a year ago with the parts for a MIDIBox sequencer and someone there taught me to solder and helped me debug it. If you’re in the Copenhagen area, come down on a tuesday night. Similar places exist all over the world, check out hackerspaces.org to see if there’s one near you. Or support a movement and start one.

I’m already in the process of augmenting the design to make it a one-voice sampler. A digital delay is a sampler that continously records and plays back as it steps through a buffer. Or conversely, a sampler is a delay that steps through a buffer once while recording and then steps through it playing back at request.

The hard part of a sampler implemented in digital logic (well, one of them) is address generation. If you’ve only got one sample at a time and all you want to do is play it back from start to finish, this can be done with counters as in the delay. More features requires more logic, but fortunately there are a few examples in the service manuals of old samplers of how to do this — the earliest Emus, in particular, have very good, thorough service manuals with clear schematics, block diagrammes and sections describing the operation of this part of the circuit. I hope to do more in-depth posts on the SP12(00) and Emulator 1 and 2 in the near future. The ones after those, unfortunately, sealed all this off in custom ICs that also do DSP.

Modern µcs are fast enough to do address generation by bit-banging if they don’t have a proper memory interface. Unfortunately, the through-hole versions generally don’t have enough pins to actually output them in a timely fashion. The Where’s The Party At uses an AVR and some latches to do it. I’ve studied the design more closely, and one thing that struck me is that it runs at a fairly low sampling rate (a maximum of 20khz) and does no filtering of either inputs or outputs. This is why it’s sound is so rough, but is something that could be easily cleaned up. I don’t particularly like the thought of working with somebody else’s 8 bit assembly, but if one used parallel-input DACs and ADCs and never let the data touch the AVR, the latter could be reduced to a pure address generator, which might lead to significant reductions in code complexity (and also free up the data pins, meaning less latches). This deserves to be explored. The ARM-based LPC1114 is also interesting, although the through-hole variant has too few pins.

Various updates

So a few things have happened in the two months that have passed since my last blog post.

I built a MIDIBox Seq V4. It doesn’t have a proper front panel yet, is just Wilba’s PCB mounted on a piece of acrylic. It is, however, functional and great as it is, so for the moment I’m crossing sequencing off my list. I’ve also got the parts for a MIDIbox FM synth which is polyphonic, so maybe I’ll have my first piece of open hardware music soon.

The sampler is progressing in various ways. A friend of mine is designing a board based on the STM32F4 with a few megabytes of external SRAM. It will be ready, hopefully, within a few months. This should be a good platform for a sampler, and also for effects which need more working memory than microcontrollers have on-chip.

Another interesting development is the port of the MIDIbox software to the STM32F4 Discovery board. This is a cheap development board based on the same Cortex M4F from ST that my friend is using for his board. The chip on this is faster than the LPC1769 used in the regular MIDIbox core, and has a number of instruction set extensions as well as hardware floating point that make it much more suitable for DSP tasks than the LPC1769.

The Discovery board also has an onboard audio rate DAC that the MIDIbox software can address, so it’s already a good platform for making synthesizers if you can tolerate C. As mentioned previously, there’s already a SD card based sample player that could provide a starting point for a proper sampler. This is my summer project. It’s not the variable-clock, 12-bit S900 recreation I really want to see, but it would certainly be useful for certain things, like a PCM drum machine.

Speaking of drum machines, I was pleased to see the Sonic Potions LXR drum machine sell out the first 100 kits in hours. This community is doing great things, and I look forward to seeing the developments.

Sampler progress, again

So I’ve been braining and going over datasheets, service manuals and schematics in most of my free time recently. The E-Mu Emulator service manuals are a great, read, BTW.

I’ve come up with an architecture that’s quite close to the S900, only a lot less chips are needed since we now have faster microcontrollers, DACs with built-in buffers, etc.

In fact, I think most of the digital parts could be done with just three chips.

The first is the microcontroller. The STM32F4 has enough on-board timers that you could run one per voice in an 8 voice system and still have enough left over for house keeping, but you might be able to get by with one for all the voices. At any rate, the idea is that the timer generates an interrupt in a way that divides down a high frequency clock to the desired (variable) sample rate, per voice.

This interrupt moves a sample from memory to the DAC. Since I find the volatility of memory on old samplers one of their most frustrating aspects, non-volatile memory is a primary requirement for me. You can use Flash or battery-backed SRAM, but I’m very attract to MRAM for its space age characteristics. You basically use it like you would SRAM, no need for erasing before writing and such, but it doesn’t require power to keep it’s data. Meaning, no internal batteries are needed. At 30 euro for a 16 megabit chip, though, it’s twice as expensive as regular SRAM. At 50 KHz sample rates, 16 Mbit is 20 seconds of sample time, minus some overhead. 20s is OK for this project, 40s more than enough.

The above chip is paralel, which means it’s extremely easy to use: just connect the address pins to GPIO pins on the µc, and the output pins to the input pins of DAC. I could use one single channel DAC and some sample and hold trickery, or a DAC per voice. I’m going for the latter. Just a few pins for the selecting which DAC to write to, loading the outputs, etc.

The missing bits, of course, is a paralel ADC and some more DAC channels to generate CV for the analogue filters, but this is the jist of it.

Sampler progress

A very interesting thread cropped up on Gearslutz recently, describing the various ways digital synthesizers and samplers implement sample/wavetable playback.

The previous approach I had in mind for my sampler was to use a regular audio codec, some 24 bit, 44.1 KHz thing, attached to an STM32M4F microcontroller. This would work.

What would be more fun, though, is to try and emulate an older, 12-bit design. Going by the taxonomy in that thread, I quickly settled on a high frequency clock, divide-by-n. The basic approach is to use a constant clock well above the desired sample rate, and use a counter or divider to derive the desired rate from it. So if you have a constant 8 MHz clock, you output a sample from your buffer every 1000 clock ticks for an 8 kHz output rate — if the sound was sampled at 8 kHz, this means it’s played back as it was recorded, but if it was sampled at, say, 11 kHz, it will be played slightly slower, resulting in a lower pitch without interpolation.

Looking at the schematics in the Akai S900 service manual (available online — give it a google), it uses an external counter to divide the clock, and a couple of 4×4 bit register files to feed a parallel input 12 bit DAC.

This is all well and good, and could be redone as most of these chips are still available, but things have improved a little since 1986. The STM32F4 has a bunch of on-chip timers that can be used instead of external counters, and buss bandwidths are good enough that you can use SPI DACs for 8 voices and 24 CV outputs (analogue filter/VCA control). I’ve sampled a 12-bit four-way DAC from TI (DAC7615) that should be just about good enough for up to ~40 kHz output rates that would work for one voice + control of one of the simpler Shruti filter boards.

Sonic Potions Drum Machine

A fellow named Julian is making a virtual analogue/FM drum machine with built-in sequencer. The details are over at sonic-potions.de. It looks quite exciting, all open hardware and software. Kits will be available, although the mainboards containing the main microcontroller and audio codecs will come preassembled as they’re smallish SMD components.

Incidentally, it’s based on the same STM4F chip that I just got a development board for. Mine’s yet to make a sound, though, but I’m looking forward to seeing the code and designs for this. And probably snagging a kit!