USB MIDI Controller

I use Ableton live a lot to jam and to DJ, and using the mouse to
control all the parameters got in the way quite often. You can’t
select a scene and turn a knob at the same time, switching between
views was tiresome. I first thought about building a midibox, but then decided to build my own midi system
using an AVR microcontroller, which I’m more used to. Blueloop helped me a lot
with the schematics, showing me a nice trick to control LEDs using the
same matrix as for the switches (this is not integrated yet,
though).

Alt Tag Added during WordPress Conversion

The midi controller is quite simple. It features 33 switches and 56
potentiometers. The switches and potentiometers are laid out in a
matrix. You can select a horizontal row by pulling down a port to 0 V,
and then reading the switch values on another port, or by going
through the ADCs in the case of the potentiometers. The actual circuit
for the microcontroller is really really simple. There is an
oscillator and a resistor for the MIDI output. The rest of the I/O
pins go to a pinhead which connects the front panel to the circuit
board.

There is a second circuit board in the midi controller that
converts the MIDI output of the first circuit to USB. It was an old
USB programmer board I built 2 years ago that was lying around. I
directly connected the power of the first circuit board to the USB
connector, and connected the MIDI output to the RX pin of the USB
programmer. I had no 12 Mhz oscillator (the one which I normally use),
so I used a 20 Mhz quartz out of an old network card. The board is
really simple too, and consists of a PDIUSBD11 chip by Phillips, which
gets commands over I2C and outputs USB, and an ATMEGA8
microcontroller. Sadly, the PDIUSBD11 is not manufactured any more,
which is kind of sad, as it is the only USB controller I am aware of
that uses I2C, which spared pins on the microcontroller side. I
implemented the USB MIDI
specification
out of the specification, and the controller seems
to work under Linux and Windows (there is a problem with MacOSX, but I
last checked two years ago and haven’t tried/debugged it since
then). I can’t publish the whole code for the usb-midi adapter though,
as most of it (including the USB stack) was written by Till Harbaum.

Alt Tag Added during WordPress Conversion

The matrices are soldered on the front panel, which I made out of a
plexiglas sheet through which I drilled holes for the switches and
pots. I had no real problems with the plexiglas, except that you have
to drill quite slowly through it in order not to split it, and that I
had to remove the melt plastic around the holes using a rasp. Each
switch has a diode at one of its outputs, so that two knobs pressed at
the same time can be distinguished on the controller side. The
potentiometers are connected to VCC one one side, to the select port
on the other side, and the variable output is connected to the ADC
using a diode. When selecting a switch or a pot, the select pin is
set to GND, all the other pins are set to VCC. Because of the diodes,
the pressed switches or the pots which are not selected are always
high, and the diode won’t allow them to influence the signal.

Alt Tag Added during WordPress Conversion

Alt Tag Added during WordPress Conversion

The code on the microcontroller is almost as simple as the circuit
board. A simple ring-buffer interrupt-based UART handling is used to
handle outgoing MIDI data (the midi controller can’t receive MIDI
events).

#define RB_SIZE 16
#define MIDI_BAUD 31250UL    /* 31.25 kbps */

volatile char tx_buf[RB_SIZE];
volatile char tx_wr = 0, tx_rd = 0;
#define TX_INCR(a, b) (((a) + (b)) & (RB_SIZE - 1))
#define UART_TX_OK (UCSRA & _BV(UDRE))
#define TX_FREE (!(TX_INCR(tx_wr, 1) == tx_rd))

SIGNAL(SIG_UART_TRANS) {
  if (tx_rd != tx_wr) {
    UDR = tx_buf[tx_rd];
    tx_rd = TX_INCR(tx_rd, 1);
  }
}

void uart_putc(char c) {
  if (UART_TX_OK && (tx_wr == tx_rd)) {
    UDR = c;
  } else {
    while (TX_INCR(tx_wr, 1) == tx_rd) ;
    tx_buf[tx_wr] = c;
    tx_wr = TX_INCR(tx_wr, 1);
  }
}

void uart_init(void) {
  tx_wr = tx_rd = 0;
  
  /* set baudrate */
  UBRRL = (((F_CPU) / (16 * MIDI_BAUD)) - 1);
  /* enable TX, interrupts */
  UCSRB = _BV(TXEN) | _BV(TXCIE);

  sei();
}

The static buffer “tx_buf” is used to buffer outgoing serial
data. “tx_wr” and “tx_rd” are used as indices into the ring
buffer. “TX_INCR” handles the indices when a character is added to the
ring buffer. “TX_FREE” checks if there is free space in the ring
buffer. “UART_TX_OK” checks if the serial interface is ready to send
another character. “SIGNAL(SIG_UART_TRANS)” is the interrupt routine
that is called when a character has successfully been sent. It checks
if the ring buffer is empty, and sends the next character if it is
not. “uart_putc” is the API to the UART code, and directly sends the
character if the serial interface is ready and the ring buffer is
empty. If not, it waits until there is space in the ring buffer, and
adds the new character to the ring buffer. “uart_init” handles the
initialization of the serial interface, sets the correctly baudrate,
and enables TX and the TX_TRANS interrupt. Finally, interrupts on the
AVR are enabled.

void io_init(void) {
  /* disable watchdog */
  WDTCR = 0;

  /* DD = 1, output */
  /* PORT = 1 wenn DD = 0, dann pull-up */
  /* portd = output */
  DDRD = 0XFF;
  
  /* enable ADC */
  DDRA = 0;
  PORTA = 0xFF;
  ADMUX = _BV(ADLAR);
  ADCSRA = _BV(ADEN)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0);
  // ADCSRA = _BV(ADEN);

  uart_init();
}

The routine “io_init” initializes the Atmel, first disabling the
watchdog, then setting PORTD to output (which is not really necessary,
as the status of the port is set by the switch polling routine). The
ADC is enabled by setting the PORTA to input, enabling pullup
resistors, and enabling the ADC in a 10 bit mode, and using AVCC as
reference voltage. Finally the UART is initialized.

/*** MIDI zeugs ***/

#define MIDI_NOTE_ON 0x9
#define MIDI_NOTE_OFF 0x8
#define MIDI_AFTERTOUCH 0xA
#define MIDI_CC 0xB
#define MIDI_PROG_CHANGE 0xC
#define MIDI_CHAN_PRESS 0xD
#define MIDI_PITCH_WHEEL 0xE

#define MIDI_STATUS_TYPE(c) (((c) & 0xF0) >> 4)
#define MIDI_STATUS_CHAN(c) ((c) & 0xF)

static inline midi_is_status(char c) {
return (c & 0x80);
}

void midi_send_note_on(unsigned char channel, unsigned char note, unsigned char velocity) {
uart_putc((MIDI_NOTE_ON

The midi controller sends three types of midi messages. When a switch
is pressed, it sends a note on message, when a switch is release, it
sends a note off message, and when a pot is modified it sends a
control channel message. All three messages are three bytes long and
sent directly to the uart using "uart_putc".

#define LED_BUTTON_PORT     D
#define PORTLED_BUTTON_PORT PORTD
#define DDRLED_BUTTON_PORT DDRD
#define PINLED_BUTTON_PORT PIND

#define SET_PIN_OUTPUT(port, pin) { DDR ## port |= _BV(pin); }
#define SET_PIN_INPUT(port, pin) { DDR ## port &= ~(_BV(pin)); }
#define SET_PIN_INPUT_PULLUP(port, pin) { SET_PIN_INPUT(port, pin); PORT ## port |= _BV(pin); }
#define SET_PIN_LOW(port, pin) { PORT ## port &= ~(_BV(pin)); }
#define SET_PIN_HIGH(port, pin) { PORT ## port |= _BV(pin); }
#define PIN_VALUE(port, pin) ((PIN ## port & _BV(pin)) ? 1 : 0)

#define NUM_BUTTONS 30

#define BUTTON_STATUS(i) (button_status[(i)] & 0xF)
#define SET_BUTTON_STATUS(i, val) { button_status[(i)] = button_status[(i)] & 0xF0 | (val); }
#define LED_STATUS(i) ((button_status[(i)] >> 4) & 0xF)
#define SET_LED_STATUS(i, val) { button_status[(i)] = button_status[(i)] & 0xF | ((val) 1 (bounce?) -> 2
wenn knopf auf 1 dann nachricht senden (note on), wieder auf 0 (note off)
*/

#define BUTTON_ROWS 5
unsigned char button_status[BUTTON_ROWS * 8];

void init_buttons(void) {
int i, j;
for (i = 0; i

This entry was posted in Bastelstaub, English, Programmstaub. Bookmark the permalink.

21 Responses to USB MIDI Controller

  1. neingeist says:

    geil 🙂 mehr krach!

    +++ neingeist

  2. Blueloop says:

    Das Ding ist ja echt kühl geworden und tolle Dokumentation!

  3. gast says:

    hallo

    ich bastel auch gerade an nem midi controller, hab allerdings ziemliche probleme mim uart des ATMEGA 16. Bekomme einfach nix rauß geschrieben.
    das liegt bestimmt an der krummen baudrate der midi spezifikation. hast du den internen oszilator des avr`s genutz zur uart taktung oder nen quarz ?

  4. manuel says:

    Mit 8 Mhz als Quartz kriegst du saubere 31250 bps hin. Muesste eigentlich kein problem sein. Vielleicht reicht der Pin output nicht fuer den receiver? Hast du einen Transistor eingebaut?

  5. manyworldsproductions says:

    really cool ,any ideas on implementing an lcd?
    how are the revisions and new features coming along?? vey keen to hear your progress.
    🙂

  6. Raphael says:

    Hallo,
    könnte ich die unterlagen vom AVR-Midi-USB Adapter bekommen?

    Gruß Raphael

  7. netzstaub says:

    Hallo Raphael,

    leider nicht, die oben dokumentierte Version ist eh mit einem Chip gebaut, den es nicht mehr gibt, und der Code ist leider zum Teil nicht meiner. Allerdings habe ich eine neue Version mit dem AT90USB1286, setz dich bitte mit mir in Verbindung manuel@bl0rg.net

  8. MonZon says:

    I need to repeat your device but with different number of buttons and pots. Can you compile a firmware for atmega644? With 8 pots (each one on 1 ADC line) and 8×8 buttons matrix. I’m asking because you said that you will not give a full source. Thank you.

  9. MonZon says:

    I need to repeat your device but with different number of buttons and pots. Can you compile a firmware for atmega644? With 8 pots (each one on 1 ADC line) and 8×8 buttons matrix. I’m asking because you said that you will not give a full source. Thank you. Mail it here slamlmd (doggy) yandex.ru

  10. netzstaub says:

    Well the code for the MIDI application itself is completely available in the posting on top (yeah that’s all the firmware), so just modify it for your need. What I’m not posting is the USB MIDI code which is obsolete anyway. Cheers, manuel

  11. MonZon says:

    some forums says that it’s not possible to make USB MIDI HID device on atmega because MIDI requres bulky endpoints. I don’t know what does it mean. Can you give me any hints how to do that?

  12. alibabashack says:

    Hi well done! … Today I finished the firmware for my own device and it passed all usb9 tests and windows knows it as audio device. So far so good … now I have a working USB midi device. I want to connect it to Traktor 3, but i can’t. It is not available inside. It is no prob of tracktor. I just can’t interact with the midipart of the device. I thought the device is mapped by a standard driver of windows so I can connect it to Traktor. What did you do to connect your device under windows to ableton live? … is there a special software required?

    Alex

  13. manuel says:

    Hi alibabashack,

    I just used the standard windows driver. Maybe there is something wrong with your usb midi descriptors? It is quite tricky… Maybe if you send me a copy of them I could go over them. Did you define only one midi output? I guess windows needs to have a port with both in and out, else it won’t be recognized correctly.

  14. alibabashack says:

    thank you for quickly answering…
    my descriptors are ok … they passed the usb9 test, but your last idea is right maybe … i just defined a midi IN port because I just want to controll tracktor and need no respond from pc .. I’ll send you a dump of my descriptors and try to define an out endpoint in addition. hope it will work then… thank you very much! 🙂

  15. alibabashack says:

    hmmm doesn’t work also with out pipe …

  16. haydos says:

    Hi I am currently starting my own project similar to yours and was scouring the web for resources. Your controller looks awesome btw! Just one thing, I can’t grasp why you have the output of 2 pots connected to one terminal, i.e. I think the schematic shows there are 16 pots connected to an 8-bit A/D port, have you used a multiplexer and just not shown it or do your rotary pots interact as pairs?

  17. haydos says:

    ahh i just read over it, and the penny dropped. Would you advise not to use mux’s because of propogation delays?

  18. slvmchn says:

    this still blows my mind everytime i bring this page up. hopefully i can get started on my knob-box soon!

  19. usb says:

    I will soon start my own midi project too. 🙂

  20. It is no prob of tracktor. I just can’t interact with the midipart of the device. I thought the device is mapped by a standard driver of windows so I can connect it to Traktor.

Leave a Reply

Your email address will not be published. Required fields are marked *