Sunday, 24 October 2010

Solenoid Drum Machine

Just a quick project to try out some new push solenoids...

Based on PIC16F688 and building on the MIDI input code used on my earlier POKEY project


include <system.h>
#include <memory.h>

#pragma DATA _CONFIG, _MCLRE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
#pragma CLOCK_FREQ 8000000
typedef unsigned char byte;

// define the pins
//#define P_LED portc.0

// MIDI defs
#define MIDIMSG(b) ((b)>>4)
#define MIDICHAN(b) ((b)&0xf)
#define MIDIMSG_NOTEON 0x09
#define MIDIMSG_NOTEOFF 0x08

// MIDI message registers
byte runningStatus = 0;
int numParams = 0;
byte midiParams[2] = {0};

#define SZ_RXBUFFER 20
byte rxBuffer[SZ_RXBUFFER];
byte rxHead = 0;
byte rxTail = 0;
int tmr[4] = {0};

////////////////////////////////////////////////////////////
// INTERRUPT HANDLER CALLED WHEN CHARACTER RECEIVED AT
// SERIAL PORT
void interrupt( void )
{
// check if this is serial rx interrupt
if(pir1.5)
{

// get the byte
byte b = rcreg;

// calculate next buffer head
byte nextHead = (rxHead + 1);
if(nextHead >= SZ_RXBUFFER)
{
nextHead -= SZ_RXBUFFER;
}

// if buffer is not full
if(nextHead != rxTail)
{
// store the byte
rxBuffer[rxHead] = b;
rxHead = nextHead;
}
}
}


////////////////////////////////////////////////////////////
// INITIALISE SERIAL PORT FOR MIDI
void init_usart()
{
pir1.1 = 1; //TXIF
pir1.5 = 0; //RCIF

pie1.1 = 0; //TXIE no interrupts
pie1.5 = 1; //RCIE interrupt on receive

baudctl.4 = 0; // SCKP synchronous bit polarity
baudctl.3 = 1; // BRG16 enable 16 bit brg
baudctl.1 = 0; // WUE wake up enable off
baudctl.0 = 0; // ABDEN auto baud detect

txsta.6 = 0; // TX9 8 bit transmission
txsta.5 = 1; // TXEN transmit enable
txsta.4 = 0; // SYNC async mode
txsta.3 = 0; // SEDNB break character
txsta.2 = 0; // BRGH high baudrate
txsta.0 = 0; // TX9D bit 9

rcsta.7 = 1; // SPEN serial port enable
rcsta.6 = 0; // RX9 8 bit operation
rcsta.5 = 1; // SREN enable receiver
rcsta.4 = 1; // CREN continuous receive enable

spbrgh = 0; // brg high byte
spbrg = 15; // brg low byte (31250)

}


byte rxInc(byte *pbIndex)
{
// any data in the buffer?
if((*pbIndex) == rxHead)
return 0;

// move to next char
if(++(*pbIndex) >= SZ_RXBUFFER)
(*pbIndex) -= SZ_RXBUFFER;
return 1;
}

////////////////////////////////////////////////////////////
// RECEIVE MIDI MESSAGE
// Return the status byte or 0 if nothing complete received
// caller must check midiParams array for byte 1 and 2
byte receiveMessage()
{
// buffer overrun error?
if(rcsta.1)
{
rcsta.4 = 0;
rcsta.4 = 1;
}

// any data in the buffer?
if(rxHead == rxTail)
return 0;

// peek at next char in buffer
byte rxPos = rxTail;
byte q = rxBuffer[rxPos];

// is it a channel msg
if((q&0x80)>0)
{
runningStatus = 0;
switch(q&0xf0)
{
case 0x80: // Note-off 2 key velocity
case 0x90: // Note-on 2 key veolcity
case 0xA0: // Aftertouch 2 key touch
case 0xB0: // Continuous controller 2 controller # controller value
case 0xC0: // Patch change 2 instrument #
case 0xE0: // Pitch bend 2 lsb (7 bits) msb (7 bits)
runningStatus = q;
numParams = 2;
break;
case 0xD0: // Channel Pressure 1 pressure
runningStatus = q;
numParams = 1;
break;
case 0xF0: // (non-musical commands) - ignore all data for now
return q;
}
// step over the message
if(!rxInc(&rxPos))
return 0;

}

// do we have an active channel message
if(runningStatus)
{

// read params
for(int thisParam = 0; thisParam < numParams; ++thisParam)
{
midiParams[thisParam] = rxBuffer[rxPos];
if(!rxInc(&rxPos))
return 0;
}
// commit removal of message
rxTail = rxPos;
return runningStatus;
}
else
{
// remove char from the buffer
rxInc(&rxTail);
return q;
}
return 0;
}


void main()
{
// osc control / 8MHz / internal
osccon = 0b01110001;

// timer0... configure source and prescaler
cmcon0 = 7;

// enable serial receive interrupt
intcon = 0b11000000;
pie1.5 = 1;

// configure io
trisa = 0b00010000;
trisc = 0b00110000;
ansel = 0b00000000;

porta=0;
portc=0;
memset(tmr,0,sizeof(tmr));

// initialise MIDI comms
init_usart();


// loop forever
for(;;)
{
// get next MIDI note
byte msg = receiveMessage();
if(msg)
{
byte note = midiParams[0];
if(note >= 48 && note < 52)
{
int which = note-48;
// 0x90 note on
// 0x80 note ff
if((msg & 0xf0) == 0x90)
{
tmr[which] = 200;
}
}
}

for(int i=0;i<4;++i)
{
if(tmr[i] > 0)
tmr[i]--;
}

porta.2 = (tmr[0]>0)?1:0;
portc.0 = (tmr[1]>0)?1:0;
portc.1 = (tmr[2]>0)?1:0;
portc.2 = (tmr[3]>0)?1:0;
}
}