Skip to content

Instantly share code, notes, and snippets.

@AzureDVBB
Last active May 20, 2019 15:29
Show Gist options
  • Save AzureDVBB/5ef288b0c430cf4093cb4025ff4782e9 to your computer and use it in GitHub Desktop.
Save AzureDVBB/5ef288b0c430cf4093cb4025ff4782e9 to your computer and use it in GitHub Desktop.
3 Team Chess Clock for Airsoft HQ gamemode
// !!!! 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