#rp2040: Using DMA to set SPI DAC (MCP4921)

Good news: I managed to get DMA working on an MCP4921 DAC, which uses SPI. It’s my first time trying to get DMA working on the Raspberry Pi Pico, so I was a little bit daunted.

Here the code which generates a 500Hz sawtooth wave:

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include <math.h>

#include "pi.h"


using u8 = uint8_t;
using u16 = uint16_t;


#define LED 25


double framerate = 44100, saw_freq = 500;
uint64_t period = 1'000'000/framerate;


void mcp4921_init(void)
{
#define	PIN_SCK		2
#define	PIN_MOSI	3
#define PIN_MISO 	4
#define	PIN_CS 		5
	int spi_speed = 18'000'000;
	spi_init(spi0, spi_speed);
	spi_set_format(spi0, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
	gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
	gpio_set_function(PIN_CS,   GPIO_FUNC_SPI);
	gpio_set_function(PIN_SCK,  GPIO_FUNC_SPI);
	gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
}

void mcp4921_put(uint16_t vol)
{
	if(vol>4095) vol = 4095;
	vol |= (0b11 <<12);
	spi_write16_blocking(spi0, &vol, 1);
}

volatile float y = 0, dy;

static void alarm_0_irq() 
{
	pi_alarm_rearm(0, period);
	mcp4921_put(y);
	y += dy;
	if(y>4095) y=0;
	pi_gpio_toggle(LED);
}

int main() 
{
	stdio_init_all();
	pi_gpio_init(LED, OUTPUT);
	dy = 4095 * saw_freq / framerate;
	mcp4921_init();
	pi_alarm_init(0, alarm_0_irq, period);

	while(1);
	return 0;
}

You can get the full project on my repo.

I hate the idea of having bottlenecks in my code, especially in light of the fact that I’m interested in audio work, probably with some DSP thrown in. DMA is a way of offloading the burden from the CPU.

I have a number of ways that I’d like to progress. One would be to see if I could convert my OLED driver to exploit DMA too. Hopefully that should create interest in my own OLED project compared to the other offerings. It ought to be great for people wanting to write games. I saw a Doom port for the Pico using OLED. I could see that my library might be used to make the game more sophisticated.

It would be nice if I could speed up my SD card library, too. Using DMA might be difficult/impossible for that, though, due to the way that the RP2040’s SPI works. Maybe using interrupts is a better bet. Or perhaps PIO. But I’m still at a loss when it comes to PIO.

The official pico-extras has audio work for I2S and PWM, so that’s something that I could possible investigate. I should also check out its SD Card stuff, which uses DMA and PIO. So it’s likely to be a way better implementation than mind. It does say that it’s a hacked prototype, mind.

I’ll add another plug for my SD card library; available here. It still has significant limitations, like being read-only.

I’m thinking that my next move would be to try to take a look at the Amiga MOD audio file format. This would advance towards making a drum machine. A range of different drums would be pretty cool. So I could advance, in baby steps, towards a drum sequencer. A drum sequencer typically costs over 100 quid, so imagine putting together a little piece of kit out of a Pico which you could make for considerably less than 20 quid.

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s