Raspberry Pi multi channel DAC for CV output?

So I’m working on a prototype for my very first module design based on a Raspberry Pi Zero. It really only needs USB host to Linux (I’m leveraging some evdev goodness), then some CV outs.

The very first prototype is dirty but works well enough with RC-filtered PWM pins going to a TL072 dual opamp to bring the levels up to CV levels.
Obviously, there are some issues:

  • It’s dirty
  • It’s noisy
  • There are only two CV outs

Of course, I’m considering going forward to the next prototype, which I’d like to integrate a proper DAC with. Since I know fairly little about the matter, I went and dived a bit into the great resources that is @pichenettes’s eurorack repo.

Now, I think the module could handle 8 CV outputs, so the Stages DAC caught my eye . While I’m confident I could hook it up to the Pi and produce the correct levels, I worry that I could not handle timing correctly, so I’m not sure how I could generate proper LFOs.

I didn’t really understand how Stages’ processor handled the timing of that DAC, but I’m worried that since the Pi don’t have super precise control over their GPIOs, that I couldn’t get a stable clock.

Obviously, I could go with a standard I2S stereo DAC, and it would be less noisy, but there still would be only two channels.
Offloading the DAC control to a separate microcontroller seems like overkill, but maybe I’m wrong ?

Do you know of a better suited solution ? Maybe a different DAC and/or embedded Linux board ?


Not familiar with the raspberry pi, but in all my recent CV generating modules (marbles, tides, stages) I (ab)use the I2S peripheral to talk to vanilla SPI DACs.

Software: I configure the I2S peripheral for 32 bit audio, and stuff the channel selection bits in the 8 upper bits, then 16 bits of data, and the DAC ignores the rest. So I can continuously write 2 or 4 samples on all channels from a single DMA interrupt request. And perfect timing of course!

Hardware: Just wire the LR signal to SS, the data signal to MOSI, and a clock is a clock.


I’m afraid I can’t help, but I’ll be keeping an eye on this thread, just out of interest.

I’m surprised there haven’t been more (are there any) modules out there based on the Pi Zero or the Pi Compute device.

Glad someone is tackling that at last.

I’m interested to know what you’re planning for your module. Can I ask, is it a generic (ie user-programmable) module, or one with a specific task? Also, will it have a screen of some kind?

Hope you don’t mind me asking.

Very interesting idea. I’m too much of a noob right now to be confident that the next prototype can work with this trick, I’m somewhat reassured.

However, imagining I want to use a 16 bit, 8 channel DAC, how would I handle these eight channels in one cycle ? :thinking:
Would I simply setup the sampling frequency to be, say 192kHz, and use 48kHz in software ?
Also, what would wiring LR to Vss acheive ?

No, it’s a very specific module, and it has zero screen, I’m afraid.

Let’s say you want to refresh each channel at 48kHz. You configure the sample rate to 48*8 = 384kHz.

Then you configure your buffer size to a multiple of 8. For example 16 stereo frames. You’ll get an interrupt request 24k times per second, every time a buffer of 16 stereo samples has to be filled. You fill this buffer with two samples for each of the 8 DAC channels.

Frame 0, L channel: 8 bit channel select code for channel 0, then 16 bits of data for sample 0 of channel 0.
Frame 0, R channel: 0.
Frame 1, L channel: 8 bit channel select code for channel 1, then 16 bits of data for sample 0 of channel 1.
Frame 1, R channel: 0.

Frame 7, L channel: 8 bit channel select code for channel 7, then 16 bits of data for sample 0 of channel 7.
Frame 7, R channel: 0.
Frame 8, L channel: 8 bit channel select code for channel 0, then 16 bits of data for sample 1 of channel 0.
Frame 8, R channel: 0.

Frame 15, L channel: 8 bit channel select code for channel 7, then 16 bits of data for sample 1 of channel 7.
Frame 15, R channel: 0.

Why is the right channel data set to 0? Because when it is shifted out, the word select (WS, aka LR, aka LRCK) is high; and since this line is hooked to the SS line of the SPI DAC, the DAC will ignore the data anyway.


Oh my god, not to Vss, you’ll create a short!

To SS, “slave select”. You need to tell the DAC when it is ready to receive data shifted to it by lowering the SS line to low, and when it should actually update the outputs by rising its SS input to high. This can be done by “abusing” the WS/LR line from the I2S peripheral for this purpose.


Haha, I knew something was off. Very nifty trick you got there :smiley:

Thanks a lot for the clarification! I hope I can get close enough to the bare metal to do that with the Pi, without too much hassle. Best case scenario, I can get my hands dirty enough to turn this trick into a pseudo 7.1 ALSA driver, that I can easily use afterwards in software. Still sounds like a lot of work, though :confused:

At first I thought the OP was about producing precision pitch CVs (but that was just projection on my part). But it seems like the aim is audio-rate outputs. In which case, presumably you know about Terminal Tedium by Max Stadler, which does a lot of what you describe, albeit for only two output channels and using an audio codec. It can be made to work with a PiZero, although using a single core for such things is not ideal, and a quad core RPi 3 is much better, despite the current draw.

Bare metal on an RPi? There are better platforms for that, I suspect. It can be done, but why bother?

Then there’s Bela, which does even more of what you want to do. It would just need a carrier module to interface it to modular level signals, supply (copious) power from the eurorack power rails, and, I would argue, provide some control buttons or encoders and a display, just to select preprogrammed set-ups on it. Bela stuff is far from cheap, though.

1 Like

Precisely! I mean, I could settle for just LFO rates, but the timing precision is important. And it seems, as far as GPIO timing, RPi is bad, except for one exception: audio.

That’s the main issue I have. I could easily design a two-channel CV using existing audio codecs. But it would be interesting to have 6 or eight channels of CV.

As for the Bela, I know it would be interesting and much less hassle, as I’ve had the pleasure of seeing it first hand at a workshop. But yeah: the Rpi Zero is 5€. The Bela Mini Cape alone is 60€, so add to that the required PocketBeagle, and you’re already at 100€. So sure, it would be more practical. But a 1000% increase in price is hard to swallow.