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;
}
}

6 comments:

  1. WOW! It works really well! You should sell the Pre-Programmed PICs on Ebay like you did for the Hand-Cranked MIDI sequencer!

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. I will email you.. removed post to hide your email address. Cheers/jason

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Hi, I'm using PIC16f1455 for similar midi decoding project, there are some bugs in my C code and the PIC programmer hired can not solve them. If you are willing to help please let me know your e-mail, I'll sent you the code and pay with paypal

    Here is my e-mail
    ho.yuanhou@gmail.com

    Best,

    ReplyDelete