dac.c 2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
#include <avr/io.h>
#include <util/delay.h>
#include "dac.h"
#include "voices.h"

const uint8_t dac_chan[NUM_VOICES] = {2, 3, 4, 5};

// Offset to get a consistent "zero" on each CV output
const uint16_t dac_offset[NUM_VOICES] = {
	57,
	48,
	50,
	55,
};

// This number corresponds to a 1V increase at the respective CV output.
const uint16_t dac_scale[NUM_VOICES] = {
	8382,
	8382,
	8383,
	8384,
};

// This tuning is chosen to be 'good enough' for the 8382-8384 scale range above
const uint16_t dac_semitones[12] = {
	0,
	699,
	1397,
	2096,
	2794,
	3493,
	4192,
	4890,
	5589,
	6287,
	6986,
	7684,
};

uint8_t
spi_io(uint8_t data)
{
	SPDR = data;
	loop_until_bit_is_set(SPSR, SPIF);
	return SPDR;
}

#define DAC_SYNC PORTB1
#define MOSI PORTB3
#define MISO PORTB4
#define SCK PORTB5
#define SS PORTB2

void
send_dac(uint8_t command, uint16_t data)
{
	PORTB &= ~_BV(DAC_SYNC);
	spi_io(command);
	spi_io(data >> 8);
	spi_io(data & 0xff);
	PORTB |= _BV(DAC_SYNC);
}


void
spi_init(void)
{
	// slave select, sck, mosi all as output
	DDRB |= _BV(SS) | _BV(MOSI) | _BV(SCK);

	PORTB |= _BV(MISO); // MISO pull-up to save power

	// spi enable
	SPCR |= _BV(SPE) | _BV(MSTR) | _BV(CPOL);
	SPSR |= _BV(SPI2X);
}

void
dac_init(void)
{
	DDRB |= _BV(DAC_SYNC);

	spi_init();

	// dac sync high for power saving
	PORTB |= _BV(DAC_SYNC);

	_delay_ms(1); // let dac boot up

	send_dac(0b01000000, 0b1111000000001111); // power down dacs 0, 1, 6, 7
}

#define DAC_MAX 65535
#define MIDI_LOWEST 24

void
dac_set_note(uint8_t voice, uint8_t note)
{
	if (voice >= NUM_VOICES) {
		return;
	}

	// Don't go lower than "C0"
	if (note < MIDI_LOWEST) {
		note = MIDI_LOWEST;
	}
	if (note > 127) {
		note = 127;
	}

	uint8_t octave = (note - MIDI_LOWEST) / 12;
	if (octave > 7) {
		return;
	}

	uint16_t data = dac_offset[voice] + octave * dac_scale[voice];

	uint16_t semi_offset = dac_semitones[(note - MIDI_LOWEST) % 12];
	if ((DAC_MAX - data) < semi_offset) {
		data = DAC_MAX;
	} else {
		data += semi_offset;
	}

	send_dac(0b00110000 | dac_chan[voice], data);
}