/* 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; } } }