#include <SPI.h>
#include <JC_Button.h> // https://github.com/JChristensen/JC_Button
const uint8_t button_pin = 9; // arduino pin on which the button is connected
const uint16_t display_refresh_rate = 3; // Hz
const bool common_anode = false; // set to true for common-anode displays
const bool resistors_on_digits = false; // set to true if resistors are on digits, and not on segments (this will change the scan "direction")
Button button( button_pin );
enum : const uint8_t
{
line_current_time,
line_best_time
};
// DISPLAY FUNCTIONS
//
// 7-segments character binary format is 0bHGFEDCBA
//
// A
// F B
// G
// E C
// D H
//
// Example : 0b00111111 = '0', 0b01110111 = 'A'
const uint8_t display_digits = 12;
uint8_t display_cache[ display_digits ];
void displayRefresh()
{
static const uint8_t index_max = resistors_on_digits == false ? display_digits : 8;
static const uint8_t interval = ( 1000 / display_refresh_rate ) / index_max;
static uint32_t timer = -interval;
const uint32_t now = millis();
if ( now - timer >= interval )
{
timer = now;
static uint8_t index = 0;
uint8_t segments;
uint16_t digits;
if ( resistors_on_digits == false )
{
digits = ~( 1 << index ); // select only the digit at the index pos
segments = display_cache[ index ]; // select the segments of this digit
}
else
{
digits = ~0; // no digits selected yet
segments = ( 1 << index ); // select only the segment at the index pos
// for each digit
for ( uint8_t i = 0; i < display_digits; ++i )
{
// if the selected segment is enabled on this digit
if ( display_cache[ i ] & segments )
{
bitClear( digits, i ); // add this digit to the digits selector
}
}
}
if ( common_anode == true )
{
segments = ~segments; // invert segments selector
digits = ~digits; // invert digits selector
}
if ( ++index == index_max )
{
index = 0;
}
digitalWrite( SS, LOW );
SPI.transfer( segments ); // send segments selector to shift register 3
SPI.transfer16( digits ); // send digits selector to shift registers 2 and 1
digitalWrite( SS, HIGH );
}
}
void displaySet( const uint8_t line, const uint8_t id, const uint8_t character, const bool raw = false )
{
uint8_t c = character;
if ( raw == false )
{
if ( c < 10 )
{
static const uint8_t digits[] =
{
0b00111111, // '0'
0b00000110, // '1'
0b01011011, // '2'
0b01001111, // '3'
0b01100110, // '4'
0b01101101, // '5'
0b01111101, // '6'
0b00000111, // '7'
0b01111111, // '8'
0b01101111, // '9'
};
c = digits[ c ];
}
else if ( c == ' ' )
{
c = 0b00000000;
}
else if ( c == '-' )
{
c = 0b01000000;
}
else
{
c = 0b10000000;
}
}
display_cache[ ( line * 6 ) + id ] = c;
}
void displaySet( const uint8_t line, const uint32_t milliseconds )
{
const uint32_t tc = (milliseconds / 10); // convert milliseconds to centiseconds
const uint32_t ts = tc / 100; // convert centiseconds to seconds
const uint8_t c = tc % 100; // calculate centiseconds 0-99
const uint8_t m = ( ts / 60 ) % 100; // calculate minutes 0-99
const uint8_t s = ts % 60; // calculate seconds 0-59
displaySet( line, 0, m / 10 );
displaySet( line, 1, m % 10 );
displaySet( line, 2, s / 10 );
displaySet( line, 3, s % 10 );
displaySet( line, 4, c / 10 );
displaySet( line, 5, c % 10 );
}
void displayFill( const uint8_t line, const uint8_t character, const bool raw = false )
{
for ( uint8_t i = 0; i < 6; ++i )
{
displaySet( line, i, character, raw );
}
}
void displaySave( const uint8_t line, uint8_t backup[6] )
{
memcpy( backup, display_cache + ( line * 6 ), 6 );
}
void displayRestore( const uint8_t line, uint8_t backup[6] )
{
memcpy( display_cache + (line * 6), backup, 6 );
}
void setup()
{
Serial.begin( 115200 );
// initialize displays
SPI.begin();
displayFill( line_current_time, 0 );
displayFill( line_best_time, '-' );
// initialize button
button.begin();
}
void loop()
{
// DECLARATION DES VARIABLES ET CONSTANTES LOCALES
const uint32_t timer = millis() / 1; // temps en millisecondes depuis le départ du programme
static uint32_t timer_chronometre = 0; // milliseconde à laquelle le chronomètre a été lancé
//static uint32_t timer_display = 0; // milliseconde à laquelle l'display a été mis à jour
static uint32_t timer_display2 = 0;
static uint8_t reinitialisation = 0; // état de la réinitialisation
static bool chronometre_actif = false; // état du chronomètre
static bool record_battu = false; // état utilisé lors d'un record battu
static bool clignotement = false; // état du clignotement du message lors d'un record battu
static uint8_t clignotements = 0; // compteur de clignotements du message
static uint32_t timer_clignotement = 0; // milliseconde à laquelle le clignotement du message a commencé
static uint16_t tour_actuel = 1; // le tour actuel
static uint32_t temps_actuel = 0; // temps du tour actuel en millisecondes
static uint32_t temps_record = UINT32_MAX; // temps du meilleur tour en millisecondes
static uint8_t backup[6];
// FIN DECLARATION DES VARIABLES ET CONSTANTES LOCALES
displayRefresh();
// GESTION DU button
button.read(); // lire l'état du button
// si le button est appuyé pendant 3 secondes, réinitialiser le temps précédent et le temps record
if ( reinitialisation == 0 && button.pressedFor( 3000 ) )
{
reinitialisation = 1;
temps_record = UINT32_MAX;
record_battu = false;
clignotement = false;
clignotements = 0;
tour_actuel = 1;
displayFill( line_best_time, '-' );
}
// si le button est appuyé pendant 5 secondes, réinitialiser complètement
else if ( reinitialisation == 1 && button.pressedFor( 5000 ) )
{
reinitialisation = 2;
chronometre_actif = false;
temps_actuel = 0;
displayFill( line_current_time, 0 );
}
// si le button est relâché
else if ( button.wasReleased() )
{
// si c'est après une réinitialisation
if ( reinitialisation > 0 )
{
// réinitialiser l'état de la réinitialisation...
reinitialisation = 0;
}
// sinon
else
{
// démarrer le chronomètre s'il est arrêté (premier appui du button)
if ( chronometre_actif == false )
{
chronometre_actif = true;
}
// sinon
else
{
// si le temps record est battu
if ( temps_record > temps_actuel )
{
// sauvegarder et afficher le temps record
temps_record = temps_actuel; //754567;
displaySet( line_best_time, temps_record );
// si c'est au moins le deuxième tour
if ( tour_actuel > 1 )
{
displaySave( line_best_time, backup );
// réinitialiser les variables pour le clignotement du message
timer_clignotement = 0;
clignotement = false;
clignotements = 0;
record_battu = true;
}
}
// incrémenter les tours
++tour_actuel;
}
// réinitialiser le temps de départ du chronomètre
timer_chronometre = timer;
}
}
// FIN GESTION DU button
// GESTION DES TIMERS
// si le chronomètre est lancé
if ( chronometre_actif == true )
{
// calculer le temps de ce tour depuis le départ du chronomètre, et l'afficher
temps_actuel = timer - timer_chronometre;
if ( timer - timer_display2 >= 250UL )
{
timer_display2 = timer;
//set( line_current_time, temps_actuel );
displaySet( line_current_time, ( temps_actuel / 250 ) * 250 );
}
// si le record a été battu, faire clignoter le temps record toutes les 250 ms
if ( record_battu == true && timer - timer_clignotement >= 333UL )
{
timer_clignotement = timer;
clignotement = !clignotement;
if ( clignotement == true )
{
displayFill( line_best_time, ' ' );
}
else
{
displayRestore( line_best_time, backup );
if ( ++clignotements == 3 )
{
record_battu = false;
}
}
}
}
}