Skip to content

Instantly share code, notes, and snippets.

@AzureDVBB
Last active October 15, 2018 16:20
Show Gist options
  • Save AzureDVBB/bdb6c56ae4fa3a1d40497325e2bcfaa5 to your computer and use it in GitHub Desktop.
Save AzureDVBB/bdb6c56ae4fa3a1d40497325e2bcfaa5 to your computer and use it in GitHub Desktop.
My 3 team chess clock sketch for the Attiny44
#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