Last active
October 15, 2018 16:20
-
-
Save AzureDVBB/bdb6c56ae4fa3a1d40497325e2bcfaa5 to your computer and use it in GitHub Desktop.
My 3 team chess clock sketch for the Attiny44
This file contains hidden or 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
#include <PinChangeInterrupt.h> | |
#include <PinChangeInterruptBoards.h> | |
#include <PinChangeInterruptPins.h> | |
#include <PinChangeInterruptSettings.h> | |
/* FOR THE LOVE OF ALL MIGHTY PLEASE USE 1MHz CLOCK!!!!!! | |
* STAY AWAY FROM THE 8MHz INTERNAL CLOCK LIKE THE PLAGUE!!!!! | |
* I MEAN IT!!! THE MILLIS FUNCTION JUST DOES NOT WORK WELL!!!!! | |
* IT IS NOT FASTER BUT SLOWER FOR SOME REASON AND NOT ACCURATE!!!! | |
* SO PLEASE!!!! I BEG OF YOU!!! STAY WITH 1MHz INTERNAL CLOCK!!!! | |
*/ | |
// display shift register pins | |
// positive edge driven | |
#define disp_clk 0 // SRCLK to clock serial data into the shift register | |
#define disp_latch 1 // 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 2 // SER (serial data) pin for theshift register | |
#define key_switch 3 // mode switch between timer setup and countdown modes | |
#define button1 4 // team 1 button | |
#define button2 5 // team 2 button | |
#define button3 6 // team 3 button | |
#define disp_a 7 // bcd decoder pin A LSB | |
#define disp_b 8 // bcd decoder pin B | |
#define disp_c 9 // bcd decoder pin C | |
#define disp_d 10 // bcd decoder pin D MSB | |
// !!!!!!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 | |
// displayed number, MSB first BCD arrays to save computational time | |
bool n0[4] = {false, false, false, false}; // 0000 | |
bool n1[4] = {false, false, false, true}; // 0001 | |
bool n2[4] = {false, false, true, false}; // 0010 | |
bool n3[4] = {false, false, true, true}; // 0011 | |
bool n4[4] = {false, true, false, false}; // 0100 | |
bool n5[4] = {false, true, false, true}; // 0101 | |
bool n6[4] = {false, true, true, false}; // 0110 | |
bool n7[4] = {false, true, true, true}; // 0111 | |
bool n8[4] = {true, false, false, false}; // 1000 | |
bool n9[4] = {true, false, false, true}; // 1001 | |
// main variable for time keeping | |
byte teamTime[3][4] = {{0,0,0,0}, {0,0,0,0}, {0,0,0,0}}; | |
// flag variable to determine which team is controlling the objective | |
volatile byte teamCapture = 0; | |
// variables used in functions | |
unsigned long lastMillis = 0; // constantly changed, storing last time an update happened | |
unsigned long lastSetMillis = 0; // used for setting timer | |
unsigned int decr_update = 1000; // tweaked so that the decrementing happens once a second exactly | |
unsigned int incr_update = 100; // tweaked to provide a servicable update rate | |
int incr = 60; // variable of how many seconds it is incremented by during setup | |
unsigned long tmpMillis = 0; // a variable for delay between quick increments and slow ones | |
void setup() { | |
// set output pins | |
pinMode(disp_ser, OUTPUT); | |
pinMode(disp_clk, OUTPUT); | |
pinMode(disp_a, OUTPUT); | |
pinMode(disp_b, OUTPUT); | |
pinMode(disp_c, OUTPUT); | |
pinMode(disp_d, OUTPUT); | |
// set pins to default value | |
digitalWrite(disp_ser, LOW); | |
digitalWrite(disp_clk, LOW); | |
digitalWrite(disp_a, LOW); | |
digitalWrite(disp_b, LOW); | |
digitalWrite(disp_c, LOW); | |
digitalWrite(disp_d, LOW); | |
// set input pins | |
pinMode(key_switch, INPUT_PULLUP); | |
pinMode(button1, INPUT_PULLUP); | |
pinMode(button2, INPUT_PULLUP); | |
pinMode(button3, INPUT_PULLUP); | |
// attach interrupts to button pins | |
// note on attinyx4 PCINT10 9 8 are disabled with the PCINT library by default | |
attachPCINT(digitalPinToPCINT(button1), team1Capture, CHANGE); | |
attachPCINT(digitalPinToPCINT(button2), team2Capture, CHANGE); | |
attachPCINT(digitalPinToPCINT(button3), team3Capture, CHANGE); | |
} | |
void loop() { | |
dispLoop(teamTime); | |
timerLoop(); | |
} | |
// pulse the shift register to clock data in | |
void Clock(){ | |
digitalWrite(disp_clk, HIGH); | |
//delay(50); | |
digitalWrite(disp_clk, LOW); | |
//delay(100); | |
} | |
// pulse latch pin to set outputs | |
void Latch(){ | |
digitalWrite(disp_latch, HIGH); | |
//delay(50); | |
digitalWrite(disp_latch, LOW); | |
//delay(100); | |
} | |
// shift a single bit of data onto the register | |
void shiftBit(bool data){ | |
// set serial data pin 1 to correct level | |
if (data == true){ | |
digitalWrite(disp_ser, HIGH); // set output | |
Clock(); // clock 1 into register | |
digitalWrite(disp_ser, LOW); // reset output | |
} | |
else if(data == false){ | |
Clock(); // clock 0 into register | |
} | |
// major speed increase can be achieved by keeping output low | |
// in applications where there is rarely a need to clock in 1 to | |
// shift register, saving a digitalWrite call every 0 clock | |
} | |
// set correct display BCD output pin levels | |
void bcdEncode(bool bcd[]){ | |
if(bcd[0] == true){digitalWrite(disp_d, HIGH);} | |
else if(bcd[0] == false){digitalWrite(disp_d, LOW);} | |
if(bcd[1] == true){digitalWrite(disp_c, HIGH);} | |
else if(bcd[1] == false){digitalWrite(disp_c, LOW);} | |
if(bcd[2] == true){digitalWrite(disp_b, HIGH);} | |
else if(bcd[2] == false){digitalWrite(disp_b, LOW);} | |
if(bcd[3] == true){digitalWrite(disp_a, HIGH);} | |
else if(bcd[3] == false){digitalWrite(disp_a, LOW);} | |
} | |
// decide which bcd code to use and set BCD pins | |
void setDisplay(byte num){ | |
if( num == 0){bcdEncode(n0);} | |
else if( num == 1){bcdEncode(n1);} | |
else if( num == 2){bcdEncode(n2);} | |
else if( num == 3){bcdEncode(n3);} | |
else if( num == 4){bcdEncode(n4);} | |
else if( num == 5){bcdEncode(n5);} | |
else if( num == 6){bcdEncode(n6);} | |
else if( num == 7){bcdEncode(n7);} | |
else if( num == 8){bcdEncode(n8);} | |
else{bcdEncode(n9);} | |
} | |
// main display loop, iterating through all displays quickly one at a time | |
// displaying an array of numbers on the display and handling timings | |
void dispLoop(byte countdown[][4]){ | |
for(byte i=0; i<3; i++){ // iterate through rows | |
for(byte j=0; j<4; j++){ // iterate through digits | |
setDisplay(countdown[i][j]); // set correct bcd pins | |
if(i == 0 && j == 0){ shiftBit(true); } // initialize by seeding 1 to shift register | |
else{ shiftBit(false); } // keep clocking 0 to move the active bit | |
Latch(); // change both displayed number and display at once | |
} | |
} | |
Clock(); // clock the last bit off aswell to relieve stress on the last display | |
Latch(); // confirm that change | |
} | |
// quick interrupt functions to update current controlling team on button press | |
void team1Capture(){ teamCapture = 1; } | |
void team2Capture(){ teamCapture = 2; } | |
void team3Capture(){ teamCapture = 3; } | |
// add seconds to the counter for given team | |
void incrementTimer(byte team, int second){ | |
if(team > 0){ | |
team--; // optional, helps with converting team number to list index | |
for(second; second>0; second--){ | |
teamTime[team][3]++; // increment seconds | |
if(teamTime[team][3] == 10){ // handle second overflow | |
teamTime[team][3] = 0; | |
teamTime[team][2]++; | |
} | |
if(teamTime[team][2] == 6){ // handle 10 second overflow | |
teamTime[team][2] = 0; | |
teamTime[team][1]++; | |
} | |
if(teamTime[team][1] == 10){ // handle minute overflow | |
teamTime[team][1] = 0; | |
teamTime[team][0]++; | |
} | |
if(teamTime[team][0] == 10){ // upon overflowing the last digit reset timer to 0 | |
teamTime[team][0] = 0; | |
teamTime[team][1] = 0; | |
teamTime[team][2] = 0; | |
teamTime[team][3] = 0; | |
} | |
} | |
} | |
} | |
// decrement current team time by 1 second | |
void decrementSecond(byte team){ | |
if(team > 0){ | |
team--; // optional, helps with converting team number to list index | |
// check if any team reached 0 second and stop decrementing if so | |
if(((teamTime[0][0]==0) && (teamTime[0][1]==0) && (teamTime[0][2]==0) && (teamTime[0][3]==0)) | |
|| ((teamTime[1][0]==0) && (teamTime[1][1]==0) && (teamTime[1][2]==0) && (teamTime[1][3]==0)) | |
|| ((teamTime[2][0]==0) && (teamTime[2][1]==0) && (teamTime[2][2]==0) && (teamTime[2][3]==0))){ | |
// skip the entire code execution of this function with a jump | |
} | |
else{ | |
teamTime[team][3]--; // decrement second by 1 | |
if(teamTime[team][3] == 255){ // handle second underflow | |
teamTime[team][3] = 9; | |
teamTime[team][2]--; | |
} | |
if(teamTime[team][2] == 255){ // handle 10 second underflow | |
teamTime[team][2] = 5; | |
teamTime[team][1]--; | |
} | |
if(teamTime[team][1] == 255){ // handle minute underflow | |
teamTime[team][1] = 9; | |
teamTime[team][0]--; | |
} | |
} | |
} | |
} | |
// gets the currently pressed down team's button and returns its team number | |
byte buttonHeld(){ | |
if (digitalRead(button1) == LOW) { return 1; } | |
else if (digitalRead(button2) == LOW) { return 2; } | |
else if (digitalRead(button3) == LOW) { return 3; } | |
else { return 0; } | |
} | |
// the main counter update loop for setting and countind down | |
void timerLoop(){ | |
unsigned long currentMillis = millis(); // get current time | |
// update interval 0.5s for the timer setup routine | |
if(currentMillis - lastMillis >= 2) { // wait condition to save on read cycles | |
if(digitalRead(key_switch)==HIGH){ | |
lastMillis = currentMillis; | |
byte currButton = buttonHeld(); // get the number of the currently pressed button | |
if(teamCapture !=0){ // if there was a button pressed outside the update loop | |
if(currButton == 0){ teamCapture = 0; } // reset flag var if there is no button held | |
else{ // otherwise increment timer based on which one it is | |
incrementTimer(currButton, incr); // increment timer for that team | |
teamCapture = 0; // reset flag var | |
lastSetMillis = currentMillis; // update last set var to enable quick incrementing | |
tmpMillis = currentMillis; // also set the temporary millis var to get a delay before quick increment | |
} | |
} | |
// if a key is held down but the flag var is not there, start incrementing based on a delay | |
else if((currentMillis - lastSetMillis >= incr_update) && (currentMillis - tmpMillis >= decr_update)){ | |
lastSetMillis = currentMillis; | |
incrementTimer(currButton, incr); | |
} | |
} | |
} | |
// update routine to increment and decrement timer | |
if(currentMillis - lastMillis >= decr_update) { | |
if(digitalRead(key_switch) == LOW){ | |
lastMillis = currentMillis; | |
decrementSecond(teamCapture); | |
} // if game is running countdown | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment