IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Arduino Discussion :

Détection de top synchro sur FI sur 162kh (fréquence étalon).


Sujet :

Arduino

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mars 2019
    Messages : 64
    Par défaut Détection de top synchro sur FI sur 162kh (fréquence étalon).
    Bonjour a tous,
    Il y a quelques mois de cela , vous m'avez aidé à mettre au point un fréquencemètre avec la soustraction des FI et autres
    petites choses qui vont bien, pour lire précisément les fréquences en AM et en FM sur certains de mes récepteurs radios
    dont je fais la collection et je m'en suis bien sortie (compréhension des timers 1 et 2).

    Il ya peu de temps je me suis fabriqué un récepteur sur 162 khz (FRANCE INTER: heure étalon) pour étalonner
    mes deux fréquencemètres,et ils en avaient besoin...

    Puis il m'est venu l'idée de récupérer la modulation de phases (photo jointe), pour le fun et mettre au point ma petite horloge a moi...

    Mais malheureusement! les quelques connaissances que j'ai acquéri ne me suffisent pas pour résoudre le premier pb sur lequel je bute.

    Aprés avoir passé des heures devant mon écran
    à tester milli(); delay, les interruptions...et avoir parcouru le net, je demande de l'aide!

    Je vous explique;

    Pendant la 59 em secondes du code horaire (voir photo), il ya un silence synchro qui dure plus de 1 seconde et le premier front montant
    qui suit ce silence est le top du début de la première seconde.

    Je n'arrive pas à écrire un petit bout de code qui me permettrait en fait pour débuter mon programme:

    1) de détecter un niveau bas égal à environ 800ms ( à partir des fronts montants, pris en charge par l' interrupt sur la pin2. Cela n'arrive
    qu'une fois dans la minute).

    2) puis quand on a compté 800ms, on active une tempo de 520 ms qui sera suivi d'un niveau haut sur la pin 13 de 50ms. Dans l'idéal, il y aura le top de la 1° seconde encadré par un niveau haut de 50ms, ce qui me permettra avec un ET logique d'éliminer les parasites éventuels.

    3) si les tops sont plus rapprochés que 800ms, la pin 13 reste à 0


    C'est juste le début , mais j'ai vraiment du mal...j'ai fait pas mal d'essais mais aucun ne fonctionnait correctement...

    je vous ai mis un de mes essais...

    1) l'interruption reinitialise "tempoDepart"
    2) je saute dans un sous programme "programme()" qui attend un niveau bas de plus de 800ms, et met à 1 la pin 13 puis va dans loop.
    Il qui passe dans "else" si le temps entre deux interrupts fait moims que 800ms et met
    la pin 13 à 0 et va dans loop.

    Le pgr que je vous montre fonctionne sur trois pattes. Il continue à tourner même sans interruption, d'ou des niveaux hauts régulièrement .

    j' ai testé le programme dans une routine appelée par l' interruption, mais apparemment millis() ne fonctionne pas dans ce genre de routine.

    La photo montre les modulation de phase et la logique associée.

    Merci de me donner un coup de main.

    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
    int led = 13;
    unsigned long tempoDepart = 0;
    int interval=800;
     
     
    void setup() {
    pinMode(led, OUTPUT);
    digitalWrite(led,LOW); 
    attachInterrupt(0, mafonction, RISING); 
    tempoDepart = millis();
     Serial.begin(115200);  
    }                                                                  
     
    void loop (){programme();}
     
     
                          void mafonction()  
                         {tempoDepart= millis();}
     
     
        void programme(){ if ( ( millis()- tempoDepart ) >= interval ) 
                        {                                                          //si plus de 800 ms sans interrupt, led allumée 50 ms
                       digitalWrite(led,HIGH);
                        delay(50);                                                 // calibrage niveau haut
                          digitalWrite (led,LOW);                   
                            tempoDepart=millis();}
     
     
                          else { digitalWrite(led,LOW); }}
    Images attachées Images attachées  

  2. #2
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonsoir Pat42
    Citation Envoyé par Pat42 Voir le message
    Le pgr que je vous montre fonctionne sur trois pattes. Il continue à tourner même sans interruption, d'ou des niveaux hauts régulièrement .
    Je n'ai pas encore étudié ton programme, mais ton problème ci dessus, peut provenir du fait que tu n'initialises pas la pin de l'interrupt:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #define interruptPin 2 
                                        // Pin de l'interrupt
    void setup()
    {
    	Serial.begin(115200);
    	pinMode(led, OUTPUT);
    	digitalWrite(led,LOW);
    
    	pinMode(interruptPin, INPUT_PULLUP);
    	attachInterrupt(digitalPinToInterrupt(interruptPin), mafonction, RISING);
    
    	tempoDepart = millis();
    	Serial.begin(115200);
    }
    J'ai légèrement modifié attachInterrupt comme préconisé chez Arduino.
    Je me "lance" dans ton programme!

    A+
    Cordialement
    jpbbricole

  3. #3
    Membre averti
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mars 2019
    Messages : 64
    Par défaut Décodage code hoaraire 162kh
    Bonsoir jpbbricole,

    En fait le programme prends bien en compte les interruptions (je pourrais éventuellement mettre une photo). A chaque interruption et si le temps entre deux interruptions est plus grand que 800ms comme indiqué dans "interval", j'ai bien le programme qui fait le job , c'est à dire, un niveau haut sur la pin 13 que j'ai calibré à 50ms-----> aprés un niveau bas de 800ms ( je n'ai pas mis pour l'instant la tempo de 520 ms indiquée dans mon précédent poste, pour simplifier ).

    Le soucis est que je n'arrive pas à faire patienter le pgr dans une boucle infinie, mais non bloquante, pour qu'il attende la prochaine interruption.
    Du coup, le programme m' envoie des niveaux "hauts parasites" sur la pin 13 de 50ms régulièrement en plus des niveaux "hauts normaux"et cela est bien compréhensible puisqu'il continue à appeler la routine (même s'il n'y pas d'interrupt).

    Dans l'autre cas, si le temps entre 2 interruptions est plus court que 800ms, le prg fait aussi le job.
    J' ai bien un niveau bas sur la pin 13 , avec régulièrement des créneaux "parasites" de 50ms puisque le prg continu a tourner.

    Pour être bien bien compris; j'aurais besoin d'une solution pour que le programme attende "dans un coin" l' interruption, qu'il fasse son job et qu' il retourne dans "son coin" en attendant la prochaine interrupt.

    j'ai essayé avec divers solution while(1) {}, boucle infini , loop, mais rien ne fonctionne.

    Cordialement

    Pat

  4. #4
    Membre averti
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mars 2019
    Messages : 64
    Par défaut Décodage code horaire 162kh
    Jpbbricole,


    j'ai oublié , je vais tester le code que vous avez modifié .

    en vous remerciant.

    Pat

  5. #5
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonsoir Pat
    Citation Envoyé par Pat42 Voir le message
    Jpbbricole,


    j'ai oublié , je vais tester le code que vous avez modifié .

    en vous remerciant.

    Pat
    J'ai, un peu, étudié ton programme. Je ne pense pas que mes modifications vont changer grand chose, c'était de la cosmétique, le problème est structurel. Je peux regarder cela demain matin.

    Bonne soirée.
    Cordialement
    jpbbricole

  6. #6
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonjour Pat

    Voilà, j'ai "étudié" ton programme. Le premier point, c'est l'indentation de ton programme, une bonne indentation rend le programme beaucoup plus claire et plus aisé à débugger.
    Tu parles d'une transition de HAUT à bas puis de bas à HAUT, donc 2 transitions à évaluer, donc l'interruption est sur l'événement CHANGE.
    A noter les variables déclarées comme volatile pour toutes les variables partagées entre le programme principal et les routine d'interruption.
    J'ai ajouté quelques variables et "embelli" le tout de quelques Serial.print pour voire ce qui se passe.

    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
    #define interruptPin 2                                      // Pin de l'interrupt
     
    volatile boolean mesureEnCours = false;                     // Volatile pour les variables traitées en interrupt
    volatile boolean mesureNouvelle = false;
    volatile unsigned long tempoDepart = millis();                       
    volatile unsigned long topSyncMilliSec = millis();                      
     
    int led = 13;
    const unsigned int topSyncMinimum=800;
     
    void setup()
    {
    	Serial.begin(115200);
     
    	pinMode(interruptPin, INPUT_PULLUP);
    	pinMode(led, OUTPUT);
     
    	pinMode(led, OUTPUT);
    	digitalWrite(led,LOW);
    	/*
    		comme on veut mesurer une impulsion négative (10001) 
    		il y a 2 transitions à détecter
    		il faut mettre CHANGE comme déclenchement
    	*/
    	attachInterrupt(digitalPinToInterrupt(interruptPin), impulsionInterruption, CHANGE);
    }
     
    void loop()
    {
    	if (mesureNouvelle == true)
    	{
    		Serial.print("\nImpulsion LOW ");
    		Serial.print(topSyncMilliSec);
    		Serial.println(" milliSec.");
     
    		topSyncAction();
     
    		mesureNouvelle = false;
    	}
    }
     
    void impulsionInterruption()
    {
    	if (digitalRead(interruptPin) == LOW && !mesureEnCours) // Si transition HIGHlow et pas de mesure en cours
    	{
    		tempoDepart= millis();
    		mesureEnCours = true;                               // Mesure en cours
    	}
    	if (digitalRead(interruptPin) == HIGH && mesureEnCours) // Si transition lowHIGH et une mesure en cours
    	{
    		topSyncMilliSec = millis()-tempoDepart;
    		mesureEnCours = false;                               // Plus de mesure en cours
    		mesureNouvelle = true;
    	}
    }
     
     
    void topSyncAction()
    { 
    	if (topSyncMilliSec >= topSyncMinimum)
    	{                                                          // si plus de 800 ms sans interrupt, led allumée 50 ms
    		Serial.println("Top sync.");
    		digitalWrite(led,HIGH);
    		delay(50);                                             // calibrage niveau haut
    		digitalWrite (led,LOW);
    	}
     
    	else 
    	{ 
    		Serial.print("Parasite ");
    		Serial.print(topSyncMilliSec);
    		Serial.println(" milliSec.");
     
    		digitalWrite(led,LOW); 
    	}
    }
    A toi de jouer

    A+
    Cordialement
    jpbbricole

  7. #7
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    Pourquoi ne pas construire les valeurs au fur et à mesure de la réception au lieu de tout stocker dans un tableau et faire le traitement plus tard ?

    au lieu d'utiliser un index pour écrire le bit au bon endroit dans le tableau, testez la valeur de cet index et faites l'opération mathématique qui va bien

    par exemple au moment où index == 0 --> on met toutes les variables à calculer à 0 - par exempleQuand index vaut 47, on sait qu'on reçoit un élément sur le jour de semaine et que c'est le bit de poids faible. on fait doncQuand index vaut 48, on sait qu'on reçoit un élément sur le jour de semaine et que c'est le second bit. on fait donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     jourSemaine += bitRecu<<1; // idem que 2* bitRecu
    Quand index vaut 49, on sait qu'on reçoit un élément sur le jour de semaine et que c'est le dernier bit. on fait donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     jourSemaine += bitRecu<<2; // idem que 4* bitRecu
    idem pour les autres indices. ça se code dans un grand switch ou des if imbriqués.


    Vous gagnez ainsi la place du tableau et les maths sont faits au fur et à mesure, vous pouvez même mettre l'affichage à jour pour le jour de la semaine dès l'index 49 reçu par exemple si vous voulez.

  8. #8
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonjour Jay M
    Citation Envoyé par Jay M Voir le message
    ...ça se code dans un grand switch ou des if imbriqués....
    Donc un switch de 60 éléments ou 60 if !!!, je ne vois pas du tout l'avantage de cette méthode et surtout à écrire et entretenir...

    Citation Envoyé par Jay M Voir le message
    Vous gagnez ainsi la place du tableau et les maths sont faits au fur et à mesure...
    Est-ce vraiment nécessaire d'économiser ce tableau?


    Cordialement
    jpbbricole

  9. #9
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Avril 2002
    Messages : 3 252
    Par défaut
    Salut,

    jpbbricole.


    Citation Envoyé par Pat42 Voir le message
    mettre au point ma petite horloge a moi...
    Alors il faudra ce méfier de millis() car dans une 50aine de jour il y aura un reset de millis() et il pourra y avoir un loupé.

  10. #10
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    Citation Envoyé par Vincent PETIT Voir le message
    Alors il faudra se méfier de millis() car dans une 50aine de jour il y aura un reset de millis() et il pourra y avoir un loupé.
    Non non, @JP l'a écrit correctement. il fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (topSyncMilliSec >= topSyncMinimum) {...}
    et topSyncMinimum est une constante et topSyncMilliSec a bien été calculé comme il le fallait avec une soustraction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     topSyncMilliSec = millis()-tempoDepart
    Une modification que j'apporterais c'est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    intconst unsigned int topSyncMinimum=800;
    pour avoir la durée en entier non signé et dire au compilateur que c'est une constante.

    L'autre modification ce serait de dupliquer les variables volatiles dans la loop dans une section critique (interruptions bloquées). En effet comme elles ont plusieurs octets, les maths peuvent planter si on a une interruption pendant qu'on utilise ces octets. (pas de garantie d'atomicité sur 32 bits avec un processeur 8 bit) ou ne pas laisser l'interruption jouer avec les variables si mesureNouvelle est vrai. (ie tant que la loop() ne l'a pas traité)

  11. #11
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonsoir Jay M
    Citation Envoyé par Jay M Voir le message
    Une modification que j'apporterais c'est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    intconst unsigned int topSyncMinimum=800;
    ...
    C'est corrigé.
    Citation Envoyé par Jay M Voir le message
    L'autre modification ce serait de dupliquer les variables ...
    Alors, là, j'ai pas tout compris, pourrais-tu me réécrire la void loop() en conséquence.

    A+
    Cordialement
    jpbbricole

  12. #12
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    Citation Envoyé par jpbbricole Voir le message
    Alors, là, j'ai pas tout compris, pourrais-tu me réécrire la void loop() en conséquence.
    Salut JP,

    ça ressemblerait sans doute à cela avec une gestion de zone critique:
    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
    // les pins
    const uint8_t interruptPin = 2;  // Pin de l'interrupt
    const uint8_t ledPin = LED_BUILTIN; // la LED intégrée sur la carte (pin 13 sur un UNO par exemple)
     
    // les constantes
    const unsigned int topSyncMinimum = 800U;
     
    // Volatile pour les variables utilisées en interruption et dans le code principal
    volatile boolean mesureEnCours = false;  
    volatile boolean mesureNouvelle = false;
    volatile unsigned long tempoDepart = 0 ;
    volatile unsigned long topSyncMilliSec = 0;
     
    // variable pour copie lors des opérations non atomiques
    unsigned long topSyncMilliSecProtected;
     
    void impulsionInterruption()
    {
      uint8_t etatPin = digitalRead(interruptPin); // ne lire qu'une seule fois la valeur de la pin, c'est lent et on veut toujours limiter le temps passé dans une ISR
      if (etatPin == LOW && !mesureEnCours) {
        // Si transition HIGH -> LOW et pas de mesure en cours
        tempoDepart = millis(); // on note le temps de départ
        mesureEnCours = true; // et le fait qu'on commence une mesure
      } else if (etatPin == HIGH && mesureEnCours) {
        // Si transition LOW -> HIGH et une mesure est déjà en cours
        topSyncMilliSec = millis() - tempoDepart; // on note la durée
        mesureEnCours = false; // On termine une mesure
        mesureNouvelle = true; // On met un drapeau pour dire à la loop qu'une durée est dispo
      }
    }
     
    void setup()
    {
      Serial.begin(115200);
     
      pinMode(interruptPin, INPUT_PULLUP);
     
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW);
     
      /*
        comme on veut mesurer une impulsion négative (10001)
        il y a 2 transitions à détecter
        il faut mettre CHANGE comme déclenchement
      */
      attachInterrupt(digitalPinToInterrupt(interruptPin), impulsionInterruption, CHANGE);
    }
     
    void loop()
    {
      if (mesureNouvelle) {
        // ZONE PROTEGEE CAR topSyncMilliSec EST SUR 4 OCTETS. OPERATIONS NON ATOMIQUES
        noInterrupts();
        topSyncMilliSecProtected = topSyncMilliSec;
        interrupts();
     
        Serial.print(F("\nImpulsion LOW "));
        Serial.print(topSyncMilliSecProtected);
        Serial.println(F(" millisec."));
     
        topSyncAction(); // va aussi utiliser topSyncMilliSecProtected
     
        mesureNouvelle = false;
      }
    }
     
    void topSyncAction()
    {
      if (topSyncMilliSecProtected >= topSyncMinimum) {
        // si plus de topSyncMinimum ms sans interrupt, led allumée 50 ms
        Serial.println(F("Top sync."));
        digitalWrite(ledPin, HIGH);
        delay(50);  // calibrage niveau haut
        digitalWrite(ledPin, LOW);
      } else {
        Serial.println(F("Parasite"));
      }
    }
    Explication:

    Sur un processeur 8 bits, la majorité des opérations sur 1 octet (test d'un booléen, comparaison de valeur, opérations mathématiques genre addition, soustraction, décalage, ...) se font en 1 cycle d'horologe et donc ne peuvent pas être tronçonnées par une interruption. On dit qu'elles sont 'atomiques' (c'est l'élément de base comme un atome).

    Il n'en va pas de même quand vous travaillez sur des variables qui font plusieurs octets, le processeur ne sachant pas traiter des opérations sur 2 ou 4 octets d'un coup, le compilateur est obligé de générer plusieurs lignes de code pour effectuer le calcul, et dans chaque ligne on va accéder à des octets faisant partie de la variable. charger cela dans un registre, faire le traitement puis éventuellement remettre une nouvelle valeur dans l'octet d'origine.

    Si vous n'avez pas de bol et qu'une interruption vient modifier la variable sur laquelle vous travaillez pendant ces opérations saucissonnées, et comme vous avez (comme il se doit) déclaré la variable volatile, le calcul ne met pas toute la donnée en mémoire cache, il accède directement à chaque octet directement en mémoire et le traitement peut ne plus vouloir rien dire car certains octets peuvent changer pendant l'opération.

    un exemple:

    Imaginez un compteur de type unsigned int, donc sur deux octets, qui s'incrémente avec des interruptions.
    Imaginez que vous voulez imprimer la valeur chaque fois que le compteur a monté de 5.

    Votre interruption pourrait ressembler à cela:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // Volatile pour les variables utilisées en interruption et dans le code principal
    volatile unsigned int compteur = 0;
    volatile bool affichageRequis = false;
     
    void incrementeISR()
    {
      compteur++;
      if (compteur % 5 == 0) affichageRequis = true;
    }
    et la loop testera le drapeau pour effectuer l'affichage
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void loop()
    {
      if (affichageRequis) {
        Serial.print(F("Le compteur vaut : "));
        Serial.println(compteur);
        affichageRequis = false;
      }
    }
    On se dit que tout va bien se passer....

    Mais si vous n'avez pas de chance, lorsque compteur vaut 255 (soit 0x00FF) un affichage sera demandé puisque c'est divisible par 5.
    Si pendant que la loop travaille sur Serial.println(compteur); pour convertir le compteur en ASCII une interruption arrive, le compteur vaudra 256 (soit 0x0100). Comme le compilateur ne travaille que sur des octets, il pourrait avoir traité l'octet de poids faible 0xFF puis ensuite voir en mémoire 0x01 pour l'octet de poids fort et donc fabriquer un affichage qui correspond à 511 (soit 0x01FF) au lieu de 255. Vous imaginez les conséquences que ça pourrait avoir si la valeur n'était pas utilisée juste en affichage mais pour déclencher un processus d'urgence par exemple si vous dépassez 500...

    ==> pour se prémunir de ces opérations non atomiques, il faut dupliquer les variables critiques qui pourraient être modifiées dans notre dos par l'interruption en créant une copie locale. Donc ici une loop correcte serait

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void loop()
    {
      if (affichageRequis) {
        // ZONE PROTEGEE CAR compteur EST SUR 2 OCTETS. OPERATIONS NON ATOMIQUES
        noInterrupts();
        unsigned int compteurProtected = compteur;
        interrupts();
        Serial.print(F("Le compteur vaut : "));
        Serial.println(compteurProtected);
        affichageRequis = false;
      }
    }
    il faut bien sûr garder la zone critique la plus courte possible, se contenter de dupliquer les variables clés afin de ne pas rater d'interruptions.
    Dans l'interruption on n'a pas à s'en soucier bien sûr puisqu'une interruption ne peut pas être interrompue.

    Sur un processeur 32 bits, beaucoup d'opérations de base se font en atomique sur 32 bits, donc on n'a moins le soucis pour des maths simples, mais un affichage de valeur ou un calcul complexe faisant intervenir plusieurs fois la variable peut aussi présenter un souci. Donc soit il faut s'assurer que l'interruption ne peut pas interférer avec nos calculs, soit se protéger en travaillant sur une copie.

    j'espère que ça clarifie mon propos.

  13. #13
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonsoir Jay M

    Voilà, j'ai modifié le programme en "m'inspirant" de tes remarques, toujours pertinentes et surtout enrichissantes, pour moi, autodidacte. J'ai découvert le C avec l'Arduino, avant j'ai été "formaté" par quelques décennies de VisualBasic!
    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
    #define interruptPin 2                                     // Pin de l'interrupt
    #define topSyncLedPin 13                                   // Pin de LED topSync
     
    volatile boolean mesureEnCours = false;                    // Volatile pour les variables traitées en interrupt
    volatile boolean mesureNouvelle = false;
    volatile unsigned long topSyncInterrDepart = millis();                       
    volatile unsigned long topSyncInterrMilliSec = millis();                      
    unsigned long topSyncMilliSec;
     
    const unsigned int  topSyncMinimum=800;                    // variable pour copie lors des opérations non atomiques
     
    void setup()
    {
    	Serial.begin(115200);
     
    	pinMode(interruptPin, INPUT_PULLUP);
    	pinMode(topSyncLedPin, OUTPUT);
     
    	pinMode(topSyncLedPin, OUTPUT);
    	digitalWrite(topSyncLedPin,LOW);
    	/*
    		comme on veut mesurer une impulsion négative (10001) 
    		il y a 2 transitions à détecter
    		il faut mettre CHANGE comme déclenchement
    	*/
    	attachInterrupt(digitalPinToInterrupt(interruptPin), impulsionInterruption, CHANGE);
    }
     
    void loop()
    {
    	if (mesureNouvelle == true)
    	{
    		noInterrupts();
    		topSyncMilliSec = topSyncInterrMilliSec;
    		interrupts();
     
    		Serial.print("\nImpulsion LOW ");
    		Serial.print(topSyncMilliSec);
    		Serial.println(" milliSec.");
     
    		topSyncAction();
     
    		mesureNouvelle = false;
    	}
    }
     
    void impulsionInterruption()
    {
    	byte interruptPinStatus = digitalRead(interruptPin);
     
    	if (interruptPinStatus == LOW && !mesureEnCours) // Si transition HIGHlow et pas de mesure en cours
    	{
    		topSyncInterrDepart= millis();
    		mesureEnCours = true;                               // Mesure en cours
    	}
    	if (interruptPinStatus == HIGH && mesureEnCours) // Si transition lowHIGH et une mesure en cours
    	{
    		topSyncInterrMilliSec = millis()-topSyncInterrDepart;
    		mesureEnCours = false;                               // Plus de mesure en cours
    		mesureNouvelle = true;
    	}
    }
     
     
    void topSyncAction()
    { 
    	if (topSyncMilliSec >= topSyncMinimum)
    	{                                                          // si plus de 800 ms sans interrupt, led allumée 50 ms
    		Serial.println("Top sync.");
    		digitalWrite(topSyncLedPin,HIGH);
    		delay(50);                                             // calibrage niveau haut
    		digitalWrite (topSyncLedPin,LOW);
    	}
     
    	else 
    	{ 
    		Serial.println("Parasite ");
    	}
    }
    J'espère n'avoir rien oublié

    A+
    Cordialement
    jpbbricole

  14. #14
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    Le programme configure la pin d’interruption pour réagir aux fronts montants ou descendants. La routine d’interruption est donc appelée à chaque transition d’état.

    L’idée de l’interruption est de calculer combien de temps a duré le signal LOW et @JPbricole traite cela avec une petite machine à états.

    Quand on rentre dans l’interruption il mémorise dans interruptPinStatus l’état de la pin pour savoir si on est HIGH ou LOW. Comme on est appelé uniquement sur les fronts, si on est LOW c’est qu’il vient de se produire une transition HIGH => LOW et inversement si on est HIGH.

    Comme on veut mesurer le temps passé à LOW, tant que la transition reçue n’est pas HIGH => LOW On ne fait rien. Lorsque on la reçoit on note l’heure de départ en mettant millis() dans topSyncInterrDepart et on met un drapeau à vrai (mesureEnCours ) qui note que ça y est on est en train de mesurer une phase LOW.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     if (interruptPinStatus == LOW && !mesureEnCours) // Si transition HIGHlow et pas de mesure en cours
    	{
    		topSyncInterrDepart= millis();
    		mesureEnCours = true;                               // Mesure en cours
    	}
    Lors de la prochaine interruption on va passer de LOW à HIGH et si une mesure est en cours (ie on a bien noté un moment de départ) alors on calcule la durée de cette phase LOW en soustrayant à l’heure courante la valeur mémorisée précédemment et on met cela dans topSyncInterrMilliSec.

    On a alors fini un cycle de mesure de phase LOW donc on enlève le drapeau mesureEnCours (mis a false) et on active un nouveau drapeau mesureNouvelle à vrai pour signaler a loop qu’une durée calculée est maintenant disponible
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     if (interruptPinStatus == HIGH && mesureEnCours) // Si transition lowHIGH et une mesure en cours
    	{
    		topSyncInterrMilliSec = millis()-topSyncInterrDepart;
    		mesureEnCours = false;                               // Plus de mesure en cours
    		mesureNouvelle = true;
    	}

    Pendant ce temps la loop tourne bêtement à ne rien faire en attendant de voir si une mesure est dispo
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     if (mesureNouvelle == true) ...
    Quand ce drapeau est détecté c’est donc qu’on a une mesure de la durée et la loop effectue un petit affichage cosmétique et appelle topSyncAction().

    Dans cette fonction on teste simplement si la durée est supérieure à 800ms et si oui on allume D13 pendant 50ms puis on l’éteint
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     if (topSyncMilliSec >= topSyncMinimum)
    	{                                                          // si plus de 800 ms sans interrupt, led allumée 50 ms
    		Serial.println("Top sync.");
    		digitalWrite(topSyncLedPin,HIGH);
    		delay(50);                                        
    		digitalWrite (topSyncLedPin,LOW);
    	}
    Si la durée était moindre alors c’est pas une bonne mesure et on l’ignore
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     else { 
    		Serial.println("Parasite ");
    	}
    Une fois topSyncAction() exécutée on retourne dans la loop où on va noter que cette mesure a bien été prise en compte
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     mesureNouvelle = false;
    en remettant le drapeau à faux et donc la loop reprend patiemment son attente active. Le drapeau sera remis à vrai par les interruptions après mesure de la prochaine séquence à LOW et on recommencera

    Ça aide ?

  15. #15
    Membre averti
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mars 2019
    Messages : 64
    Par défaut Décodage code hoaraire 162kh
    Bonjour Jay M et merci d'avoir pris un moment pour me donner ces explications.
    Un petit bonjour aussi à jpbbricole.

    Je vais me "mettre dessus".


    Bonne soirée et à bientôt...

    Pat42

  16. #16
    Membre Expert
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 017
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Retraité des réseaux informatiques
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Février 2013
    Messages : 1 017
    Par défaut
    Bonjour Pat
    Citation Envoyé par Pat42 Voir le message
    Je vais me "mettre dessus".
    C'est vrai, qu'avec une explication comme celle de @Jay M, ça donne envie de "creuser"!

    A+
    Bonne soiré (Sagement à la maison)
    Cordialement
    jpbbricole

  17. #17
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    Citation Envoyé par jpbbricole Voir le message
    C'est vrai, qu'avec une explication comme celle de @Jay M, ça donne envie de "creuser"!
    je n'ai fait que commenter votre boulot - c'est vous qui avez fait le gros du travail !!!

  18. #18
    Membre averti
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Mars 2019
    Messages : 64
    Par défaut 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

  19. #19
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    Vous pouvez reposter tout le code?

  20. #20
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    vous avez déjà fait un gros boulot ! à mon avis faut juste virer ces appels puisqu'ils sont faits dans affichage_I2c() (qu'il suffit d'appeler quand le tableau des bits reçus est plein)

Discussions similaires

  1. TOP 10 sur WeBI
    Par aemag dans le forum Webi
    Réponses: 9
    Dernier message: 21/05/2008, 20h41
  2. Detecter si l'on a toujours la synchro sur son modem
    Par yoghisan dans le forum API, COM et SDKs
    Réponses: 25
    Dernier message: 21/10/2005, 18h09
  3. Top 10 sur plusieurs items
    Par hussard dans le forum Langage SQL
    Réponses: 1
    Dernier message: 03/10/2005, 09h33
  4. Réponses: 3
    Dernier message: 23/08/2005, 09h43
  5. Détection d'un front sur la broche RI d'un port série
    Par G3G3 dans le forum Ordinateurs
    Réponses: 2
    Dernier message: 19/08/2005, 17h14

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo