Wednesday, March 01, 2006

sog #2 - midi input with PIC

Here's some information about my recent attempts to get MIDI input to work with a PIC microcontroller. It took me a long time to get this to work correctly; info published on the net seems to focus much more heavily on MIDI output than on MIDI input. So hopefully this can help some people out in the future.

Here is a schematic for both input and output that is currently available on the web:

midi input and output schematics

We'll only be looking at the top circuit, which is MIDI input. Here is my breadboard of that top circuit:

midi input circuit on breadboard

A. The PIC 18F452, there's a good deal of info about this PIC floating around
B. A 20 Mhz powered osciallator. We'll come back to this.
C. An opto-isolator, part 6N138. This is used to prevent any one MIDI input from damaging others in a circuit. I've heard you can get by without using one of these, but I wasn't able to pull it off and also use terminal communication at the same time.
D. A zener diode, part 1N914
E. The ground from the MIDI input
F. The transmission feed from the MIDI input

The Oscillator

Let's talk about the osciallator (B) first. It's important to use a reasonably fast clock source in order to accomodate MIDI's 31250 baud transfer rate. I tried using a 20Mhz crystal coupled with 22 pf capacitors to generate this clock, but they wouldn't clock the PIC correctly at that high a speed; I read from one excellent source that 20 Mhz crystals don't work well on breadboards. A powered oscillator is just as easy to hook up anyways, and also frees up a pin on your PIC.

One other option is to use the PLL(Phase Locked Loop) feature of the PIC 18F452. This allows you to run at 4 times the speed of whatever crystal you are using. Since the PIC 18F452 runs at 40Mhz tops, if you get a 10Mhz crystal and enable PLL you should be able to run twice as fast as using the 20MHz oscillator usehere. I haven't tried it, but I know that projects at the midiBox site use this method.

When you program your PIC, make sure that your "External Clock" or "External Clock Input/Output" option is selected as the oscillator. It doesn't matter which one; EC allows you to use OSC2/CLK0 as clock output running at 1/4 speed; ECIO allows you to use that pin (RA6) as an extra input or output. I've included a fuse in the C code that selects EC automatically when the .hex file is loaded into your programmer.

The Opto-Isolator / MIDI input

There are eight pins on the isolator; only six are used. First, our MIDI input (F) is passing through a 220 ohm resistor and into the positive input of the opto-isolator(O-I). The ground from our MIDI Input (E) is connected to the negative input pin on the O-I. A diode is put in place between this two pins to make sure that current travels in the correct direction.

The other side of the O-I is more confusing, at least to me. Power enters the top pin, the bottom pin is grounded. There's a resistor placed between the top power pin and the second pin from the bottom, which is where the MIDI signal (travelling on the blue wire) is outputted from. I'm not quite sure why this is there, but I couldn't get it ot work without it, so so be it. The MIDI signal is then sent to the UART on the PIC, in this case RC7. It's easy to poll the UART for data without having to hang the chip waiting for it.

It's worth pointing out again that I've read the opto-isolator isn't strictly necessary. I was in fact able to get MIDI input going into the PIC without it. However, as soon as I hooked up another RS232 port on the chip so that I could monitor the data the PIC was receiving on a computer terminal, the MIDI no longer seemed to work correctly. Adding in the O-I fixed the problem. At least I think that was what fixed the problem...

Edit: I got a little more info about the opto-isolator from my father, who is an electrical engineer. Apparently it completely separates the input voltage from the output voltage. Information is sent from input to output using flashes of light as opposed to toggling electrical connections. In the above circuit diagram, pin 6 (the output pin) flips back and forth between being connected to pin 5 (the ground) and pin 8 (power). When it is flipped to ground (most likely when no light is being transmitted across the o-i) the current flows to the +5 input. The 270 ohm resistor prevents a short circuit in this event. When it is flipped to power, the signal flows to the microcontroller, as this is the path of least resistance (no 270 ohm resistor).


If you don't know how to use the Watchdog timer, make sure you turn it off. There's a fuse in the code below that turns it off automatically.

If you want to use a RS-232 connection to a computer terminal in addition to MIDI, make sure you send the ground connection of the RS-232 through a 22k resistor... otherwise it seems to interfere with the MIDI signal somehow.

The Code

This code was written for the CCS C compiler. When noteOn messages are received it will pin high on D0 (lighting up the LED in my breadboard picture). When noteOff messages are received it will pin low on D0, turning the LED off. This code only manipulates note messages, it ignores everything else. I made this code as simple as possible to just show the basic concepts; still it would be good if you knew a little bit about MIDI messages before looking at the code. There's a great link at the bottom of the article which will tell you more than you ever needed to know about MIDI.

/* Midi Input - 2006 Charlie Roberts -
** Outputs high to a selected pin (D0) when a noteon MIDI message is received,
** outputs low to the pin when a noteoff message is received
** Written for the PIC 18F452 and the CCS C compiler

#fuses EC, NOWDT
#use delay(clock=20000000)

#use rs232(stream=MIDI, baud=31250, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8)

BYTE msg[3]; // an array that will hold store MIDI info until an entire three byte message is received
BYTE msgCheck; // use to check if we are dealing with note messages.
int count=0; // count the number of MIDI bytes received in the current message
int1 startTest=0; // have we started a note message yet?

void performMsg(); // turn pin D0 on or off based on type of MIDI message received

#int_RDA // this interrupt occurs whenever info is "RS232 (R) Data (D) is Available (A)"
void RxBytes() { // the function to be carried out when the intterupt occurs
msgCheck = fgetc(MIDI); // get the byte currently in the MIDI stream
if(count == 0 && msgCheck == 0x90) {
// if we have not started a message and this is the start byte of a note message
msg[0] = msgCheck; // store the byte in our msg array
count++; // update the count so that we know we have one byte of our message
startTest = 1; // we have started a new message
}else if (startTest == 1) {
msg[count] = msgCheck; // store the next byte
if (count == 3) { // ... then we have a complete MIDI note message
count = 0;
startTest = 0;

void performMsg() {
if (msg[2] != 0x00) { // if it is not a 0 velocity noteOff message
output_high(PIN_D0); // light the led on PIN_Do
output_low(PIN_D0); // turn the LED off

main() {
while(TRUE) {
// empty loop to keep program going

Suggested Links
Excellent MIDI reference
Information on Tom Igoe's site about MIDI I/O.


Blogger underwood said...

Hello, nice info. But the shematic gif and the page is down!

7:22 AM  

Post a Comment

<< Home