I don’t want anyone thinking they’re going to see a bunch of music related items on the blog because I’m really no good at such things. My brother on the other hand is very talented and we recently decided he needed some more MIDI toys. A lot of his music is already created with the help of MIDI.
MIDI is “Musical Instrument Digital Interface“; a standard that defines hardware and protocol. If you want more information on it such as specifications Google is your friend.
My brother had some information on the specification but not really anything that helped me much. I did a lot of reading on the specifications on the MIDI Manufacturers Association webpage and then countless other sites describing the protocol in length. It even looks like there is a tutorial on the Arduino site, although I didn’t look at it because I don’t use them. Again, I’m not going to go into the protocol anymore than I discussed the specification. I’ll let you do that leg work. However, the *really* short version is there are a couple key commands followed (usually) by some extra bytes (generally 1 or 2). All the data is transmitted like normal 8N1 serial data at 31.25kbaud. Lucky for me 31.5k has a nice PIC SPBRG division value for low error rate (at 4MHz).
Hardware Interface: I decided I wasn’t going to build the 5mA loop interface and I remembered the Toymakers ( @tymkrs ) had a nice little interface board already built. One trip on over to their Tinde Store and I had it a few days later.
It’s a small kit, it came well packed, and it fit the bill just fine. I have nothing but praise this little kit with only one tiny little nit-picky mention. I wish they included a URL of the location of the instructions for building the kit. They had a couple resistors and I needed to know what was R1, R2, etc.. it was easy to find, a return trip to Tindie linked instructions, a construction video! and a lot of information on their website. So I know I’m picky.. If they wanted to make a 9.7 a 10.. that’d be it ๐
After everything was plugged in, code was loaded on the PIC, a MIDI device plugged in.. etc.. I checked out the MIDI kit to see response and how noisy it might be… no complaints here.. it looked good…
The Firmware: It’s just “sample” code. I haven’t written any real output yet because my brother is in charge of the “analog” bits which really means he is going to figure out what he wants in block diagrams and I will have to figure out how to make it happen in circuit. Right now I’m reading the MIDI signal in on UART by interrupt… and checking for my command signal. (I’m using a Control Change because of my MIDI device… you will most likely want to change this). The control change value is 0xB1 in my case, then… my device (Knob #1) which is 0x11 … and finally it gives me a value (knob position 0x00 to 0x7F). I’m taking that position value and transmitting it out the UART to my PC…
I used the TAUTIC 20 pin dev board (any groaning of “AGAIN??”??) … but a change-up! ๐ I used a PIC 18F14K22 … because I felt like getting crazy ๐ and the 18 series is optimized for C so I will probably go back to the 18F series … I think the last time I used one was my ESR meter? A few changes switching to the 18 series.. but nothing huge. Just getting used to slightly different registers. Last warnings about the code; It’s simple… it’s just checking to see if your interface it working… (or if you’re sending MIDI and you don’t have a scope or LA). It has a lot of clutter because I was using it for some other non-MIDI related testing but it’s easy to spot and delete if you need to copy it as a starting point for whatever you’re working on. (Do share!)
/*
* File: main.c
* Author: Charles M Douvier
* Contact at: http://iradan.com
*
* Created on February 8, 2014, 11:39 AM
*
* Target Device:
* 18F14K22 on Tautic 20 pin dev board
*
* Project: MIDI Slave
*
*
* Version:
* 0.1 Configuration, 31.25Kbaud TX&RX
* 0.2 Grab MIDI byte from 31.25K MIDI and turn around and TX the value of the
* MIDI command. <CMD: Control Change><Device><Value>
* <B1><11><value> //my example
*
*/
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//config bits
#pragma config FOSC=IRC, WDTEN=OFF, PWRTEN=OFF, MCLRE=ON, CP0=OFF, CP1=OFF, BOREN=ON
#pragma config STVREN=ON, LVP=OFF, HFOFST=OFF, IESO=OFF, FCMEN=OFF
#define _XTAL_FREQ 4000000 //defined for delay
/*
* Variables
*/
long int decm; //long temp
int tempi; //temp
int i, ilevel; //temp
int itxdata; //int RS232 tx data
char buf[10]; //buff for iota
volatile unsigned int uart_data; // use 'volatile' qualifer as this is changed in ISR
/*
* Functions
*/
void interrupt ISR() {
if (PIR1bits.RCIF) // see if interrupt caused by incoming data
{
uart_data = RCREG; // read the incoming data
PIR1bits.RCIF = 0; // clear interrupt flag
//
}
}
void uart_xmit(unsigned int mydata_byte) {
while(!TXSTAbits.TRMT); // make sure buffer full bit is high before transmitting
TXREG = mydata_byte; // transmit data
}
void serial_init(void)
{
// calculate values of SPBRGL and SPBRGH based on the desired baud rate
//
// For 8 bit Async mode with BRGH=0: Desired Baud rate = Fosc/64([SPBRGH:SPBRGL]+1)
// For 8 bit Async mode with BRGH=1: Desired Baud rate = Fosc/16([SPBRGH:SPBRGL]+1)
TXSTAbits.BRGH=1; // select low speed Baud Rate (see baud rate calcs below)
TXSTAbits.TX9=0; // select 8 data bits
TXSTAbits.TXEN = 1; // enable transmit
RCSTAbits.SPEN=1; // serial port is enabled
RCSTAbits.RX9=0; // select 8 data bits
RCSTAbits.CREN=1; // receive enabled
//BRGH=1 31.25KHz
//SPBRG=7
SPBRG=7; //
PIR1bits.RCIF=0; // make sure receive interrupt flag is clear
PIE1bits.RCIE=1; // enable UART Receive interrupt
INTCONbits.PEIE = 1; // Enable peripheral interrupt
INTCONbits.GIE = 1; // enable global interrupt
__delay_ms(50); // give time for voltage levels on board to settle
// uart_xmit('S'); // transmit a character example
}
void init_io(void) {
TRISAbits.TRISA0 = 0; // output
TRISAbits.TRISA1 = 0; // output
TRISAbits.TRISA2 = 0; // output
TRISAbits.TRISA4 = 0; // output
TRISAbits.TRISA5 = 0; // output
ANSEL = 0x00; // no A/D
ANSELH = 0x00;
TRISBbits.TRISB4 = 0; // RB4 = nc
TRISBbits.TRISB5 = 1; // RB5 = nc
TRISBbits.TRISB6 = 0; // RB6 = nc
TRISBbits.TRISB7 = 0; // RB7 = nc
TRISCbits.TRISC0 = 0; // output
TRISCbits.TRISC1 = 0; // output
TRISCbits.TRISC2 = 0; // output
TRISCbits.TRISC3 = 0; // output
TRISCbits.TRISC4 = 0; // output
TRISCbits.TRISC5 = 0; // output
TRISCbits.TRISC6 = 1; // input
TRISCbits.TRISC7 = 1; // input
}
void set_cmd11(void)
{
//set an AD output
while(uart_data==0x11)
{
i++; //wait for next char
}
ilevel = uart_data; //finally the value
uart_xmit(ilevel);
}
void check0xb1(void)
{
if (uart_data == 0xB1)
{
while(uart_data==0xB1)
{
i++; //wait for next char
}
if ( uart_data == 0x11) //and the knob 1 is "device 11"...
set_cmd11();
}
}
int main(void) {
init_io();
// set up oscillator control register, using internal OSC at 4MHz.
OSCCONbits.IRCF = 0x05; //set OSCCON IRCF bits to select OSC frequency 4MHz
OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
serial_init();
decm=30; //testing
ltoa(buf,decm,10); //long conversion to buffer
tempi=strlen(buf); //uh, adding leading zeros..
uart_xmit('+');
uart_xmit(itxdata);
uart_xmit('.');
LATAbits.LATA0=0;
while (1) {
i++;
while (uart_data)
{
check0xb1(); //my midi device is sending a Control Change 0xB1
uart_data=0;
}
}
return (EXIT_SUCCESS);
}