/*
Programme pc_rm1 : régleur de montre - chronocomparateur
Auteur : P. Chour - 2018-12-31 - V1.4
Matériel : carte Arduino pro micro, afficheur I2C 2 lignes 16 caractères, 2 LEDS, une entrée "signal", un contacteur rotatif avec poussoir
Différences entre V1.0 et V1.1
- amélioration de la gestion du bouton poussoir
- correction bug sur signe avance/retard par heure
Différences entre V1.1 et V1.2
- passage en 64 bits
- test durée d'affichage (53ms pour deux lignes). Alternance de l'affichage pour diminuer cette durée.
- amélioration du comptage de la durée à long terme
- Affichage avance-retard sur 24 heures
Différences entre V1.2 et V1.3
- Ajout battement 19800, 25200
Différences entre V1.3 et V1.4 (30/3/2019)
Portage de quelques améliorations de présentation du code de PC-RM3.
Mémorisation de la dernière valeur de battement choisie en EEPROM
Portage et amélioration de l'algorithme de mesure depuis PC-RM3
*/
#include <limits.h>
#include <PinChangeInterrupt.h>
#include <PinChangeInterruptBoards.h>
#include <PinChangeInterruptPins.h>
#include <PinChangeInterruptSettings.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
//#define DEBUG_SERIAL
//#define DEBUG_TIME
//#define DEBUG_AFF
#define DureeSansSignal 50 // Pour la synchro. Délai durant lequel on ne veut pas de signal (en ms).
//
// Constantes, define et variables pour la mesure.
//
long EcartDureeTicTac; // Conservation de la moyenne court terme de la différence entre un TicTac et la période attendue
long NbEcartDureeTicTac;
int64_t EcartDureeTicTacLong;
int64_t NbEcartDureeTicTacLong;
uint64_t DureeLongTerme; // Pour mesure précise des périodes. En µS.
uint64_t NbDureeLongTerme; // Nombre de mesures DureeLongTerme
uint64_t EcartTicTac; // Pour moyenne de la différence de l'écart entre Tic et Tac
uint64_t NbEcartTicTac; // pour calcul de la moyenne : nombre d'EcartTicTac
#define MaxBattements 7 // Nombre de valeurs dans Battements[] (et idem pour Periode, demi periode et Aberrant)
// Les constantes qui suivent dépendent de Battements. Elles sont précalculées pour éviter de perdre du temps lors des traitements.
// Si on souhaite ajouter un nouveau battement, on l'ajoute dans le tableau Battement à la position i
// On calcule la période correspondante que l'on ajoute dans le tableau Periode à la position i
// On calcule la demi période que l'on ajoute dans le tableau DemiPeriode à la position i
// On calcule une valeur de durée de demi période que l'on considère comme aberrante. Pour ma part, c'est en général 1/4 de période. On l'ajoute dans
// le tableau Aberrant à la position i
// On met à jour la valeur MaxBattements qui correspond aux nombres de battements que l'on a défini.
const uint64_t Battements[MaxBattements] = {18000, 19800, 21600, 25200, 28800, 36000, 3600}; // en battements par heures
const uint64_t Periode[MaxBattements] = {400000, 363636, 333333, 285714, 250000, 200000, 1000000}; // periode en µS
const uint64_t DemiPeriode[MaxBattements] = {200000, 181818, 166666, 142857, 125000, 100000, 500000}; // demi période en µS
int IndexBattements = 0; // Valeur du battement courant dans le tableau Battements (et Periode et Aberrant).
const unsigned int AdIndexBattements = 0; // Adresse de la sauvegarde de IndexBattement en EEPROM
// Mise à jours par le programme d'interruption ou le processus de synchronisation
unsigned long TempsTicTacPrec; // Dernière valeur Timer d'un Tic ou d'un Tac mis à jour par le programme d'interruption
unsigned long TempsTicTac; // Temps TicTac courant. TempsTictac-TempsTicTacPrec = durée entre Tic et Tac
unsigned long DiffTicTac; // Durée d'un Tic ou d'un Tac. Mis à jour par routine d'interruption.
unsigned long DiffTicTacPrec; // Valeur précédente de DiffTicTac
bool ITTic = false; // Vrai si un Tic ou un Tac a été détecté (génération interruption). Doit être remis à faux par l'utilisateur
// Pin du processeur et usage
//
#define SignalEntree 9 // PIN Signal d'entrée de la mesure
#define LED_tic 4 // PIN LED rouge (Tic)
#define LED_tac 5 // PIN LED verte (Tac)
#define poussoir 8 // PIN bouton poussoir
#define droite 7 // PIN contact "droite" du contacteur
#define gauche 6 // PIN contact "gauche du contacteur
// Etats de l'automate
//
enum TEtat {E_selection,E_mesure,E_selection_trans,E_mesure_trans,E_pause,E_pause_in_trans,E_pause_out_trans,E_mesure_redemarre_trans,E_selection_change_trans};
enum TEtatSynchro {E_Synchro_En_Cours,E_Synchro_En_Cours_Trans, E_Synchro_Ok, E_Synchro_Ok_Trans};
TEtat EtatAutomate = E_selection_trans; // Etat courant de l'automate
const char Depassement[4] = "***\0";
// gestion du contacteur rotatif
//
#define AntiRebondPoussoir 50 // durée en millisecondes de l'anti rebond
unsigned long TimerPoussoir = 0; // Timer pour antirebond
#define DureeDouble 700 // Durée max d'un double appui en ms
unsigned long TimerDouble = 0; // Timer pour double appui
boolean Gauche = false; // Contacteur va à gauche
boolean Droite = false; // contacteur va à droite
byte GaucheSeq = 0; // séquence courante de lecture du contacteur rotatif
byte DroiteSeq = 0;
boolean GauchePrec = false; // Ancienne valeur du contacteur rotatif
boolean DroitePrec = false;
#define AntiRebondRotation 5 // durée en millisecondes de l'anti rebond
unsigned long TimerRotation = 0; // timer pour anti rebond de la rotation
LiquidCrystal_I2C lcd(0x3f, 16, 2); // Initialisation de l'afficheur : adresse 0x27, 16 caractères, 2 lignes
/*
Initialisation du programme
*/
void setup()
{
int i;
bool Etat = false;
lcd.init(); // Initialisation de l'afficheur
lcd.clear();
lcd.print("Initialisation"); // Un petit texte pour faire patienter
lcd.setCursor(0, 1);
lcd.print("P. Chour V1.4"); // C'est moi, c'est moi, c'est moi !
pinMode(poussoir, INPUT); // La pin où est connecté le bouton poussoir en entrée.
pinMode(droite, INPUT); // La pin où est connecté le contact "droite"
pinMode(gauche, INPUT); // La pin où est connecté le contact "gauche"
digitalWrite(poussoir, HIGH); // active résistance pull up
digitalWrite(droite, HIGH); // active résistance pull up
digitalWrite(gauche, HIGH); // active résistance pull down
pinMode(SignalEntree, INPUT); // La pin où est connecté le signal à mesurer
digitalWrite(SignalEntree, LOW); // active résistance pull up
pinMode(LED_tic, OUTPUT); // La pin où est connecté la LED rouge
pinMode(LED_tac, OUTPUT); // La pin où est connecté la LED verte
for (i = 0; i < 5; i++) {
lcd.noBacklight(); // Allumage rétro éclairage afficheur
FlipLed(&Etat);
delay(200);
lcd.backlight(); // Allumage rétro éclairage afficheur
FlipLed(&Etat);
delay(200);
}
#if defined(DEBUG_SERIAL)
Serial.begin(9600);
#endif
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(SignalEntree), TicTacIT, RISING); // Signal d'entrée sous interruption.
enablePinChangeInterrupt(digitalPinToPinChangeInterrupt(SignalEntree)); // On active l'interruption du signal d'entrée
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(poussoir), PoussoirIT, FALLING); // Signal poussoir sous interruption.
enablePinChangeInterrupt(digitalPinToPinChangeInterrupt(poussoir)); // On active l'interruption du poussoir
sei();
}
/*
Procédure sous interruption
Déclenchée si signal en entrée (tic-tac)
*/
void TicTacIT()
{
if (!ITTic) { // Si IT en cours de traitement, on l'ignore
TempsTicTac = micros(); // Sauve la valeur du compteur courant
ITTic = true;
}
}
/*
Procédure sous interruption
Déclenchée lorsque l'on appuie sur le poussoir
*/
void PoussoirIT()
{
unsigned long courante;
courante = millis();
// valeur courante de l'horloge systeme
if (DureeVrai(TimerPoussoir, courante) >= AntiRebondPoussoir) { // Prend-on en compte l'impulsion (antireboond) ?
TimerPoussoir = courante;
switch (EtatAutomate) {
case E_mesure: // On est dans l'état E_mesure. On passe en pause
EtatAutomate = E_pause_in_trans;
TimerDouble = courante;
break;
case E_pause:
EtatAutomate = E_pause_out_trans;
TimerDouble = courante;
break;
case E_pause_in_trans: // On est en transition vers la pause : si deux appui rapide, on passe en sélection
case E_pause_out_trans:
if (DureeVrai(TimerDouble, courante) <= DureeDouble) {
EtatAutomate = E_selection_trans;
}
break;
case E_selection: // On est dans l'état E_selection. On passe dans l'état E_mesure
EtatAutomate = E_mesure_trans;
break;
default: // On peut être dans l'exécution d'une transition. Dans ce cas, on ne fait rien
break;
}
}
}
//
// La fonction ci-dessous mesure le temps en prenant en compte un éventuel bouclage de la fonction millis ou micro
// On lui donne la valeur de départ de la valeur à mesurer et la valeur courante de millis ou micro. Elle rend la durée écoulée en millisecondes ou microseconde.
// DureeVrai = courante-dernière
//
unsigned long DureeVrai(unsigned long derniere, unsigned long courante) {
if (courante < derniere) { // on est passé par zéro
return (0xffffffffu - derniere) + courante;
} else {
return courante - derniere;
}
}
// La fonction ci-dessous convertit un nombre de 64 bits en string
// En entrée : N = nombre de 64 bits
// En sortie : string contenant le nombre
String Uint64ToString(uint64_t N) {
String S;
if (N <= ULONG_MAX) {
return String((unsigned long)N);
} else {
S = "";
while (N > ULONG_MAX) {
S = String((unsigned long)(N%10))+S;
N = N / 10;
}
if (N > 0) {
S = String(((unsigned long)N)) + S;
}
if (S == "") {S = "0";}
return S;
}
}
/*
* Routine conservée (mais simplifiée) pour ne pas trop différer du logiciel PC-RM3.
* Initialise la fenêtre d'occultation à une valeur fixe d'1/2 de la demi période
* En entrée : index du battement choisi. Pas de contrôle
* En sortie : Fenêtre basse d'occultation et fenêtre haute d'occultation initialisés (en ms)
*/
void InitFenetre(int IndexBattements, unsigned long *FenetreBasseOccultation, unsigned long *FenetreHauteOccultation)
{
*FenetreBasseOccultation = DemiPeriode[IndexBattements]/2000;
*FenetreHauteOccultation = (Periode[IndexBattements]/1000)-*FenetreBasseOccultation;
}
/*
* Fait Basculer les LED en fonction d'un booléen en entrée
* Ce booléen est inversé en sortie
*/
void FlipLed(bool *Etat)
{
if (*Etat) { // Clignotement des LED
digitalWrite(LED_tic, HIGH);
digitalWrite(LED_tac, LOW);
} else {
digitalWrite(LED_tic, LOW);
digitalWrite(LED_tac, HIGH);
}
*Etat = !(*Etat); // Pour affichage alterné des LED
}
//
// La fonction qui suit teste le contacteur rotatif et détermine s'il y a eu
// rotation ou pas. Elle fonctionne par test d'état (donc pas très performante).
// Positionne les variables d'état Gauche, Droite
// La fonction rend vrai si il y a eu rotation et que le sens a pu être déterminé
//
void TestRotation() {
unsigned long courante;
courante = millis(); // valeur courante de l'horloge systeme
// Lit les signaux présents sur les pins droite et gauche du contacteur
boolean GaucheVal = digitalRead(gauche);
boolean DroiteVal = digitalRead(droite);
if ((GauchePrec != GaucheVal) || (DroitePrec != DroiteVal)) { // il y a eu changement d'état
if (DureeVrai(TimerRotation, courante) >= AntiRebondRotation) { // Prend-on en compte l'impulsion (antirebond) ?
TimerRotation = courante;
GaucheSeq <<= 1;
GaucheSeq |= GaucheVal;
GaucheSeq &= 0b00001111;
GauchePrec = GaucheVal;
DroiteSeq <<= 1;
DroiteSeq |= DroiteVal;
DroiteSeq &= 0b00001111;
DroitePrec = DroiteVal;
// Mask the MSB four bits
// Compare the recorded sequence with the expected sequence
if ((GaucheSeq == 0b00001001) && (DroiteSeq == 0b00000011)) {
Gauche = true;
}
if ((GaucheSeq == 0b00000011) && (DroiteSeq == 0b00001001)) {
Droite = true;
}
if (Droite || Gauche) {EtatAutomate = E_selection_change_trans;}
}
}
}
//
// Formatte une valeur pour affichage en microsecondes, millisecondes, secondes ou minutes dans un tableau de char terminé par null
// V1 = valeur en microsecondes
// L = longueur max affichage. Le formatage implique au moins 2 caractères + éventuellement un signe => jusqu'à 3 caractères sont pris. L Max = 19
// Signe = Signe à afficher. Espace => pas de signe. On gagne un caractère sur l'affichage.
// Rend "***" si dépassement capacité ou affichage impossible
// Affichage => unité sur deux caractères + signe sur 1 (+ ou -) ou (pas de signe) caractère + 1 chiffre => L >= 3 si pas de signe et L >= 4 si signe
// Pas de vérification de débordement !!! L'appelant doit faire attention que L et le buffer soient de la bonne taille.
//
void FormatTemps(uint64_t V1, int L, char Signe, char buf[]) {
uint64_t VTempPF;
uint64_t VTemppf;
char bufPF[20];
int iPF, k, i, j;
if (L > 20) {L=19;} // Une précaution !
buf[L] = '\0';
for (i=0; i<L;i++){bufPF[i]=' ';}
VTempPF = V1/60000000;
if (VTempPF > 0) {
buf[L-1] = 'n';
buf[L-2] = 'm';
VTemppf = (V1-(VTempPF*60000000))/1000000;
} else {
VTempPF = V1/1000000;
if (VTempPF > 0) {
VTemppf = V1-(VTempPF*1000000)/1000;
buf[L-1] = 's';
buf[L-2] = ' ';
} else {
VTempPF = V1/1000;
if (VTempPF > 0) {
VTemppf = (V1-VTempPF*1000);
buf[L-1] = 's';
buf[L-2] = 'm';
} else {
VTemppf = V1;
buf[L-1] = 's';
buf[L-2] = 'u';
}
}
}
if (((VTempPF > 10) && ((L-2) <= 1)) || ((VTempPF > 100) && ((L-2) <= 2)) || (VTempPF > 1000)) {
memcpy(buf,Depassement,sizeof(Depassement));
return;
}
iPF = 0;
while (VTemppf > 0) {
bufPF[iPF] = (char)(48+VTemppf-(VTemppf/10)*10); // attention aux optimisations du compilo !
VTemppf = VTemppf/10;
iPF = iPF+1;
}
if (iPF == 0) {
bufPF[0] = '0';
iPF = 1;
}
bufPF[iPF] = '.';
iPF = iPF+1;
if (VTempPF == 0) {
bufPF[iPF] = '0';
iPF = iPF+1;
} else {
while (VTempPF > 0) {
bufPF[iPF] = (char)(48+VTempPF-(VTempPF/10)*10); // attention aux optimisations du compilo !
VTempPF = VTempPF/10;
iPF = iPF+1;
}
}
if (Signe != ' ') {
bufPF[iPF] = Signe;
iPF = iPF+1;
};
i = L-3;
j = iPF-L+2;
if (j < 0) {j = 0;}
k = 0;
for (k=j; k < iPF; k++) {
buf[i] = bufPF[k];
i = i-1;
}
while (i >= 0) {
buf[i] = ' ';
i = i-1;
}
#if defined(DEBUG_AFF)
Serial.println(String((unsigned long)V1) + " / " + buf);
#endif
}
//
// Affichage des mesures
// AAfficher² : 0 = Différence TicTac, 1=Différence période, 2= moyenne période, 3=avance retard par jour
//
void AffMesure(int IndexBattements, byte AAfficher)
{
int i;
uint64_t MoyenneTic;
uint64_t MoyenneTac;
long VTemp;
uint64_t VTempU;
int64_t VTemp64;
char buf[21];
char Signe ;
#if defined(DEBUG_TIME)
// Test durée exécution affichage
unsigned long ENTREE;
ENTREE = millis();
// Fin test durée exécution affichage
#endif
#if defined(DEBUG_AFF)
for (i=0;i<20;i++) {
buf[i]='A';
}
#endif
// Affichage période. On n'affiche que si l'on a au moins deux mesures
switch (AAfficher) {
case 1 : // Moyenne des différences entre Tic et Tac
if (NbEcartTicTac > 0) {
FormatTemps(EcartTicTac/NbEcartTicTac,7,' ',buf);
lcd.setCursor(9,0);
lcd.print(buf);
}
break;
case 0 : // Ecart Tic Tac par rapport à période
if (NbEcartDureeTicTac > 0) {
EcartDureeTicTacLong = EcartDureeTicTacLong+EcartDureeTicTac;
NbEcartDureeTicTacLong = NbEcartDureeTicTacLong+NbEcartDureeTicTac;
if (EcartDureeTicTac > 0) {
VTempU = EcartDureeTicTac/NbEcartDureeTicTac;
Signe = '+';
} else {
VTempU = (-EcartDureeTicTac)/NbEcartDureeTicTac;
Signe = '-';
}
FormatTemps(VTempU,8,Signe,buf);
lcd.setCursor(0,0);
lcd.print(buf);
EcartDureeTicTac = 0;
NbEcartDureeTicTac = 0;
}
break;
case 2 : //Moyenne de la période
if (NbEcartDureeTicTacLong > 0) {
if (EcartDureeTicTacLong > 0) {
VTempU = EcartDureeTicTacLong/NbEcartDureeTicTacLong;
Signe = '+';
} else {
VTempU = (-EcartDureeTicTacLong)/NbEcartDureeTicTacLong;
Signe = '-';
}
FormatTemps(VTempU,8,Signe,buf);
lcd.setCursor(0,1);
lcd.print(buf);
}
break;
case 3 : // Ecart
VTempU = Periode[IndexBattements]*NbDureeLongTerme;
if (VTempU > DureeLongTerme) { // Si durée attendue est inférieure à durée mesurée
VTempU = (VTempU-DureeLongTerme)/NbDureeLongTerme; // La montre avance
Signe = '+';
} else {
VTempU = (DureeLongTerme-VTempU)/NbDureeLongTerme; // Sinon, elle retarde
Signe = '-';
}
FormatTemps(VTempU*Battements[IndexBattements]*(uint64_t)12,7,Signe,buf);
lcd.setCursor(9,1);
lcd.print(buf);
break;
default:;
}
#if defined(DEBUG_TIME)
// Test durée exécution affichage : mesure = 62 ms et 71 ms
Serial.println("Duree affichage (ms) :"+String(millis()-ENTREE));
// Fin Test durée exécution affichage
#endif
}
/**********************************
APPLICATION
**********************************/
void loop()
{
int i;
bool LEDFlip = true; // Pour alterner l'allumage des LED
bool CalculPossible = false;
byte AAfficher = 0; // pour affichage des mesures. Pour des raisons de temps d'affichage, on n'affiche qu'une mesure à la fois
unsigned long AffTimer; // On ne fait un affichage des mesures que toutes les 1000ms (Arduino, c'est lent !)
unsigned long SynchroTimer; // Timer pour la synchronisation et pour la détection d'inactivité
unsigned long FenetreBasseOccultation; // Durée basse à partir de laquelle on accepte une interruption
unsigned long FenetreHauteOccultation; // Durée haute au delà de laquelle on refuse accepte une interruption. On doit resynchroniser la mesure
long TempsTic; // pour affichages, temporaire
long TempsTac; // pour affichages, temporaire
TEtatSynchro EtatSynchro = E_Synchro_En_Cours_Trans; // Etat courant de l'automate de synchronisation
int IndexBattements; // Valeur du battement courant dans le tableau Battements, Periode, demi-periode...
const String NonInitialise = "------ ------";
AffTimer = millis();
EEPROM.get(AdIndexBattements,IndexBattements); // Récupération des valeurs par défaut de IndexBattements et IndexOccultation
if ((IndexBattements < 0) || (IndexBattements >= MaxBattements)) {IndexBattements = 0;}
InitFenetre(IndexBattements,&FenetreBasseOccultation,&FenetreHauteOccultation);
AffTimer = millis();
while (1) {
switch (EtatAutomate) {
//
// Passage en mode sélection, on efface l'écran et on passe en changement de battement
//
case E_selection_trans:
lcd.clear();
lcd.print("Selection");
//
// Transition de changement de battement
//
case E_selection_change_trans:
if (Gauche) {
Gauche=false;
IndexBattements--;
if (IndexBattements < 0) {IndexBattements = MaxBattements-1;}
}
if (Droite) {
Droite=false;
IndexBattements++;
if (IndexBattements >= MaxBattements) {IndexBattements = 0;}
}
lcd.setCursor(0, 1);
lcd.print(Uint64ToString(Battements[IndexBattements]) + " Bat/h ");
InitFenetre(IndexBattements,&FenetreBasseOccultation,&FenetreHauteOccultation);
EtatAutomate = E_selection;
break;
//
// Passage en mode mesure. On affiche les valeurs courantes de la mesure
//
case E_mesure_trans:
lcd.clear();
lcd.print(NonInitialise);
lcd.setCursor(0, 1);
lcd.print(NonInitialise);
EEPROM.put(AdIndexBattements,IndexBattements); // Sauvegarde valeur par défaut du battement en EEPROM. N'écrit que s'il y a eu changement
EcartDureeTicTac = 0;
NbEcartDureeTicTac = 0;
EcartDureeTicTacLong = 0;
NbEcartDureeTicTacLong = 0;
EcartTicTac = 0;
NbEcartTicTac = 0;
DureeLongTerme = 0;
NbDureeLongTerme = 0;
EtatAutomate = E_mesure;
break;
//
// passage en pause
//
case E_pause_in_trans: // on n'était pas dans l'état E_pause
if (DureeVrai(TimerDouble, millis()) > DureeDouble) {
EtatAutomate = E_pause;
lcd.setCursor(9, 1);
lcd.print(" PAUSE ");
}
break;
case E_pause_out_trans: // on était dans l'état E_pause
if (DureeVrai(TimerDouble, millis()) > DureeDouble) {
EtatSynchro = E_Synchro_En_Cours_Trans;
EtatAutomate = E_mesure_redemarre_trans;
lcd.setCursor(9, 1);
lcd.print(" ");
}
break;
//
// En pause, on attend un événement (bouton poussoir)
//
case E_pause:
delay(1);
break;
//
// Si on n'a pas eu de double appui, alors, on repars en mesure
//
case E_mesure_redemarre_trans:
lcd.setCursor(10, 1);
lcd.print(" ");
EtatAutomate = E_mesure;
break;
case E_mesure:
if (CalculPossible) { // ITTic positionné par routine d'interruption
// Pour Calcul de la moyenne de la différence entre un TicTac et la période attendue
EcartDureeTicTac = EcartDureeTicTac+(DiffTicTac+DiffTicTacPrec-Periode[IndexBattements]);
TempsTic = DiffTicTacPrec-DemiPeriode[IndexBattements];
TempsTac = DiffTicTac-DemiPeriode[IndexBattements];
NbEcartDureeTicTac = NbEcartDureeTicTac+1;
// Pour calcul de la moyenne de la différence entre un Tic et un Tac
if (DiffTicTacPrec > DiffTicTac) {EcartTicTac = EcartTicTac+DiffTicTacPrec-DiffTicTac;} else {EcartTicTac = EcartTicTac+DiffTicTac-DiffTicTacPrec;}
NbEcartTicTac = NbEcartTicTac+1;
// Pour calcul de l'avance/retard
if (DureeLongTerme < 0x0FFFFFFFFFFFFFFF) {
DureeLongTerme = DureeLongTerme + DiffTicTac + DiffTicTacPrec;
NbDureeLongTerme = NbDureeLongTerme+1;
}
CalculPossible = false;
} else {
if (DureeVrai(AffTimer, millis()) > 1000) { // on affiche que toutes les 1000 ms
AffTimer = millis();
AffMesure(IndexBattements, AAfficher);
AAfficher = (AAfficher+1) %4; // Et on affiche qu'une valeur à la fois car ça prend du temps ! La valeur à afficher change à chaque appel
}
}
break;
//
// Dans l'état E_selection, on ne fait que choisir le battement de la montre.
// La gestion du contacteur rotatif est fait par test d'état car on n'a que ça à faire.
// On sort de l'état E_selection par appui sur le bouton poussoir du contacteur. Cet appui est
// détecté par interruption
//
case E_selection:
TestRotation();
break;
//
// L'état default correspond à une erreur d'automate (pas d'état connu).
// En cas d'erreur, on retourne dans l'état E_Selection
//
default:
EtatAutomate = E_selection_trans;
lcd.clear();
lcd.print("Erreur 1");
delay(1000);
break;
}
//-------------------------------------------------
// Automate de synchronisation
//-------------------------------------------------
switch (EtatSynchro) {
case E_Synchro_En_Cours_Trans: // Passage en synchronisation. On recherche une période de silence suffisante pour que la prochaine IT soit le début d'un Tic ou d'un Tac
SynchroTimer = millis();
ITTic = false;
CalculPossible = false;
EtatSynchro = E_Synchro_En_Cours;
break;
case E_Synchro_En_Cours: //
if (ITTic) { // Il y a eu une IT en dessous de la période attendue. Pas bon, on recommence à attendre un silence.
FlipLed(&LEDFlip);
SynchroTimer = millis(); // On réinitialise le timer
ITTic = false;
} else {
if (DureeVrai(SynchroTimer,millis()) > DureeSansSignal) { //On n'a pas eu d'IT durant un certain temps.
EtatSynchro = E_Synchro_Ok_Trans; // Donc, on a une période de silence suffisante pour que la prochaine IT soit le début d'un Tic ou d'un Tac.
}
}
break;
case E_Synchro_Ok_Trans: // On attend un début de Tic ou de Tac pour démarrer la mesure.
if (ITTic) {
SynchroTimer = millis(); // On réinitialise le timer
EtatSynchro = E_Synchro_Ok; // Etat synchronisé
TempsTicTacPrec = TempsTicTac;
LEDFlip = false;
ITTic = false;
}
break;
case E_Synchro_Ok:
if (DureeVrai(SynchroTimer,millis()) > FenetreHauteOccultation) { // Si la différence entre le dernier Tic valide et le temps courant > à la fenêtre d'occultation haute, on a un pb. Il faut resynchroniser
EtatSynchro = E_Synchro_En_Cours_Trans;
} else {
if (DureeVrai(SynchroTimer,millis()) > FenetreBasseOccultation) { // Si la différence est supérieure à la fenetre basse d'occultation, on réactive les IT
if (ITTic) {
if (LEDFlip) {
DiffTicTac = DureeVrai(TempsTicTacPrec,TempsTicTac);
CalculPossible = true;
} else {
DiffTicTacPrec = DureeVrai(TempsTicTacPrec,TempsTicTac);
}
TempsTicTacPrec = TempsTicTac;
FlipLed(&LEDFlip);
SynchroTimer = millis();
}
}
}
ITTic = false;
break;
default:
lcd.clear();
lcd.print("Erreur 2");
delay(1000);
EtatSynchro = E_Synchro_En_Cours_Trans;
break;
}
}
}