// Skeeball controller program // Jason Greene // 6/13/2009 // www.idleamusements.com #include #include #include #include #include #include #include #include void Init(void); void SetTune(int); void NextNote(void); void SetDisplay(int); void DisplayRender(void); void SevenSend(char); void SetLights(int); void LightEffects(void); //Global Variables //These variables represent the status of the game and outputs volatile unsigned char GameState; volatile unsigned int TheScore; volatile unsigned char TheBallCount; volatile unsigned char BallInPlayState; volatile unsigned char SolenoidState; volatile unsigned char LightState; volatile unsigned char LightSequence; volatile unsigned char DisplayState; volatile unsigned char DisplaySequence; //Music variables volatile float MusicStep; volatile float MusicMover; volatile unsigned int MusicDelay; volatile int MusicState; volatile int MusicPosition; //RTTTL Info represents the stored tunes converted from RTTTL format volatile int RTTTLCount; volatile int RTTTLBPM; volatile unsigned char RTTTLNote[80]; volatile unsigned char RTTTLDuration[80]; //Switch counters to track button debounce volatile unsigned int SwitchCounter[3]; //Countdown timers to generate events volatile unsigned int SolenoidTimer; volatile unsigned int LightTimer; volatile unsigned int DisplayTimer; volatile unsigned int EndGameTimer; //Flags to trigger update events volatile unsigned char NoteUpdateFlag; volatile unsigned char DisplayUpdateFlag; volatile unsigned char ScoreUpdateFlag; volatile unsigned char BallUpdateFlag; volatile unsigned char SolenoidUpdateFlag; volatile unsigned char LightUpdateFlag; volatile unsigned char EndGameFlag; // SineTable unsigned char SineTable[256] = {133,136,139,142,145,149,152,155,158,161,164,167,169,172,175,178, 181,184,186,189,192,194,197,200,202,205,207,209,212,214,216,218,220,222,224,226, 228,230,232,233,235,237,238,240,241,242,243,245,246,247,248,248,249,250,251,251, 252,252,252,253,253,253,253,253,253,253,252,252,252,251,251,250,249,248,248,247, 246,245,243,242,241,240,238,237,235,233,232,230,228,226,224,222,220,218,216,214, 212,209,207,205,202,200,197,194,192,189,186,184,181,178,175,172, 169,167,164,161,158,155,152,149,145,142,139,136,133,130,127,124, 121,118,115,112,109,105,102,99,96,93,90,87,85,82,79,76, 73,70,68,65,62,60,57,54,52,49,47,45,42,40,38,36,34,32,30,28, 26,24,22,21,19,17,16,14,13,12,11,9,8,7,6,6,5,4,3,3, 2,2,2,1,1,1,1,1,1,1,2,2,2,3,3,4,5,6,6,7,8,9,11,12, 13,14,16,17,19,21,22,24,26,28,30,32,34,36,38,40,42,45,47,49, 52,54,57,60,62,65,68,70,73,76,79,82,85,87,90,93,96,99,102,105, 109,112,115,118,121,124,127,127}; int main (void) { Init(); sei(); //Setup Attract Mode SetDisplay(1); SetLights(1); SetTune(1); /* Welcome to the infinite loop where we look for flags generated from switch or timer events. This represents to logical flow of the game. */ while(1) { //Score change if(ScoreUpdateFlag==1) { TheScore=TheScore+10; //A score of 150 is special and makes you a "Winner" //so we play a special tune and update the Winner light on the display if(TheScore==150) { SetTune(3); LightUpdateFlag=1; } ScoreUpdateFlag=0; DisplayUpdateFlag=1; BallInPlayState++; if(BallInPlayState==1) { SetTune(2); } } //Ball played if(BallUpdateFlag==1) { TheBallCount=TheBallCount+1; BallUpdateFlag=0; DisplayUpdateFlag=1; //Last Ball if(TheBallCount==9) { EndGameTimer=31250; } else { //Ball out with no score //Play some sad little tune if(BallInPlayState==0) { SetTune(4); } } BallInPlayState=0; } //Look up next note if(NoteUpdateFlag==1) { NextNote(); } //Solenoid Events if(SolenoidUpdateFlag==1) { SolenoidUpdateFlag=0; SolenoidState--; //Time to turn off if(SolenoidState==0) { PORTC &= ~(1<=0;x--) { if(item & (0x01<149) { PORTB |= 1<>4; octave=RTTTLNote[MusicPosition] & 0x0f; NoteFrequency =0; switch (note) { case 1: NoteFrequency = 55 * (1 << (octave - 1)); break; case 2: NoteFrequency = 58.27 * (1 << (octave - 1)); break; case 3: NoteFrequency = 61.735 * (1 << (octave - 1)); break; case 4: NoteFrequency = 65.406 * (1 << (octave - 1)); break; case 5: NoteFrequency = 69.296 * (1 << (octave - 1)); break; case 6: NoteFrequency = 73.416 * (1 << (octave - 1)); break; case 7: NoteFrequency = 77.782 * (1 << (octave - 1)); break; case 8: NoteFrequency = 82.407 * (1 << (octave - 1)); break; case 9: NoteFrequency = 87.307 * (1 << (octave - 1)); break; case 10: NoteFrequency = 92.499 * (1 << (octave - 1)); break; case 11: NoteFrequency = 97.999 * (1 << (octave - 1)); break; case 12: NoteFrequency = 103.826 * (1 << (octave - 1)); break; case 0: NoteFrequency = 0; break; } MusicStep=(float)((float)256/(float)(31250/NoteFrequency)); MusicMover=MusicStep; duration=4; switch (RTTTLDuration[MusicPosition] & 0x0f) { case 0: duration=1; break; case 1: duration=2; break; case 2: duration=4; break; case 3: duration=8; break; case 4: duration=16; break; case 5: duration=32; break; } //Check for duration modifier if((RTTTLDuration[MusicPosition] & 0xf0) > 0) { duration = duration * 1.5; } MusicDelay= (float)((float)(60 /(float)RTTTLBPM) * (float)(4 /(float)duration) * (float)31250); MusicPosition=MusicPosition+1; } NoteUpdateFlag=0; } /* ISR catches the timer interrupts and handles all the important stuff. If in a tune we play the next sample of the sine wave. we then check our switches (using a counter to handle debounce) to drive the logical state We decrement any running timers and flag where needed events for lights, solenoid, etc. */ ISR(TIMER1_COMPA_vect) { //Music : if in a tune play the next step in the sine wave and decrement the delay if(MusicState==1) { PORTD = SineTable[(int)MusicMover]; MusicMover=MusicMover+MusicStep; if(MusicMover>255) { MusicMover=MusicMover-256; } if(MusicDelay>0) { MusicDelay=MusicDelay-1; } else { PORTD=0; NoteUpdateFlag=1; } } //Check inputs PC0=Score, PC1 = ball counter, PC2 = start // GameState = 0 means not in a game if(GameState==0) { //Start game switch if(PINC&(1<15000) SwitchCounter[2]=100; } else { SwitchCounter[2]=0; } } // Game in play if(GameState==1) { //Scores if(PINC&(1<15000) SwitchCounter[0]=100; } else { SwitchCounter[0]=0; } //Ball Out if(PINC&(1<15000) SwitchCounter[1]=100; } else { SwitchCounter[1]=0; } } //Now we decrement any timers and generate update flags if necessary if(SolenoidTimer>0) { SolenoidTimer--; if(SolenoidTimer==0) { SolenoidUpdateFlag=1; } } if(LightTimer>0) { LightTimer--; if(LightTimer==0) { LightUpdateFlag=1; } } if(DisplayTimer>0) { DisplayTimer--; if(DisplayTimer==0) { DisplayUpdateFlag=1; } } if(EndGameTimer>0) { EndGameTimer--; if(EndGameTimer==0) { EndGameFlag=1; } } } /* Init handles port and timer setup as well as initial global values */ void Init() { // Sound Outout on Port D DDRD = _BV(PD0) | _BV(PD1) | _BV(PD2)| _BV(PD3) | _BV(PD4)| _BV(PD5) | _BV(PD6)| _BV(PD7); PORTD=0; // Outputs for score display P5 = clock P4 = Data // Outputs for Lamps P1=game over P2=Winner P3=Free Play DDRB = _BV(PB1) | _BV(PB2) | _BV(PB3) | _BV(PB4) | _BV(PB5); PORTB = 0; // Inputs PC0 = Score, PC1=ball counter, PC2 = Start switch //Output for top light and ball solenoid PC4 & PC3 DDRC=0; DDRC = _BV(PC3) | _BV(PC4) ; PORTC= (1<