Home > Atmel AVR > Atmel AVR MIDI Keyboard using STK500

Atmel AVR MIDI Keyboard using STK500

Using the STK500 as a simple MIDI keyboard
By 09/11/10 [Last Edited by Joseph 24/11/10]
BOOKMARK
LOGIN
REGISTER
Simple little project using the switches on the front of the AVR to simulate a MIDI keyboard. The USART TX pin was connected to pin 15 of the gameport on the computer, and ground to pin 4 of the gameport. I used an application named MIDI-OX to dump live MIDI data, and map the input to the Microsoft GS Wavetable Synth.



Useful sites/documentation

MIDI and the AVR
Gameport Pinout
MIDI Interface on Wikipedia
MIDI Interface at BadIdeas

NOTE: On the STK500, the AVR ATMEGA8515 is set to 3.686Mhz by default. We need a baud rate of 31250 bps (or very close) to comply with MIDI standard. Unfortunately, a 3.686Mhz clock misses this value by far, as the UBBR ends up being near 6.5, when the UBBR can only be an integer. Rounding this up or down results in a very inaccurate baud speed, and therefore complete corruption occurs. I set the fuse bit SUT_CKSEL to Int. RC osc 8Mhz to get an 8Mhz clock. I ended up with a whole number for UBBR when using this speed, which is perfect!

This code is pretty poor. It polls the switch inputs, but I couldn't be bothered to use interrupt driven code, lazy me. Also, the getkey function locks up when you press down multiple switches.

#include <avr/io.h>
#define F_CPU 8000000UL //Define crystal/RC osc frequency
#include <util/delay.h>

void init_io(void);
void init_usart(unsigned int baud);
void txbyte(unsigned char data);
unsigned char rxbyte(void);
unsigned char retkey(unsigned char input);

int main(void) 
{
	init_io();
	init_usart(15);
	unsigned char prev = 0;
	unsigned char cur = 0;

	while(1) //Infinite loop
	{
		cur = PINA; //Get switch input
		if (cur < 255) //If a switch is pressed
		{
			if (prev != cur) //If the input isn't the same as previously
			{
				txbyte(144); //NOTE_ON
				txbyte(retkey(255-cur)); //NOTE
				txbyte(100); //VELOCITY
				
			}
		}
		prev = cur;
	}
}

unsigned char retkey(unsigned char input)
{
	//Designed to make the relationship between each input switch linear
	//Will not work if you hold down multiple switches, only one at a time
	unsigned char sec = 1;
	unsigned char j = 50;
	for(unsigned char i=1; sec>0; i=i*2)
	{
		sec = input - i;
		j++;
	}
	return j;
}

void txbyte(unsigned char data) 
{
	while ( !( UCSRA & (1<<UDRE)) ); //Wait for empty slot to send data
	UDR = data; //Send it
}

unsigned char rxbyte (void) 
{
	while ( !(UCSRA & (1<<RXC)) ); //Wait until there is a byte to be read from RX buffer
	return UDR; //Get it
}

void init_io(void)
{
	DDRA = 0;
	PORTA = 255;
	//Port A is the input
}

void init_usart(unsigned int baud) 
{
	// Setup serial I/O for communication
	UBRRH = (unsigned char)(baud>>8);
	UBRRL = (unsigned char)baud;                           
	UCSRB = (1<<RXEN)|(1<<TXEN);                 
	UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}