// Author : Quentin BENETHUILLERE // Date of creation : 2021/01/16 // Last modification : 2021/04/27 /* * Program function(s): * This program is a countdown using a four 7-segments digits component (HSN-3642BS). * It displays the time left in the format : "mm.ss". * When the time left is below a certain treshold the display starts flashing. */ //* ----------- //* LIBRAIRIES //* ----------- // N/A // ------------------------- // DECLARATION OF DEFINES // ------------------------- // N/A // ------------------------- // DECLARATION OF VARIABLES // ------------------------- // ------------------------------------------------ // Adjustable variables (program main parameters) // ------------------------------------------------ const byte min_start = 6; // Number of minutes in the countdown at start. RANGE = [0-99]. const byte sec_start = 10; // Number of seconds in the countdown at start. RANGE = [0-60]. const int flashing_period = 500; // Flashing period (ms). const byte min_start_flashing = 5; // Time left in minutes when the digits start flashing. // ------------------------------------------------ // PIN connections // ------------------------------------------------ const byte data = 4; // PIN connected to DS of 74HC595. const byte lock = 5; // PIN connected to ST_CP of 74HC595. const byte clock_BCD = 6; // PIN connected to SH_CP of 74HC595. const byte digit1 = 11; // PIN connected to the 1st digit on the 4 digit component. const byte digit2 = 10; // PIN connected to the 2nd digit input on the 4 digit component. const byte digit3 = 9; // PIN connected to the 3rd digit input on the 4 digit component. const byte digit4 = 8; // PIN connected to the 4th digit input on the 4 digit component. // ------------------------------------------------ // Other variables // ------------------------------------------------ const byte nb_of_digits = 4; // Number of digits used in the display. RANGE = [1-4]. const byte nb_of_characters = 10; // Number of characters possible to display for 1 digit. Ex : 10 in order to display only the digits between 0 and 9. const unsigned int total_time_start = (min_start * 60 + sec_start); // Total time at start. const byte table_digits_134[nb_of_characters] = {B11111100, B01100000, B11011010, B11110010, B01100110, B10110110, B10111110, B11100000, B11111110, B11110110}; // Table indicating for each number (between 0 and 9) the "binary code" (8 bits) permitting to command the 7-segments Digit. As the 4digits component I use is a common anode, the values will have to be inverted. This table is used to display digits 1, 3 and 4 where the dot will NEVER be turned ON. const byte table_digits_2[nb_of_characters] = {B11111101, B01100001, B11011011, B11110011, B01100111, B10110111, B10111111, B11100001, B11111111, B11110111}; // Table indicating for each number (between 0 and 9) the "binary code" (8 bits) permitting to command the 7-segments Digit. As the 4digits component I use is a common anode, the values will have to be inverted. This table is used to display the second digit only where the dot will ALWAYS be turned ON (as a separator). /* Note : As the Most Significant Bit is sent first in this program, here is the mapping to apply between the 4 digits component and the 8 bits shift register : Q7 (1st bit received) connected to PIN "a" of the 7-segments display. Q6 (2nd bit received) connected to PIN "b" of the 7-segments display. Q5 (3rd bit received) connected to PIN "c" of the 7-segments display. ... Q1 (7th bit received) connected to PIN "g" of the 7-segments display. Q0 (8th bit received) connected to PIN "dp" (dot) of the 7-segments display. */ char table_time[nb_of_digits]; // Table to store the remaining time in the countdown digit by digit (first element = minutes tens digit / second element = minutes units digit / third element = seconds tens digit / fourth element = seconds units digit) unsigned long total_time_passed = 0; // Total time since the program has been started. unsigned long previous_time = 0; // Variable to master the flashing delay. byte min_left = min_start; // Number of minutes left in the timer. byte sec_left = sec_start; // Number of seconds left in the timer. boolean display_digit = true; // Variable used for the flashing. // ------------------------- // DECLARATION OF OBJECTS // ------------------------- // N/A // --------------- // INITIALIZATION // --------------- // Setup code. Run once at power up, or each time the RESET button is pressed: void setup() { /* //For debug purposes Serial.begin(9600); */ // ------------------------------------------------ // Input / Output // ------------------------------------------------ pinMode(lock, OUTPUT); pinMode(clock_BCD, OUTPUT); pinMode(data, OUTPUT); pinMode(digit1,OUTPUT); pinMode(digit2,OUTPUT); pinMode(digit3,OUTPUT); pinMode(digit4,OUTPUT); // ------------------------------------------------ // PIN initial states // ------------------------------------------------ digitalWrite(digit1, LOW); digitalWrite(digit2, LOW); digitalWrite(digit3, LOW); digitalWrite(digit4, LOW); } // end of setup() function // ------------------------- // MAIN // ------------------------- // Loop function that runs repeatedly: void loop() // Updating the countdown as long as time is not expired. { // Calculating the remaining time to be displayed in the timer and splitting it digit by digit // Note: it would have been nice to perform those actions in a separate function but I don't know yet how to return an array from a function: total_time_passed = millis()/1000; if (total_time_passed < total_time_start) { min_left = (total_time_start - total_time_passed) / 60; sec_left = (total_time_start - total_time_passed) % 60; table_time[0] = min_left / 10; table_time[1] = min_left % 10; table_time[2] = sec_left / 10; table_time[3] = sec_left % 10; } // end if else { for (byte x=0; x < nb_of_digits; x++) { table_time[x] = 0; } // end for } // end else // Determining whether the digits must be displayed or not (from a flashing perspective) : if (millis() - previous_time > flashing_period && min_left < min_start_flashing) { display_digit=!display_digit; previous_time = millis(); } // end if // When we are in the period where the digits must be displayed (otherwise do nothing) : if (display_digit==true) { // Updating the digits 1 by 1 : for (char j = 0; j < nb_of_digits; j++) { // Selecting the digit to be updated select_digit(j); update_digit(table_time[j],j); clean_digit(); } // end for } // end if } // end of loop() function void select_digit(byte digit_nb) { switch(digit_nb) { case 0 : digitalWrite(digit2, LOW); digitalWrite(digit3, LOW); digitalWrite(digit4, LOW); digitalWrite(digit1, HIGH); break; case 1 : digitalWrite(digit1, LOW); digitalWrite(digit3, LOW); digitalWrite(digit4, LOW); digitalWrite(digit2, HIGH); break; case 2 : digitalWrite(digit1, LOW); digitalWrite(digit2, LOW); digitalWrite(digit4, LOW); digitalWrite(digit3, HIGH); break; case 3 : digitalWrite(digit1, LOW); digitalWrite(digit2, LOW); digitalWrite(digit3, LOW); digitalWrite(digit4, HIGH); break; } // end switch } // end of "select_digit" function void update_digit(byte digit, byte digit_nb) { // Activating the lock while the bits are transferred one by one digitalWrite(lock, LOW); // Sending all bits through shiftOut : /* IMPORTANT : The first bit sent (whatever LSB or MSB is chosen) corresponds to Q7, 2nd bit sent corresponds to Q6. MSB -> Most Significant Bit is sent first. WARNING : Need to use "~" before the byte value in order to control the LEDs at LOW voltage level. */ if (digit_nb ==1) // If the digit to be updated is the second one, then the dot must ALWAYS be displayed as a separator, otherwise the dot must NEVER be displayed. { shiftOut(data, clock_BCD, MSBFIRST, ~(table_digits_2[digit])); // Note the bits inversion with "~" as a common anode display is used. } // end if else { shiftOut(data, clock_BCD, MSBFIRST, ~(table_digits_134[digit])); // Note the byte inversion with "~" as a common anode display is used. } // end else // Releasing the lock so that the digit is updated digitalWrite(lock, HIGH); } // end of "update_digit" function void clean_digit() /* * This function permits to turn OFF every segment of a digit in order to avoid overlapping when updating the digits */ { // Activating the lock while the bits are transferred one by one digitalWrite(lock, LOW); // Sending all bits through shiftOut shiftOut(data, clock_BCD, MSBFIRST, B11111111); // Releasing the lock so that the digit is updated digitalWrite(lock, HIGH); } // end of "clean_digit" function