Sunday 28 February 2010

When Lava met Mopho

I got a new toy! an Mopho analog synth from Dave Smith Instruments. First use.. a companion to my MIDI lavalamp to free it up from PC based soft synths. Since my original MIDI lavalamp project was based on a standard Arduino, I decided this time to make a quick little module for it using a PIC16F688 (my current weapon of choice)



Here is the circuit on stripboard



...the mess on the back...



...and the schematic...



... the lamp is wired up as described on this post

... and the PIC source code is included below (SourceBoost compiler)

#include <system.h>

#include <memory.h>



#define ANA_0 0b00000000

#define ANA_1 0b00000100

#define ANA_2 0b00001000

#define ANA_3 0b00001100

#define ANA_4 0b00010000

#define ANA_5 0b00010100

#define ANA_6 0b00011000

#define ANA_7 0b00011100

#define ADC_MAX 6



#define P_HEARTBEAT porta.5



#pragma DATA _CONFIG, _MCLRE_OFF&_WDT_OFF&_INTRC_OSC_NOCLKOUT

#pragma CLOCK_FREQ 8000000

#define ADC_AQUISITION_DELAY 10



typedef unsigned char byte;



enum {

ADC_CONNECT,

ADC_ACQUIRE,

ADC_CONVERT

};



void init_usart()

{

pir1.1 = 1; //TXIF transmit enable

pie1.1 = 0; //TXIE no interrupts



baudctl.4 = 0; // synchronous bit polarity

baudctl.3 = 1; // enable 16 bit brg

baudctl.1 = 0; // wake up enable off

baudctl.0 = 0; // disable auto baud detect



txsta.6 = 0; // 8 bit transmission

txsta.5 = 1; // transmit enable

txsta.4 = 0; // async mode

txsta.2 = 0; // high baudrate BRGH



rcsta.7 = 1; // serial port enable

rcsta.6 = 0; // 8 bit operation

rcsta.4 = 0; // enable receiver



spbrgh = 0; // brg high byte

spbrg = 15; // brg low byte (31250)

}



void send(unsigned char c)

{

txreg = c;

while(!txsta.1);

}



void sendController(byte channel, byte controller, byte value)

{

P_HEARTBEAT = 1;

send(0xb0 | channel);

send(controller&0x7f);

send(value&0x7f);

P_HEARTBEAT = 0;

}



byte adcInput[ADC_MAX] = {ANA_2, ANA_3, ANA_4, ANA_5, ANA_6, ANA_7};

byte adcInitComplete = 0;

int adcResult[ADC_MAX] = {-1,-1,-1,-1,-1,-1};

int adcIndex = 0;

int adcState = ADC_CONNECT;



////////////////////////////////////////////////////////////////

//

// doADC

//

// State machine for running the ADC and updating the adcResult

// array with the result from each analog input

//

void doADC()

{

switch(adcState)

{

// Connect ADC to the correct analog input

case ADC_CONNECT:

adcon0=0b10000001 | adcInput[adcIndex];

tmr0 = 0;

adcState = ADC_ACQUIRE;

// fall through



// Waiting for a delay while the ADC input settles

case ADC_ACQUIRE:

if(tmr0 > ADC_AQUISITION_DELAY)

{

// Start the conversion

adcon0.1=1;

adcState = ADC_CONVERT;

}

break;



// Waiting for the conversion to complete

case ADC_CONVERT:

if(!adcon0.1)

{

// store the result

adcResult[adcIndex] = (((int)adresh)<<8)|adresl;



// and prepare for the next ADC

if(++adcIndex>=ADC_MAX)

{

adcIndex = 0;

adcInitComplete = 1;

}

adcState = ADC_CONNECT;

}

break;

}

}





#define BUFLEN 8

typedef struct

{

char midiChannel;

char midiController;

int minADC;

int maxADC;

char currentValue;

char history[BUFLEN];

} CONTROLLER;



CONTROLLER controllers[ADC_MAX] = {0};



void initInput(int which, byte channel, byte controller)

{

controllers[which].midiChannel = channel;

controllers[which].midiController = controller;

controllers[which].minADC = -1;

controllers[which].maxADC = -1;

controllers[which].currentValue = -1;

}



void checkInput(int which)

{

// pointer to the controllers

CONTROLLER *p = &controllers[which];



// read the raw analog value 0-1023

int adc = adcResult[which];



// remember highest and lowest values

if((p->minADC == -1) || (p->minADC > adc))

p->minADC = adc;

if((p->maxADC == -1) || (p->maxADC < adc))

p->maxADC = adc;



// get the range of known readings

int range = p->maxADC - p->minADC;

if(range < 1)

range = 1;



// scale the current value into the range

// NB no floating point support...

int newValue = (127*(adc - p->minADC))/range;



// add the value into the history buffer

long smoothed = 0;

for(int j=0; j<BUFLEN-1;++j)

{

p->history[j] = p->history[j+1];

smoothed += p->history[j];

}

p->history[BUFLEN-1] = newValue;

smoothed += newValue;

smoothed /= BUFLEN;



// has the value changed?

if(smoothed != p->currentValue)

{

sendController(p->midiChannel, p->midiController, smoothed);

p->currentValue= smoothed;

}

}



void main()

{

int i;



// osc control / 8MHz / internal

osccon = 0b01110001;



// timer0... configure source and prescaler

option_reg = 0b10000011;

cmcon0 = 7;



// configure io

trisa = 0b00001010;

trisc = 0b00001111;

ansel = 0b11111100;



// turn on the ADC

adcon1=0b00100000; //fOSC/32

adcon0=0b10000001; // Right justify / Vdd / AD on



// initialise MIDI comms

init_usart();



// Initialise the controllers

initInput(0, 0, 1);

initInput(1, 0, 2);

initInput(2, 0, 4);

initInput(3, 0, 7);

initInput(4, 0, 11);

initInput(5, 0, 74);



adcInitComplete = 0;

for(;;)

{

doADC();

if(adcInitComplete)

{

for(i=0;i<ADC_MAX;++i)

checkInput(i);

adcInitComplete = 0;

delay_ms(20);

}

}

}

Sunday 21 February 2010

Stylophone MIDI controller

A few months ago I used an Arduino clone board to send MIDI messages out of a Stylophone. I always intended to take it to the next level and get another Stylophone (preferably a broken one) and rip out the guts to fit all the electronics inside, and also add a few buttons and pots for perfomance controllers.

Well, I finally got round to it. This time I am using a PIC16F688 microcontroller.. this little monkey only has 14 pins and costs a mere £1 yet it has a built in clock, serial port and ADC, which means its pretty much the *only* component needed in this project (with the exception of a couple of resistors and switches).

I added a pitchbend pot, a modwheel and a pot to control the note velocity. And pushbuttons to shift octaves and "hold" a MIDI note (basically force the code to forget to send note-off message so the last note rings on after lifting the stylus). This allows a kind of polyphonic drone out of the usually strictly monophonic stylophone.



I will include the code below. I wont bother with a schematic, but the wiring to the PIC16F688 is as follows

pin 1 - 5 volt supply
2 - octave UP momentary switch (other side of switch connected to ground)
3 - octave DOWN momentary switch (other side of switch connected to ground)
6 - to pin 5 of MIDI out socket via a 220R resistor. Pin 4 of the socket is pulled up to 5V via another 220R resitor
7 - wiper of PITCHBEND pot (100k). Pot terminal between from ground/+5V
8 - wiper of VELOCITY pot (100k). Pot terminal between from ground/+5V
9 - to the stylus. Also pulled up to +5v via 470k resistor
10 - activity LED via 1k resistor
11 - wiper of MOD WHEEL pot (100k). Pot terminal between from ground/+5V
13 - HOLD NOTE momentary switch (other side of switch connected to ground)
14 - to ground

If you want to run it from a PP3 you'll need a 5V voltage regulator. You also need to connect the stylophone keyboard/resistor ladder between 0V and 5V and you will need to set up the scale[] array based on the ADC values you get from each pad on *your* stylophone keyboard (which are almost certainly different to mine)

A few photos






// MIDI STYLOPHONE.. PIC16F688.. (c) 2010 hotchk155

// SourceBoost C



// Header files

#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 MIDI_A 45 // default root note

#define NO_NOTE 0x7f // means stylus "off keyboard"

#define NUM_PADS 20 // number of stylophone pads



#define BUTTON_DEBOUNCE 10 // debounce octave buttons

#define ADC_AQUISITION_DELAY 10 // settling time for ADC

#define PBD_TOL 16 // tolerance applied to pitchbend ADC

#define MOD_TOL 5 // tolerance applied to modulation ADC



// Digital pins

#define P_HEARTBEAT portc.0 // activity LED

#define P_UP porta.5 // octave UP button

#define P_DN porta.4 // octave DOWN button

#define P_HOLD porta.0 // note hold button



// Analog pin mappings

#define ANA_MOD 0b00001000 // AN2 - MOD WHEEL

#define ANA_KBD 0b00010100 // AN5 - KEYBOARD STYLUS

#define ANA_VEL 0b00011100 // AN6 - VELOCITY

#define ANA_PBD 0b00011000 // AN7 - PITCHBEND



// define the four analog inputs

enum {

ADC_KBD,

ADC_VEL,

ADC_MOD,

ADC_PBD,

ADC_MAX

};



// for the state machine which read analog inputs

enum {

ADC_CONNECT,

ADC_ACQUIRE,

ADC_CONVERT

};



// define the ADC readings for each stylophone key pad. This

// is likely to be different if you make your own circuit, so

// you will need to work out your own ADC values

int scale[NUM_PADS+1] = {

0x000,

0x043,

0x081,

0x0b9,

0x0ed,

0x110,

0x149,

0x172,

0x199,

0x1bd,

0x1df,

0x1ff,

0x21c,

0x238,

0x252,

0x26b,

0x281,

0x298,

0x2ab,

0x2c0,

0x3ff

};



// midi note at bottom of scale (can be shifted

// up and down by an octave at a time)

char baseNote = MIDI_A;



// data used by doADC function

byte adcInput[ADC_MAX] = {ANA_KBD, ANA_VEL, ANA_MOD, ANA_PBD};

byte adcInitComplete = 0;

int adcResult[ADC_MAX] = {-1,-1,-1,-1};

int adcIndex = 0;

int adcState = ADC_CONNECT;



////////////////////////////////////////////////////////////////

//

// init_usart

//

// Initialise the PIC16F688 USART (serial port) according to the

// requirements of sending MIDI traffic

//

void init_usart()

{

pir1.1 = 1; //TXIF transmit enable

pie1.1 = 0; //TXIE no interrupts



baudctl.4 = 0; // synchronous bit polarity

baudctl.3 = 1; // enable 16 bit brg

baudctl.1 = 0; // wake up enable off

baudctl.0 = 0; // disable auto baud detect



txsta.6 = 0; // 8 bit transmission

txsta.5 = 1; // transmit enable

txsta.4 = 0; // async mode

txsta.2 = 0; // high baudrate BRGH



rcsta.7 = 1; // serial port enable

rcsta.6 = 0; // 8 bit operation

rcsta.4 = 0; // enable receiver



spbrgh = 0; // brg high byte

spbrg = 15; // brg low byte (31250 baud)

}



////////////////////////////////////////////////////////////////

//

// send

//

// Send a single byte out on the serial port

//

void send(byte c)

{

txreg = c;

while(!txsta.1);

}



////////////////////////////////////////////////////////////////

//

// sendController

//

// Send a MIDI continous controller message

//

void sendController(byte channel, byte controller, byte value)

{

P_HEARTBEAT = 1;

send(0xb0 | channel);

send(controller&0x7f);

send(value&0x7f);

P_HEARTBEAT = 0;

}



////////////////////////////////////////////////////////////////

//

// startNote

//

// Send a MIDI note on message (or note off if 0 velocity)

//

void startNote(byte channel, byte note, byte velocity)

{

P_HEARTBEAT = 1;

send(0x90 | channel);

send(note&0x7f);

send(velocity&0x7f);

P_HEARTBEAT = 0;

}



////////////////////////////////////////////////////////////////

//

// pitchBend

//

// Send a MIDI pitchbend message (14 data bits)

//

void pitchBend(byte channel, int value) {

P_HEARTBEAT = 1;

byte msb = (value>>7)&0x7f;

byte lsb = value&0x7f;

send(0xE0 | channel);

send(lsb);

send(msb);

P_HEARTBEAT = 0;

}



////////////////////////////////////////////////////////////////

//

// doADC

//

// State machine for running the ADC and updating the adcResult

// array with the result from each analog input. This function is

// called periodically and keeps the adcResult[] array updated so

// other code can just check the array rather than making direct

// calls to the ADC

//

void doADC()

{

switch(adcState)

{

// Connect ADC to the correct analog input

case ADC_CONNECT:

adcon0=0b10000001 | adcInput[adcIndex];

tmr0 = 0;

adcState = ADC_ACQUIRE;

// fall through



// Waiting for a delay while the ADC input settles

// - this is neededs or you can get garbage readings

// as the ADC transitions between one input voltage

// and another. The TMR0 (timer 0) register is used for

// timings this

case ADC_ACQUIRE:

if(tmr0 > ADC_AQUISITION_DELAY)

{

// Start the conversion

adcon0.1=1;

adcState = ADC_CONVERT;

}

break;



// Waiting for the conversion to complete

case ADC_CONVERT:

if(!adcon0.1)

{

// store the result. Note that the PIC16F688 has

// a 10 bit ADC so we need to form a 10 bit value

// from ADRESH and ADRESL

adcResult[adcIndex] = (((int)adresh)<<8)|adresl;



// and prepare for the next ADC

if(++adcIndex>=ADC_MAX)

{

adcIndex = 0;



// flag that each ADC has been read at least

// one time, so adcResult now contains valid

// information

adcInitComplete = 1;

}

adcState = ADC_CONNECT;

}

break;

}

}



////////////////////////////////////////////////////////////////

//

// getNote

//

// Map ADC values from the stylus to MIDI note values by

// looking for the scale[] entry which lies closest to the

// input value

//

char getNote(int input)

{

for(int i = 0; i < NUM_PADS; ++i)

{

int lo = 0;

int hi = 0x3ff;

if(i>0)

{

lo = (scale[i-1] + scale[i]) / 2;

}

if(i<NUM_PADS)

{

hi = (scale[i] + scale[i+1]) / 2;

}

if(input >=lo && input <=hi)

{

if(i==NUM_PADS)

return NO_NOTE;

return baseNote+i;

}

}

return NO_NOTE;

}



////////////////////////////////////////////////////////////////

//

// main

//

// Where program starts running!

//

void main()

{

// osc control / 8MHz / internal

osccon = 0b01110001;



// timer0... configure source and prescaler

// port A weak pull ups enabled

option_reg = 0b00000011;



// enable pull ups on each button

wpua = 0b00110001;



// turn off the comparator to allow digital IO on CIO pins

cmcon0 = 7;



// set data direction on each pin

trisa = 0b00110101;

trisc = 0b00001110;



// set up the analog input pins

ansel = 0b11100100;



// turn on the ADC

adcon1=0b00100000; //fOSC/32

adcon0=0b10000001; // Right justify / Vdd / AD on



// start up the serial port

init_usart();



// ensure that the initial aquisition is completed

// for all analog inputs that we're using

adcInitComplete = 0;

while(!adcInitComplete)

doADC();





//char buttons = 0;

char debounce = 0;

char lastNote = NO_NOTE;

int lastPitchBend = -1;

int lastModWheel = -1;

int value;

int diff;

for(;;)

{

// the debounce variable makes sure that user

// has release buttons for a period of time before

// a new press on the button can be registered.

// handles possibility of "switch bounce"



// Buttons are pulled up and touch ground when

// pressed, so the pin reads low when the button

// is being pressed

if(debounce > 0)

{

if(P_UP&&P_DN)

--debounce;



}

else

{

if(!P_UP)

{

// octave shift UP

if(baseNote < 103)

baseNote+=12;

debounce = BUTTON_DEBOUNCE;

}

else if(!P_DN)

{

// octave shift DOWN

if(baseNote > 12)

baseNote-=12;

debounce = BUTTON_DEBOUNCE;

}

}



// poll the ADCs

doADC();



// check for a new note being played

char note = getNote(adcResult[ADC_KBD]);

if(note != lastNote)

{

// do we need to kill the previous note?

if(lastNote != NO_NOTE && P_HOLD)

{

// make it so!

startNote(0,lastNote,0);

}

// is a new note playing (rather than stylus

// removed from keyboard?)

if(note != NO_NOTE)

{

// play a note with appropriate velocity

char velocity = (adcResult[ADC_VEL]>>3)&0x7f;

startNote(0,note,velocity);

}

lastNote = note;

}



// check for change in pitchbend which is

// outside the "noise" tolerance

value = adcResult[ADC_PBD];

diff = value - lastPitchBend;

if(diff*diff > (PBD_TOL*PBD_TOL))

{

// Send MIDI pitchbend.. this has a 14-bit

// data value

pitchBend(0, value<<4);

lastPitchBend = value;

}



// check for change in modwheel which is

// outside the "noise" tolerance

value = adcResult[ADC_MOD]>>3;

diff = value - lastModWheel;

if(diff*diff > (MOD_TOL*MOD_TOL))

{

// Send MIDI continuous controller message

// for controller #1 (mod wheel) which has

// 7 bit data value

sendController(0, 1, value);

lastModWheel = value;

}

}

}

Wednesday 10 February 2010

DIY Games Console

Another PIC project... this one using a 14-pin 16F688 and playing a version of "Breakout". I might see if I can get a convincing version of "Space Invaders" to run on that 8x8 matrix too....



Usual setup of 74HC595 shift registers (x3) and ULN2803 NPN transistor arrays (x2). Columns are driven directly thru 100R resistors from one of the 595's... rows alternate red LEDs/green LEDs and are driven via 2 x chained 595's (data out from one goes to data in on the other) which in turn drive the NPN arrays, so only 5 I/O's from the MCU are needed to drive the display..

- Data in for the 595 driving the columns
- Shift clock for the 595 driving the columns
- Data in for the first of chained 595's driving the rows
- Shift clock for the pair of 595's driving the columns
- Store clock line for all 3 x 595's

There are 4 buttons: 3 are connected to PIC I/O for controlling game (only 2 used for Breakout game) and other is MCU reset (grounds MCLR#). 12k pull up resistors on all 4 lines.

The piezo buzzer is connected to the remaining I/O via a 0.1uF capacitor.

The LED matrix was from Sure Electronics (on eBay).. I got 10 of them for about £10. It is red/green but by driving both you get orange.

Here is the PIC code


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

// Config bits
#pragma DATA _CONFIG, _WDT_OFF & _INTRC_OSC_NOCLKOUT 

// Clock freq (for SourceBoost delayt functions)
#pragma CLOCK_FREQ 8000000

typedef unsigned char byte;

// define IO ports
#define P_STORE     porta.2
#define P_DT_ROW    porta.4
#define P_SH_ROW    porta.5
#define P_DT_COL    portc.0
#define P_SH_COL    portc.1
#define P_BUTTON3 portc.5
#define P_SPEAKER portc.3
#define P_BUTTON1 portc.4
#define P_BUTTON2 portc.2

// define IO port direction
#define P_TRISA 0b00000000
#define P_TRISC 0b00110100

// macro defs
#define SET_RED(x,y) disp[y]|=1<<(7-(x))
#define SET_GREEN(x,y) disp[8+(y)]|=1<<(7-(x))
#define SET_ORANGE(x,y) SET_RED(x,y); SET_GREEN(x,y)    
#define BUTTON_DEBOUNCE 2

// info used by the interrupt handler
byte soundPhase = 0;
byte soundPeriod =  100;
byte soundDur = 0;

// display buffer (rows 0-7 for red, 8-15 for green)
byte disp[16];

//////////////////////////////////////////////////////
//
// interrupt handler 
//
// timer0 interrupt is used to drive the piezo speaker
//
void interrupt( void )
{
// check if this is timer0 overflow event
if( intcon.2 )
{
// drive the piezo sounder
soundPhase=!soundPhase;
P_SPEAKER = soundPhase?1:0;

// still sounding?
if(!soundDur)
{
// stop the interrupt.. killing sound
intcon.5 = 0;
}
else
{
// still sounding
--soundDur;
}

// setup the next timer interrupt
tmr0=soundPeriod;

// clear interrupt flag
intcon.2 = 0;

}
}

//////////////////////////////////////////////////////
//
// beep
// 
// start a sound playing
//
void beep(byte pitch, byte dur)
{
soundPeriod = 255-pitch;
soundDur = dur;
intcon.5 = 1;
}

//////////////////////////////////////////////////////
//
// refresh
// 
// update the LED matrix based on content of the 
// disp[] array
//
void refresh()
{
  int i;
  
  // clear vertical shift register and load a logic 1 at 
  // bit position 0. This bit will be shifted along to 
  // drive each row of the LED matrix in turn
  for(i=0;i<16;++i)
  {
    P_SH_ROW = 0;
    P_DT_ROW = (i==15)?1:0;
    P_SH_ROW = 1;
  }
  P_DT_ROW = 0;

  // for each row of data (8 x red, 8 x green)
  for(i=0;i<16;++i)
  {
// this cross reference of vertical bit position to row of the 
// disp[] array is used since the matrix is connected for wiring
// convenience and the order of rows is different
byte ix[16] = { 15, 7, 14, 6, 13, 5, 12, 4, 0, 8, 1, 9, 2, 10, 3, 11 };

// look up the row data byte
    byte d=disp[ix[i]];
    
    // store clock low
    P_STORE = 0;

    // load the 8 bits of data    
    for(int j=0;j<8;++j)
    {
      // shift a column bit
      P_SH_COL = 0;
      P_DT_COL = d&1;
      P_SH_COL = 1;
      d>>=1;
    }
    
    // store clock high.. row data is clocked to 
    // the output of shift registers, simultaneously
    // with the clocking in of a new scan row in the
    // vertical shift registers
    P_STORE = 1;

    // set pins low again and add a "display delay"
    // while the row data is shown, before it is 
    // hidden again
    P_SH_ROW = 0;
    P_DT_COL = 0;
    delay_ms(1);  
    P_SH_ROW = 1;
    P_STORE = 0;
  }
  P_SH_COL = 0;  
  P_SH_ROW = 0;  
}    
   
void breakout()
{
int i;
byte bricks[8];
byte rowsOfBricks=3;
byte speed = 250;
byte lives=3;

// loop for each level
for(;;)
{
// setup the wall
memset(bricks,0,sizeof(bricks));
memset(bricks,255,rowsOfBricks);
    
// init variables
char x=3; // position of bat
char bx=4; // position of ball
char by=6;
char dx=0; // direction of ball
char dy=-1;

// ball movement counter. Set to a value to
// give a short delay at the start of a level
byte bc = 100;  

// counter used to debounce the movement buttons
byte buttonDebounce = 0;

// loop until level is complete
for(;;)
{
// do we need to move the ball?
if(++bc == 0)
{
// reset the counter
bc = speed;

// calc next ball position
char nx = bx + dx;
char ny = by + dy;
if(nx<0||nx>7) // off screen left or right
{
dx=-dx;
nx=bx;
}
if(ny<0||ny>7) // off screen top or bottom
{
dy=-dy;
ny=by;
}
if(ny==7) // on the bottom row?
{
if(bx==x) // flat hit left side
{
if(dx>0) dx=0; else dx=-1;
beep(200,50);
}
else if(bx==x+1) // hit right side
{
if(dx<0) dx=0; else dx=1;
beep(200,50);
}
else if(nx==x) // hit left end
{
dx=-1;
beep(100,50);
}
else if(nx==x+1) // hit right end
{
dx=1;
beep(100,50);
}
else
{
// ball has dropped off bottom of screen
for(i=0;i<3;++i)
{
// death routine
refresh();
beep(50,100);
delay_ms(100);
refresh();
beep(150,100);
delay_ms(100);
}

// lose a life
if(lives-- <= 0)
{
// all lives gone
for(;;)
{
for(i=0;i<50;++i)
refresh();
delay_ms(500);
}
}
else
{
// start of next round
x=3;
bx=4;
by=6;
dx=0;  
bc=100;
}
}

// common stuff
nx=bx;
ny=by;
dy=-1;
}

// move the ball
bx = nx;
by = ny;

// hit a brick?
if(bricks[by]&(1<<(7-bx)))
{
// remove the brick and bounce
bricks[by]&=~(1<<(7-bx));
dy=-dy;
beep(100,200);

// any bricks left?
byte allGone = 1;
for(i=0;i<sizeof(bricks);++i)
{
if(bricks[i])
{
allGone=0;
break;
}
}

// end of level
if(allGone)
{
// beep
for(i=0;i<10;++i)
{
beep(50,50);
delay_ms(100);
}

// add more bricks
if(rowsOfBricks<5)
{
rowsOfBricks++;
}
else
{
// or make it faster
if(speed < 254)
++speed;
}
break;
}
}
}

// prepare screen buffer
memcpy(&disp[0], bricks, sizeof(bricks));
memcpy(&disp[8], bricks, sizeof(bricks));

// show ball
SET_RED(bx,by);

// show bat
SET_GREEN(x,7);
SET_GREEN(x+1,7);

// still in debounce period?
if(buttonDebounce)
{
// wait for button release
if(P_BUTTON1 && P_BUTTON2 && P_BUTTON3)
buttonDebounce--;
}
else 
{
// left?
if(!P_BUTTON1)
{
if(x>0) x--;
buttonDebounce = BUTTON_DEBOUNCE;
}
// right?
else if(!P_BUTTON2)
{
if(x<6) x++;
buttonDebounce = BUTTON_DEBOUNCE;
}
}

// and refresh the display
refresh();
}
}
}

void main()


osccon = 0b01110001; // osc control / 8MHz / internal
cmcon0 = 7; // comparator off
ansel=0; // digital IO

trisa = P_TRISA; // port A I/O direction
trisc = P_TRISC; // port C I/O direction
  
porta = 0; // clear port A
portc = 0; // clear port C

option_reg = 0b10000011; // timer0... configure source and prescaler
intcon.7 = 1; // GIE - enable interrupts
intcon.6 = 1; // PEIE - enable interrupts
intcon.5 = 0; // T0IE - timer 0 interrupts diabled for now
intcon.2 = 0; // T0IF - clear timer 0 interrupt flag

breakout();
}