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

  1. #1
    Membre à l'essai
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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
    Points : 23
    Points
    23
    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 émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    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
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  3. #3
    Membre à l'essai
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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
    Points : 23
    Points
    23
    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 à l'essai
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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
    Points : 23
    Points
    23
    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 émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    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
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  6. #6
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    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
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  7. #7
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    Avril 2002
    Messages
    3 190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    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 190
    Points : 11 573
    Points
    11 573
    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é.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  8. #8
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    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é)

  9. #9
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    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
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  10. #10
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    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.

  11. #11
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    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
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  12. #12
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    salut JP

    partagé avec plaisir

    juste dans topSyncAction() j'avais viré
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Serial.print(topSyncMilliSec);
    Serial.println(" milliSec.");
    digitalWrite(topSyncLedPin,LOW);
    car la LED est forcément LOW et vous appelez cette fonction depuis la loop() où vous aviez déjà affiché justement topSyncMilliSec, donc pas la peine de répéter pour garder l'affichage "simple".

    et dans l'interruption j'avais proposé un else entre les deux if histoire de gagner en performance puisque les 2 conditions ne peuvent pas être vraies en même temps.

    mais oui c'est cohérent et vous avez bien intégré la zone critique pour capturer la valeur.

  13. #13
    Membre à l'essai
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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
    Points : 23
    Points
    23
    Par défaut Décodage code hoaraire 162kh
    Bonjour Jpbbricole, à Jay M et à tous ceux qui participent à l'avancement de mon projet "mon horloge à moi".

    Merci pour ton bout de code qui fonctionne parfaitement.
    Un pb de résolu .
    Je joins une photo.

    Si j'ai bien compris, c'est le premier front montant après 800ms de "silence" qui génère l'interruption.

    Je voulais faire un peu plus compliqué en encadrant le top 1°sec. par une impulsion calibrée (50ms), puis appliquer
    un "ET" à la porte et au top 1°sec, et ainsi immuniser le système aux parasites qui pourraient déclencher l'interrupt,
    avant le top du début 1°sec.

    Mais après des tests, l'antenne à côté d'une belle alimentation à découpage, le filtrage tient le coup et pas de souci.

    Je vais me pencher sur ton programme pour bien le comprendre et apprendre... (j'aurais éventuellement besoin d'aide pour bien comprendre)

    Il faut ensuite que je me penche sur les secondes et la lecture des bits, ouvrir une porte de de 200ms après un temps de 800ms au début de chaque seconde.

    j'ai du temps...confinement oblige.

    Il est certain que j'aurais encore besoin de vos précieuses aides.

    Cordialement
    Pat42

    Ps: Etant débutant, je ne suis pas rentré dans votre discussion avec Jay M, lol.
    J'ai testé le prg que tu as modifié et c'est ok, bien que je n'ai évidemment pas tout compris de ce qu'il apporte en plus...

    Peux tu m'expliquer la raison pour laquelle il y a deux bouts de code identique ligne 17 et 19 ?
    pinMode(topSyncLedPin, OUTPUT

    Cordialement
    Pat42
    Images attachées Images attachées  

  14. #14
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    Par défaut
    Citation Envoyé par Pat42 Voir le message
    Peux tu m'expliquer la raison pour laquelle il y a deux bouts de code identique ligne 17 et 19 ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pinMode(topSyncLedPin, OUTPUT);
    c'est parce que @JP voulait voir si vous suiviez

    (bug de copier coller sans doute en pratique, ça ne sert bien sûr à rien)

  15. #15
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    Par défaut
    Bonjour Pat
    Citation Envoyé par Pat42 Voir le message
    Si j'ai bien compris, c'est le premier front montant après 800ms de "silence" qui génère l'interruption.
    Non pas tout à fait, le premier flanc descendant (début du silence) déclenche le chrono topSyncInterrDepart et le prochain flanc montant, l'arrête et on capture le temps dans topSyncMilliSec. Ensuite, on évalue si topSyncMilliSec est >= à topSyncMinimum pour décider si parasite ou pas.

    Citation Envoyé par Pat42 Voir le message
    Peux tu m'expliquer la raison pour laquelle il y a deux bouts de code identique ligne 17 et 19 ?
    pinMode(topSyncLedPin, OUTPUT
    Un oubli tu peux en supprimer une des deux.

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

  16. #16
    Membre à l'essai
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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
    Points : 23
    Points
    23
    Par défaut Décodage code horaire 162kh
    Merci pour vos réponses, il est 23h37 et je suis encore en train de me casser la tête pour comprendre la routine appelée par l'interruption
    void impulsionInterruption().

    Il y a quelques mois, j'ai écrit un programme pour un fréquencemètre à installer sur certaines de mes radios ( j'écoute les ondes courtes, cela sert) avec multiplexage de cinq afficheurs 7 segments , utilisé le timer 1 et 2,fait des soustractions, des divisions, des "réglages" joué avec les modulos et avec votre aide sur les timers, je m'en suis bien sorti.

    Eh là! j'ai quelques difficultés à comprendre le programme que vous m'avez gentiment écrit.

    Je suis perplexe.

    Perdrais-je des neurones...?


    Pat42

  17. #17
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    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 ?

  18. #18
    Membre à l'essai
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Mars 2019
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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
    Points : 23
    Points
    23
    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

  19. #19
    Membre émérite
    Avatar de jpbbricole
    Homme Profil pro
    Retraité des réseaux informatiques
    Inscrit en
    Février 2013
    Messages
    1 012
    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 012
    Points : 2 341
    Points
    2 341
    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
    L'expérience est la seule chose qu'il ne faut acheter que d'occasion!

  20. #20
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 711
    Points : 5 390
    Points
    5 390
    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 !!!

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