Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #21
    Membre à l'essai
    Décodage code hoaraire 162kh
    Bonjour à vous... Je suis sagement à la maison lol,

    J'essaie en me servant de ton programme, de le réécrire sans interruption, avec des millis() , delay et digitalRead (c'est pour apprendre).
    Il me semble que cela devrait être aussi fiable qu'avec des interruptions...? on mesure des temps de plusieurs dizaines de millisecondes (on est au ralenti).

    Pour l'instant je bute sur les drapeaux et les millis() qui ne se laissent pas facilement manipuler dans des "conditions IF".


    Une aide sera sans doute la bienvenue , sinon, Leclerc vend des horloges pilotées par dcf77 .

    Bonne soirée

    pat42

  2. #22
    Membre éprouvé
    Bonsoir Pat
    Citation Envoyé par Pat42 Voir le message
    J'essaie en me servant de ton programme, de le réécrire sans interruption, avec des millis() , delay et digitalRead (c'est pour apprendre).
    Il me semble que cela devrait être aussi fiable qu'avec des interruptions...? on mesure des temps de plusieurs dizaines de millisecondes (on est au ralenti).....
    Bof! Peut-être, mais certainement très ardu à développer, c'est clair qu'avec environ 6 semaines de confinement, tu as le temps . Avec les interrupt, ça se fait "presque tout seul", tu n'as qu'à pêcher le résultat quand l'interrupt te le signale.

    Citation Envoyé par Pat42 Voir le message
    sinon, Leclerc vend des horloges pilotées par dcf77 .
    Il y a aussi ça.

    Cordialement
    jpbbricole
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  3. #23
    Membre éclairé
    Citation Envoyé par Pat42 Voir le message
    J'essaie en me servant de ton programme, de le réécrire sans interruption, avec des millis() , delay et digitalRead (c'est pour apprendre).
    Il me semble que cela devrait être aussi fiable qu'avec des interruptions...? on mesure des temps de plusieurs dizaines de millisecondes (on est au ralenti).
    Pour l'instant je bute sur les drapeaux et les millis() qui ne se laissent pas facilement manipuler dans des "conditions IF".
    Oui c'est tout à fait réalisable étant donné que le code ne fait rien d'autre pour le moment et qu'on s"intéresse à des événements lents. Une petite machine à état va régler cela pour vous.

    Voici un exemple de comment ça pourrait se coder (tapé ici, donc pas sûr à 100% que ça compile) - je vous laisse debuguer si nécessaire

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    // les constantes
    const uint8_t pinImportante = 2;
    const uint8_t ledPin = LED_BUILTIN; // la LED intégrée sur la carte (pin 13 sur un UNO par exemple)
     
    const uint16_t topSyncMinimum = 800U;   // durée min d'attente
    const uint16_t dureeSyncLED = 50U;      // durée d'allumage de la LED
    const uint16_t antiRebond = 15U;        // si le signal n'est pas pur, on évitera les rebonds en attendant un peu
     
    // variable pour la mesure du temps
    uint32_t topChronoDepartMesure, topChronoFinMesure;
     
    // les différentes etapes du processus
    enum : uint8_t {ATTENTE_HIGH, ATTENTE_LOW, MESURE, LED_SYNC_ON} etapeMesure = ATTENTE_HIGH;
     
    void setup()
    {
      pinMode(pinImportante, INPUT_PULLUP);
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW);
      Serial.begin(115200);
    }
     
    void loop()
    {
      uint8_t etatPinImportante = digitalRead(pinImportante);
      switch (etapeMesure) {
     
        case ATTENTE_HIGH: // Lors du premier lancement, il ne faut pas prendre le train en route, on attend un HIGH
          if (etatPinImportante == HIGH) etapeMesure = ATTENTE_LOW; // c'est bon on est prêt à commencer
          break;
     
        case ATTENTE_LOW: // On attend que la pin passe à LOW
          if (etatPinImportante == LOW) {
            topChronoDepartMesure = millis();
            delay(antiRebond);
            etapeMesure = MESURE; // c'est bon on mesure
          }
          break;
     
        case MESURE: // on est LOW, on attend de repasser à HIGH
          if (etatPinImportante == HIGH) { // ok, on vient de repasser HIGH
            topChronoFinMesure = millis();
            if (topChronoFinMesure - topChronoDepartMesure >= topSyncMinimum) { // si plus de topSyncMinimum ms alors led allumée 50 ms
              digitalWrite(ledPin, HIGH);
              etapeMesure = LED_SYNC_ON; // on active la lED
              Serial.print(F("TOP SYNC !"));
            } else {
              etapeMesure = ATTENTE_LOW; // On est prêt à recommencer
              Serial.print(F("Parasite !"));
              delay(antiRebond);
            }
            Serial.print(F("\tTEMPS = "));
            Serial.print(topChronoFinMesure - topChronoDepartMesure);
            Serial.println(F(" ms."));
          }
          break;
     
        case LED_SYNC_ON:
          if (millis() - topChronoFinMesure >= dureeSyncLED) {
            digitalWrite(ledPin, LOW);
            etapeMesure = ATTENTE_LOW; // On est prêt à recommencer
          }
          break;
      }
    }


    L'idée d'une machine à état c'est qu'on définit formellement dans quel état se trouve le programme à un instant T et quels sont les événements qui peuvent survenir pour nous faire changer d'état.
    La structure du code est simple, dans la loop on regarde dans quel état on est par un switch/case et pour chaque cas on traite les événements possibles

    J'ai identifié 4 états que je range pour la bonne forme dans un enum
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    enum : uint8_t {ATTENTE_HIGH, ATTENTE_LOW, MESURE, LED_SYNC_ON} etapeMesure = ATTENTE_HIGH;
    et je vais suivre mon état dans la variable etapeMesure. Au début du programme on veut s'assurer que l'on part avec la pin HIGH pour bien détecter une transition vers LOW. donc l'état initial sera ATTENTE_HIGH et le case associé ne fait qu'attendre un HIGH.

    Une fois qu'on reçoit ce HIGH, on passe à l'étape suivante qui est d'attendre le LOW.

    Quand on reçoit LOW on note l'heure de départ et on passe à l'étape suivante qui est la mesure et on attend le prochain HIGH.

    Quand on reçoit le HIGH, on note l'heure de fin. et on vérifie si c'est un signal qui a bien durée plus de 800ms ou pas. Si c'est un bon signal alors on passe dans un mode ou on allume la LED sinon on retourne à l'attente

    Quand on est dans le mode LED allumée, on attend que les 50ms se soient écoulées puis on retourne au départ.

    ça devrait marcher car les signaux sont lents, mais on voit bien avec cette approche que si par hasard le signal vient à changer pendant que la LED de sync est allumée, on ne le voit pas tout de suite alors qu'avec l'approche par interruption c'est capturé pour vous. (On voit aussi qu'il y a plus de code qu'avec l'interruption !)

    A titre "éducatif" c'est toujours bon de savoir écrire une petite machine à états.

  4. #24
    Membre à l'essai
    Décodage code horaire 162kh
    Bonjour à vous,

    C' est uniquement pour apprendre et bien comprendre que je veux un petit programme sans interruption.
    Il est certain que les interrupts seront nécessaires, car il y aura à gérer, l'afficheur I2c, le décodage des bits ect...

    Je suis allé faire un tour sur le lien, Effectivement, ce sont des modules pour la réception de DCF 77.
    Il y a quelques temps de cela , je m'étais fabriqué un récepteur sur la fréquence de 77.5 khz et la logique qui va bien pour l'heure (avec une tartine de CI).
    La réception n'étant pas terrible, je me retourne sur Allouis qui m'arrive clair et net, et la carte arduino nano qui me donnera souplesse et simplicité pour des modifs
    éventuelles.


    Super le code et les explications, je vais me les garder pour se soir lol.

    Je vous mets en pièce jointe un souvenir..
    Une carte de développement (intégrant le célèbre 68 HC11) utilisée dans le bureau d'étude dans lequel je bossais en 1991. Cette carte se connecte sur un pc ou un Minitel .
    Cela permettait aux collègues programmeurs d'emmener du boulot chez eux lol ...A l'époque les PC n'étaient pas courant chez les particuliers, le minitel SI !

    Je vais prendre un peu de temps pour la remettre en fonctionnement, pour le fun.

    Merci et bonne soirée à vous.

    PAT42

  5. #25
    Membre à l'essai
    Décodage code hoaraire 162kh
    Bonsoir, j'ai testé le code sans interrupt. et cela fonctionne sans avoir eu besoin de débugger...
    Avec ce que j' ai appris , je vais essayer de greffer le top des secondes sur le programme.

    Bonne soirée à vous et à JP.

    Pat 42

  6. #26
    Membre éclairé
    Citation Envoyé par Pat42 Voir le message
    Bonsoir, j'ai testé le code sans interrupt. et cela fonctionne sans avoir eu besoin de débugger...


    cool

    bonne soirée (et merci pour la jolie photo souvenir !!)

  7. #27
    Membre à l'essai
    Décodage code horaire 162kh
    Bonjour a vous,
    j'ai bossé sur mon horloge pilotée , mais comme n'ayant pas encore beaucoup d'expérience, je bute sur deux pbs...

    J'ai intégré dans le programme qui gère la synchro un timer (timer 2) qui est synchronisé par le top synchro et me génère une interruption toutes les secondes.

    Je me suis aperçu que la seconde est trop longue et qu' à la fin d'une minute , je ne suis plus synchro de 5ms. j'ai diminué le compteur "nbrInterrup" à 248, mais là, c'est l"inverse et il me manque du temps. Puis je rajouter du temps précisément avec "delay" sans que cela crée un soucis dans le fonctionnement des interruptions ou y a t 'il une autre solution ?

    Et ma question principale est la suivante,

    je n'arrive pas à intégrer une fonction tempo de 50ms avec millis(); dans une quelconque routine pour avoir un niveau haut de 50 ms à chaque seconde.
    Comment puis je m'y prendre en sachant que je m'en sors avec delay ( mais que je veux éviter d'utiliser car j'aurais besoin après chaque interruption d' un niveau haut de 50ms indiquant par une led les secondes et "au même instant" le démarrage d'une tempo de 200ms pour lire les bits de données suivant le top.) ?

    je vous mets les deux programmes.

    Je vous remercie.

    Pat

    Programme avec delay.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    #define interruptPin 2
    int16_t nbrInterrupt;
    volatile boolean compteur = 0;                                                                                //volatile boolean mesureEnCours = false;
    volatile boolean mesureNouvelle = false;
    volatile unsigned long tempoDepart = millis();
    volatile unsigned long topSyncMilliSec = millis();
    int ledSeconde = 12;
    int ledMinute = 13;
    const unsigned int topSyncMinimum = 1000;
     
    void setup()
    {
      Serial.begin(115200);
      pinMode (ledSeconde, OUTPUT);
      digitalWrite(ledSeconde, LOW);
      pinMode (ledMinute, OUTPUT);
      digitalWrite(ledMinute, LOW);
     
                                                           // Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier,
      TCCR2A = 0;                                         //on peut aussi écrire bitClear (TCCR2A, WGM21); // WGM21 = 0 -----//bitClear (TCCR2A, WGM20); // WGM20 = 0
      TCCR2B = 0b00000110;                               // clk/256 est incrémenté toutes les 16uS
      TIMSK2 = 0b00000001;                               // TOIE2 Autorise l'interruption quand le compteur déborde
      pinMode(interruptPin, INPUT_PULLUP);
      pinMode(ledMinute, OUTPUT);
     
     
     
      attachInterrupt(digitalPinToInterrupt(interruptPin), impulsionInterruption, CHANGE);
    }
     
    void loop()
    {
      if (mesureNouvelle == true)
      {
        topSyncAction();
        mesureNouvelle = false;
      }
      if (compteur == 1)
      {
        routineSeconde();
        compteur = 0;
      }
     
    } 
     
     
     
     
    void impulsionInterruption()
    {
      if (digitalRead(interruptPin) == LOW)// && !mesureEnCours)
      {
        tempoDepart = millis();
        //mesureEnCours = true;
      }
      if (digitalRead(interruptPin) == HIGH)// && mesureEnCours)
      {
        topSyncMilliSec = millis() - tempoDepart;
        // mesureEnCours = false;
        mesureNouvelle = true;
     
      }
     
    }
     
     
     
     
    void topSyncAction()
    {
      if (topSyncMilliSec >= topSyncMinimum)
      {
        nbrInterrupt = 0;
        digitalWrite (ledMinute,HIGH);
        delay(50);
        digitalWrite (ledMinute, LOW);
        //digitalWrite (ledSeconde,HIGH);
       //routineSeconde();
       //digitalWrite (ledSeconde,HIGH);
      }
     
      else
      {
        digitalWrite(ledMinute, LOW);
      }
     
    }
     
    ISR(TIMER2_OVF_vect)
    {
      TCNT2 = 6;                      //on met TCNT2 a 7 pour avoir un comptage de 249 incrementations 256 -7= 249
     
      if (nbrInterrupt++ >= 249)     //interruption toutes les secondes
      {
        nbrInterrupt = 0;            //on remet nbrIterrupt à 0
        compteur = 1;
     
      }
     
    }
     
     
     
    void routineSeconde()
    {
      digitalWrite ( ledSeconde, HIGH);
      delay(50);
      digitalWrite (ledSeconde, LOW);
     
    }


    programme non fonctionnel avec millis();

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    #define interruptPin 2
    int16_t nbrInterrupt;
    volatile boolean compteur = 0;
    volatile boolean mesureEnCours = false;                                                                              
    volatile boolean mesureNouvelle = false;
    volatile unsigned long tempoDepart = millis();
    volatile unsigned long topSyncMilliSec = millis();
    volatile unsigned long departSeconde = millis();
    int ledSeconde = 12;
    int ledMinute = 13;
    const unsigned int topSyncMinimum = 1000;
     
    void setup()
    {
      Serial.begin(115200);
      pinMode (ledSeconde, OUTPUT);
      digitalWrite(ledSeconde, LOW);
      pinMode (ledMinute, OUTPUT);
      digitalWrite(ledMinute, LOW);
     
      // Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier,
      TCCR2A = 0;                                         //on peut aussi écrire bitClear (TCCR2A, WGM21); // WGM21 = 0 -----//bitClear (TCCR2A, WGM20); // WGM20 = 0
      TCCR2B = 0b00000110;                               // clk/256 est incrémenté toutes les 16uS
      TIMSK2 = 0b00000001;                               // TOIE2 Autorise l'interruption quand le compteur déborde
      pinMode(interruptPin, INPUT_PULLUP);
      pinMode(ledMinute, OUTPUT);
     
     
     
      attachInterrupt(digitalPinToInterrupt(interruptPin), impulsionInterruption, CHANGE);
    }
     
    void loop()
    {
      if (mesureNouvelle == true)
      {
        topSyncAction();
        mesureNouvelle = false;
      }
     
      if (digitalRead (ledSeconde) == HIGH)
      {
       departSeconde = millis();
       routineSeconde();
      }
     
     
    }
     
     
     
     
    void impulsionInterruption()
    {
      if (digitalRead(interruptPin) == LOW && !mesureEnCours)
      {
        tempoDepart = millis();
        mesureEnCours = true;
      }
      if (digitalRead(interruptPin) == HIGH && mesureEnCours)
      {
        topSyncMilliSec = millis() - tempoDepart;
         mesureEnCours = false;
        mesureNouvelle = true;
     
      }
     
    }
     
     
    void topSyncAction()
    {
      if (topSyncMilliSec >= topSyncMinimum)
      {
        nbrInterrupt = 0;
        digitalWrite (ledMinute, HIGH);
        delay(50);
        digitalWrite (ledMinute, LOW);
        //digitalWrite (ledSeconde,HIGH);
        //routineSeconde();
        //digitalWrite (ledSeconde,HIGH);
      }
     
      else
      {
        digitalWrite(ledMinute, LOW);
      }
     
    }
     
    ISR(TIMER2_OVF_vect)
    {
      TCNT2 = 6;                      //on met TCNT2 a 7 pour avoir un comptage de 249 incrementations 256 -7= 249
     
      if (nbrInterrupt++ >= 249)     //interruption toutes les secondes
      {
        nbrInterrupt = 0;            //on remet nbrIterrupt à 0
        compteur = 1;
        digitalWrite(ledSeconde , HIGH);
     
      }
     
    }
     
    void routineSeconde()
    {
     
     
      if (millis() >= departSeconde == 50)
      {
        digitalWrite (ledSeconde, LOW);
      }
     
    }

  8. #28
    Membre éclairé
    dans la version avec interruption il faut que les variables partagées en lecture/écriture entre l'ISR et la loop soient déclarées en volatile
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    volatile uint16_t nbrInterrupt;


    Mais avec des traitements nécessaire à la ms, je pense que vous n'avez pas besoin de timer.

    Ensuite vous aurez toujours une dérive, le résonateur des petits Arduino n'est pas précis dans la durée donc pour faire des mesures sur du long terme la solution est de ne pas utiliser l'oscillateur interne du micro mais de prendre un vrai oscillateur ou s'assurer qu'on ne cumule pas une mesure trop longue

    je n'arrive pas à intégrer une fonction tempo de 50ms avec millis(); dans une quelconque routine pour avoir un niveau haut de 50 ms à chaque seconde.
    il faut le faire avec une machine à état comme dans le code que j'avais proposé avec le
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    case LED_SYNC_ON:
    ou juste un if sur l'état de cette LED (ce qui revient à avoir plusieurs machines à états en parallèle si on veut gérer des événements disjoints)

  9. #29
    Membre à l'essai
    Décodage code horaire 162kh
    Bonjour, Jay M

    Je vais modifier "nbrInterrupt"
    Je n'aurais pas de pb de dérive étant donné que je réinitialise le compteur une fois par minute avec la synchro. donc pas de soucis . Le pb est que la seconde ne fait pas une seconde et qu'a la fin de la minute, les micros secondes sont devenues des ms.

    Mon souci avec millis(), est que cela ne fonctionne pas dans la fonction interruption et même dans une routine appelée par la fonction interruption.
    Il faut que j'utilise la loop et là je bug.

    j'ai pensé à ne pas utiliser le timer, car oui! cela ne va pas vite...

    Merci pour les conseils

    Cordialement

    Pat

  10. #30
    Membre éclairé
    inspirez vous du code posté en #23 pour faire une machine à état...

    Mon souci avec millis(), est que cela ne fonctionne pas dans la fonction interruption et même dans une routine appelée par la fonction interruption.
    la valeur de millis() est correcte à l'entrée de l'interruption mais oui elle n'évolue plus et si vous avez à passer plus d'une milliseconde dans une interruption c'est qu'il y a un souci de conception de toutes façons.