Sunday, June 03, 2018

Monophonic USB MIDI to CV Converter with 8 Octave Range

This is a USB-powered monophonic USB MIDI to CV converter with a roughly 8 octave range, from roughly -3.3V to 5V for pitch CV, 0V to 4.096V for velocity CV and a 3.3V gate for note on / note off events. The DAC is 12 bits, meaning that the range of 8.32V at a rate of 1V per octave is distributed across 4096 steps, yielding a pitch resolution of approximately 2.43 cents per data step.

The Teensy LC separates an incoming MIDI note message into values for pitch CV, velocity CV and a note gate. The pitch and velocity values are sent to an MCP4822 dual 12-bit DAC.

The first channel of the MCP4822 is amplified by two using the first op-amp of a TL072, and then offset by negative 3.3V by using the second op-amp of a TL072 resulting in a pitch CV range of approximately -3.3V to 5V. The velocity CV and note gate are fed directly to the Eurorack setup via a 1k resistors.

A Digilent Powerbrick uses 5V from the VIN on the Teensy LC and transforms this into  ±12V power supply, used to power the TL072.

This type of setup and can be refined and scaled to higher bit depth resolutions by using various DACs, different voltage ranges by changing the ratio between R2 and R1 (for scale) and feeding a different voltage into R8 (for offset).

#include <SPI.h>

int cs_pin = 0;
int gate_pin = 14;
int amp_gain = 0; // gain for amplifier - 0 = 2x, 1 = 1x
int previous_pitch;
float pitch_change_value;
float pitch_value;
float maximum_voltage = 8.32; // voltage output after op amps
float maximum_pitch_value = maximum_voltage * 12.0; 
float number_of_steps = 4096; // DAC resolution

void setup() {
  pinMode(cs_pin, OUTPUT);
  pinMode(gate_pin, OUTPUT);
  digitalWriteFast(cs_pin, HIGH);

  SPI.begin();
  usbMIDI.setHandleNoteOn(OnNoteOn);
  usbMIDI.setHandleNoteOff(OnNoteOff);
  usbMIDI.setHandlePitchChange(OnPitchChange);

}

void loop() {
  usbMIDI.read();
}

void OnNoteOn (byte channel, byte pitch, byte velocity) {
  if (channel == 1) {
    if (velocity > 0) {

      previous_pitch = pitch;
      pitch_value = map(previous_pitch + pitch_change_value, 0.0, maximum_pitch_value, 0.0, number_of_steps);
      writeDAC(cs_pin, 0, pitch_value);
      writeDAC(cs_pin, 1, velocity << 5);
      digitalWriteFast(gate_pin, HIGH);
    }

    else {
      digitalWriteFast(gate_pin, LOW);
    }
  }
}

void OnNoteOff (byte channel, byte pitch, byte velocity) {
  if (channel == 1) {
    digitalWriteFast(gate_pin, LOW);
  }
}

void OnPitchChange (byte channel, int pitch_change) {
  if (channel == 1) {
    pitch_change_value = map((float) pitch_change, 0.0, 16383.0, -12.0, 12.0);
    pitch_value = map(previous_pitch + pitch_change_value, 0.0, maximum_pitch_value, 0.0, number_of_steps);
    writeDAC(cs_pin, 0, pitch_value);
  }
}

void writeDAC (int cs, int dac, int val) {
  digitalWrite(cs, LOW);
  dac = dac & 1;
  val = val & 4095;
  SPI.transfer(dac << 7 | amp_gain << 5 | 1 << 4 | val >> 8);
  SPI.transfer(val & 255);
  digitalWrite(cs, HIGH);
}






0 comments: