Last active
May 20, 2019 15:29
-
-
Save AzureDVBB/5ef288b0c430cf4093cb4025ff4782e9 to your computer and use it in GitHub Desktop.
3 Team Chess Clock for Airsoft HQ gamemode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// !!!! the following code is for the ATMEL ATTINY44 microcontroller !!!! | |
// display shift register pins | |
// positive edge driven | |
#define disp_clk 10// SRCLK to clock serial data into the shift register | |
#define disp_ser_latch 9 // latch pin to push changes in shift register and decoder to output | |
// the RCLK is directly connected while the LT is fed through an inverter | |
#define disp_ser 8 // SER (serial data) pin for theshift register | |
// pins for interrupt on button presses | |
#define button1 0 // Button for team 1 | |
#define button2 1 // Button for team 2 | |
#define button3 2 // Button for team 3 | |
// mode switches | |
#define arming 3 // switch for arming and disarming timer | |
// blinking column pins | |
#define blink1 6 // blinking column for team 1 | |
#define blink2 5 // blinking column for team 2 | |
#define blink3 4 // blinking column for team 3 | |
// !!!!!!ULTRAMEGASUPER WARNING!!!!!!!!! | |
// if you want to pass arrays to functions | |
// !!!!DO NOT MAKE IT A CONSTANT!!!!!!!! | |
// it will run and compile, but it WILL NOT run properly, trust me | |
// lookup table array for sending correct half-bytes to the display driver | |
// array index is the number sent, element value is the half-byte to send | |
// the array index 10 is for writing a blank | |
byte dispMap[11] ={0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 15}; | |
// variables keeping track of the team time per digit | |
// M M : S S | |
byte team1[4] = {0, 0, 0, 0}; | |
byte team2[4] = {0, 0, 0, 0}; | |
byte team3[4] = {0, 0, 0, 0}; | |
byte team3Reversed[4] = {1, 2, 3, 4}; // needed to fix shitey wiring, use its function | |
// array holding column select data | |
// wiring dependant which order they select in | |
byte column[4] = {8, 4, 2, 1}; | |
//byte column[4] = {1, 2, 4, 8}; | |
// correlation array, maps the sent half-byte to display driver inputs | |
byte driverMap[4] = {3, 2, 1, 4}; | |
// globals | |
byte tickrate = 30; // how many times a second to poll input states of buttons | |
int second_adjust = (8136*2) - 1036; // 1Hz interrupt counter comparison register | |
byte blankRow[4] = {10, 10, 10, 10}; // use this array to blank out all display segments | |
// cached computation globals | |
int tick = second_adjust/tickrate; | |
// flags | |
bool blinkCol1 = false; // flag storing wether seconds colon for team 1 is on or off | |
bool blinkCol2 = false; // flag storing wether seconds colon for team 2 is on or off | |
bool blinkCol3 = false; // flag storing wether seconds colon for team 3 is on or off | |
bool blanked = false; // flag for storing wether a display row is blanked or not | |
// interrupt used globals | |
volatile byte winning_team = 0; // which team is currently winning (counting down) | |
// interrupt used flags | |
volatile bool winner = false; // flag showing if anyone won and game over | |
volatile bool armed = false; // armed true = game started, armed false = time setup | |
volatile bool pressed_button = false; // a button got pressed flag | |
volatile bool state_button1 = false; // button1 state flag (LOW = false) | |
volatile bool state_button2 = false; // button2 state flag (LOW = false) | |
volatile bool state_button3 = false; // button3 state flag (LOW = false) | |
void setup() { | |
noInterrupts(); | |
// set output pins | |
pinMode(disp_clk, OUTPUT); | |
pinMode(disp_ser, OUTPUT); | |
pinMode(disp_ser_latch, OUTPUT); | |
pinMode(blink1, OUTPUT); | |
pinMode(blink2, OUTPUT); | |
pinMode(blink3, OUTPUT); | |
// set input pins | |
pinMode(button1, INPUT); | |
pinMode(button2, INPUT); | |
pinMode(button3, INPUT); | |
pinMode(arming, INPUT); | |
// set default outputs | |
digitalWrite(disp_clk, LOW); | |
digitalWrite(disp_ser, LOW); | |
digitalWrite(disp_ser_latch, LOW); | |
digitalWrite(arming, LOW); | |
digitalWrite(blink1, LOW); | |
digitalWrite(blink2, LOW); | |
digitalWrite(blink3, LOW); | |
// initialize pin change interrupt handler vectors | |
PCMSK0 = 0; // set entire pinchange enable register to 0 (disables PCINT0-PCINT7) | |
PCMSK1 = 0; // set entire pinchange enable register to 0 (disables PCINT11-PCINT8) | |
// NOTE: there is only two interrupt handler vectors (ISR) on attiny44 | |
// PCINT0_vect for PCIE0 and PCINT1_vect for PCIE1 | |
bitSet(GIMSK, PCIE0); // enable PCMSK0 interrupts (PCINT0_vect handler) | |
bitSet(PCMSK0, PCINT0); // enable PCINT0 pin change interrupt (button1) | |
bitSet(PCMSK0, PCINT1); // enable PCINT1 pin change interrupt (button2) | |
bitSet(PCMSK0, PCINT2); // enable PCINT2 pin change interrupt (button3) | |
// initialize Timer1(16bit) | |
TCCR1A = 0; // clear register A | |
TCCR1B = 0; // clear register B | |
bitSet(TIMSK1, OCIE1A); // enable Timer1 compare A interrupt | |
bitSet(TIMSK1, OCIE1B); // enable Timer1 compare B interrupt | |
OCR1A = second_adjust; // set compare A register (adjust untill it triggers at 1Hz) | |
OCR1B = tick; // set compare B register (use this for other input polling) | |
//set Timer1 prescaler (64) | |
bitSet(TCCR1B, CS10); | |
bitSet(TCCR1B, CS11); | |
interrupts(); // allow interrupts | |
} | |
void loop() { | |
noInterrupts(); | |
// timing critical functions | |
if(!winner){dispLoop();} // main time display loop | |
else if (winner){ // time display loop with a row bilnking | |
if(blanked){dispLoopWon();} // blanked a row | |
else{dispLoop();} // normal display | |
} | |
interrupts(); | |
// other code | |
} | |
// interrupt service routines | |
// pin change interrupts | |
ISR(PCINT0_vect){pressed_button = true;} // register button press | |
// timer interrupts | |
ISR(TIM1_COMPA_vect){ // timer1 1Hz interrupt signal (compare A) | |
TCNT1 = 0; // reset timer count value !!!IMPORTANT CODE!!! | |
armed = digitalRead(arming); // check arming switch status | |
// timer1 compare B register setting !!!IMPORTANT CODE!!! | |
// we can use if statements to set the scan rate of the code on the interrupt | |
OCR1B = tick; // game is running, use tickrate for responsiveness | |
if(!winner && armed){ // game is running and no winner | |
timeDecr(winning_team); // count down for winning team | |
blinkSecondsColon(winning_team); // blinking the seconds column at 2Hz for winning team | |
} | |
else if (!armed){ // disarmed, in setup mode | |
winner = false; | |
winning_team = 0; | |
} | |
else if (winner && armed){ | |
// start blinking the entire row | |
if(!blanked){blanked = true;} | |
else{blanked = false;} | |
blinkSecondsColon(winning_team); | |
} | |
} | |
ISR(TIM1_COMPB_vect){ // timer1 interrupt signal (compare B) use for polling inputs | |
OCR1B += tick; // increment comparison register | |
if(armed && pressed_button && !winner){ // game is going and a button press registered | |
// Read each button input | |
// Invert it if buttons are normally pulled HIGH | |
state_button1 = digitalRead(button1); | |
state_button2 = digitalRead(button2); | |
state_button3 = digitalRead(button3); | |
// handle button states | |
if(state_button1 && !state_button2 && !state_button3){ // if only button1 pressed | |
winning_team = 1; // set team 1 for countdown | |
} | |
else if(!state_button1 && state_button2 && !state_button3){ // if only button1 pressed | |
winning_team = 2; // set team 2 for countdown | |
} | |
else if(!state_button1 && !state_button2 && state_button3){ // if only button1 pressed | |
winning_team = 3; // set team 3 for countdown | |
} | |
else if((state_button1 && state_button2) || (state_button2 && state_button3) || | |
(state_button3 && state_button1)){ // if there is 2 buttons pressed down at once | |
winning_team = 0; // pause countdown | |
} | |
pressed_button = false; // reset button press flag | |
} | |
else if(!armed && pressed_button){ // game is in setup mode and a button press is registered | |
// Read each button input | |
state_button1 = digitalRead(button1); | |
state_button2 = digitalRead(button2); | |
state_button3 = digitalRead(button3); | |
// handle button states | |
if(state_button1 && !state_button2 && !state_button3){ // if only button1 pressed | |
timeIncr(1); // increment team1 timer | |
} | |
else if(!state_button1 && state_button2 && !state_button3){ // if only button1 pressed | |
timeIncr(2); // increment team2 timer | |
} | |
else if(!state_button1 && !state_button2 && state_button3){ // if only button1 pressed | |
timeIncr(3); // increment team3 timer | |
} | |
pressed_button = false; // reset button press flag | |
} | |
} | |
// function definitions | |
void shiftClock(){ | |
// pulses the clock pin to shift in data | |
digitalWrite(disp_clk, HIGH); | |
digitalWrite(disp_clk, LOW); | |
} | |
void latchClock(){ | |
// pulses latch pin to change outputs | |
digitalWrite(disp_ser_latch, HIGH); | |
digitalWrite(disp_ser_latch, LOW); | |
} | |
void shiftByte(byte data){ | |
// pushes bits in to the register | |
// depending on wiring its either LSB or MSB first | |
shiftOut(disp_ser, disp_clk, LSBFIRST, data); | |
} | |
void reverseTeam3(){ | |
// called every time team3 reverse is needed and updates it | |
team3Reversed[0]=team3[3]; | |
team3Reversed[1]=team3[2]; | |
team3Reversed[2]=team3[1]; | |
team3Reversed[3]=team3[0]; | |
} | |
void dispCol(byte col_num){ | |
// fix for buggy wiring on my end | |
reverseTeam3(); // updated reversed list | |
// displays the specified row with values | |
shiftByte((dispMap[team3Reversed[col_num]] << 4)+ column[col_num]); // 4bit team3 + 4bit column select | |
shiftByte((dispMap[team1[col_num]] << 4) + dispMap[team2[col_num]]); // 4bit team1 + 4bit team2 | |
latchClock(); | |
} | |
void dispLoop(){ | |
// iterates through column displays | |
for (int i=0; i<4; i++){ | |
dispCol(i); | |
//delay(500); | |
} | |
} | |
void dispColWon(byte col_num){ | |
// similar to 'dispCol()' function just blanks out the winning team | |
switch (winning_team){ | |
case 1: // team 1 won, blank out their display | |
shiftByte((dispMap[team3Reversed[col_num]] << 4)+ column[col_num]); | |
shiftByte((dispMap[blankRow[col_num]] << 4) + dispMap[team2[col_num]]); | |
latchClock(); | |
break; | |
case 2: | |
shiftByte((dispMap[team3Reversed[col_num]] << 4)+ column[col_num]); | |
shiftByte((dispMap[team1[col_num]] << 4) + dispMap[blankRow[col_num]]); | |
latchClock(); | |
break; | |
case 3: | |
shiftByte((dispMap[blankRow[col_num]] << 4)+ column[col_num]); | |
shiftByte((dispMap[team1[col_num]] << 4) + dispMap[team2[col_num]]); | |
latchClock(); | |
break; | |
} | |
} | |
void dispLoopWon(){ | |
// iterates through column displays | |
for (int i=0; i<4; i++){ | |
dispColWon(i); | |
//delay(500); | |
} | |
} | |
void resolveTimeUp(byte teamtime[]){ | |
// fix incorrect values when incrementing | |
if(teamtime[3] >= 10){ // 1s digit, goes up to 9 | |
teamtime[2] += 1; // add 1 to 10s digit | |
teamtime[3] -= 10; // subtract 10 from 1s digit | |
} | |
if(teamtime[2] >= 6){ // 10s digit, goes up to 5 | |
teamtime[1] += 1; // add 1 to the 1m digit | |
teamtime[2] -= 6; // subtract 6 from 10s digit | |
} | |
if(teamtime[1] >= 10){ // 1m digit, goes up to 9 | |
teamtime[0] += 1; // add 1 to the 10m digit | |
teamtime[1] -= 10; // subtract 10 from 1m digit | |
} | |
if(teamtime[0] >= 10){ // 10m digit goes up to 9 | |
// if it goes over 9 then overflow happened, reset everything to 0 in teamtime | |
for (int i=0; i<4; i++){teamtime[i] = 0;} | |
} | |
} | |
void timeIncr(byte team){ | |
// increment team's time by 5m | |
switch (team){ // select correct team | |
case 1: | |
team1[1] += 5; // add 5m | |
resolveTimeUp(team1); // resolve any time issues | |
break; | |
case 2: | |
team2[1] += 5; // add 5m | |
resolveTimeUp(team2); // resolve any time issues | |
break; | |
case 3: | |
team3[1] += 5; // add 5m | |
resolveTimeUp(team3); // resolve any time issues | |
break; | |
} | |
} | |
void resolveTimeDown(byte teamtime[]){ | |
// counts down by 1 resolving time issues | |
if (teamtime[3] > 0){teamtime[3] -= 1;} // decrement 1s digit if > 0 | |
else{ // if 1s digit is 0 | |
if (teamtime[2] > 0){teamtime[2] -= 1;} // decrement 10s digit if > 0 | |
else{ // if 10s digit is 0 | |
if (teamtime[1] > 0){teamtime[1] -= 1;} // decrement 1m digit if > 0 | |
else{ // if 1m digit is 0 | |
if (teamtime[0] > 0){teamtime[0] -= 1;} // decrement 10m digit if > 0 | |
else{goto outside;} // break out of the nesting | |
teamtime[1] = 9; // set 1m digit to 9 | |
} | |
teamtime[2] = 5; // set 10s digit to 5 | |
} | |
teamtime[3] = 9; // set 1s digit to 9 | |
} | |
outside: | |
if (teamtime[0] == 0 && teamtime[1] == 0 && teamtime[2] == 0 && teamtime[3] == 0){ | |
// stop counting, a team has won, set a flag for it | |
winner = true; | |
} | |
} | |
void timeDecr(byte team){ | |
// pass proper team to decrementing function | |
switch(team){ | |
case 1: | |
resolveTimeDown(team1); // send team 1 ref | |
break; | |
case 2: | |
resolveTimeDown(team2); // send team 2 ref | |
break; | |
case 3: | |
resolveTimeDown(team3); // send team 3 ref | |
break; | |
} | |
} | |
void blinkSecondsColon(byte team){ | |
// blinks the currently winning teams seconds colon | |
switch (team){ // select correct team | |
case 1: | |
if(!blinkCol1){digitalWrite(blink1, HIGH); blinkCol1 = true;} //turn second colon on if it wasnt | |
else{digitalWrite(blink1, LOW); blinkCol1 = false;} //turn second colon off if it was | |
if(blinkCol2){digitalWrite(blink2, LOW); blinkCol2 = false;} //turn second colon off | |
if(blinkCol3){digitalWrite(blink3, LOW); blinkCol3 = false;} //turn second colon off | |
break; | |
case 2: | |
if(!blinkCol2){digitalWrite(blink2, HIGH); blinkCol2 = true;} | |
else{digitalWrite(blink2, LOW); blinkCol2 = false;} | |
if(blinkCol1){digitalWrite(blink1, LOW); blinkCol1 = false;} | |
if(blinkCol3){digitalWrite(blink3, LOW); blinkCol3 = false;} | |
break; | |
case 3: | |
if(!blinkCol3){digitalWrite(blink3, HIGH); blinkCol3 = true;} | |
else{digitalWrite(blink3, LOW); blinkCol3 = false;} | |
if(blinkCol1){digitalWrite(blink1, LOW); blinkCol1 = false;} | |
if(blinkCol2){digitalWrite(blink2, LOW); blinkCol2 = false;} | |
break; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment