Acquisition signal carré pour déclencher une interruption

Bonjour à tous

j'ai réalisé de un montage me permettant d'afficher la rotation d'un axe
J'utilise un détecteur de proximité inductif LJ12A3-2-Z/BX NPN normalement ouvert (NO) connecter sur la pin digitale 2 avec une résistance de 10K connecter sur le 5v (PullUp )
J'utilise les interruptions sur D2

void setup() 
{
  pinMode(pinCapteur, INPUT);             
  attachInterrupt(digitalPinToInterrupt(pinCapteur), onEvent,FALLING);
}

void loop() 
{
  //Affichage Tr/Rouleau
  switch (flag)
  {
    case 2:
      rpm = 60000000 /(currentMicros - previousMicros);
      //Affichage PC
      Serial.print(F("i"));
      Serial.println(rpm);
      previousMicros = currentMicros; 
      flag = 1;
      break;
  }
  
  
}

void onEvent()
{
  currentMicros = micros();
  flag = 2;
}

Cette partie fonctionne correctement, les valeurs sont cohérentes mais ce n'est peut être pas la bonne méthode .

Maintenant j'essaye d'acquérir la rotation d'un moteur thermique via le fil de bougie avec un capteur normalement
[Interface régime moteur digitale Shop-Racing, Régime Moteur, Capteurs MOTO - Shop-racing]
Je devrais récupérer un signal carré Vmin=0v, Vmax=Tension alimentation du capteur (12v pour ma part).
Je l'ai simplement mis a la place du détecteur de proximité mais cela ne fonctionne, mes compétences s'arrête la.

J'espère que mon post est assez explicite et de trouver la direction pour mes recherches futures.
Cordialement
Pascal

Ton arduino (quel modèle ?) ne va du tout aimer une tension supérieure à sa tension d'alim... (5V ? 3,3 V ?)

Il faut mettre un pont diviseur à l'entrée.

Effectivement

Dans cette partie du code tu lis 2 fois currentMicro. Donc potentiellement 2 valeurs différentes. L'erreur est sans doute minime mais elle est là.
Il vaudrait mieux faire quelque chose comme ça.

      noInterrupts();
      temps = currentMicros;
      interrupts()
      rpm = 60000000 /(temps - previousMicros);
      //Affichage PC
      Serial.print(F("i"));
      Serial.println(rpm);
      previousMicros = temps; 
      flag = 1;
      break;

Ainsi tu utilises le même temps dans tout ton code. De plus le blocage des interruptions pendant la lecture de la variable currentMicros garantie qu'elle ne sera pas modifiée pendant sa lecture, il y a 4 octets à récupérer et rien ne te protège d'une modification de son contenu pendant la lecture des 4 octets.

le code est incomplet, il faut aussi poster la déclaration des variables (volatile ?)

si votre loop est trop lente par rapport au RPM vous aurez deux ou plus interruptions potentiellement et votre calcul sera faux puisque vous n'allez considérer qu'il n'y a eu qu'un seul tour. il faudrait aussi enregistrer le nombre de ticks que vous avez eu depuis le dernier passage (au lieu du flag par exemple ce serait un compteur)

Bonjour;

Sans ton premier montage avec un capteur inductif de type NPN comme celui là

En sortie capteur, au lieu d'avoir un relais à piloter, tu t'es relié à l'entrée arduino à laquelle il y a une résistance de PULLUP.

C'est tout à fait correct.

Mais dans le 2éme cas, je ne crois pas que tu puisse remplacer une entrée NPN par un signal carré 0-12V
Premiérement car 12V c'est trop pour une entrée 0-5V, comme le dit @J-M-L , il faut mettre 1 pont diviseur
Deuxiémement, la résistance de PULLUP me semble inutile voir même elle risque de fausser le résultat du pont diviseur.

RAJOUT:

J'oubliais, il faut aussi relier les 0V de l'arduino et du capteur, sinon ça ne marche pas.

Bonjour
Déjà un grand merci pour vos réponses.

Professeur Mephisto
Mon Arduino est une UNO alimenter par le port USB. Je le mesure a 4v avec mon voltmetre
Je vais rechercher la formule pour calculer la valeur des résistances pour le pont diviseur.

fdufnews
Effectivement j'avais de temps en temps des décrochages je vais tester avec ton code

JML
Effectivement je n'ai publier tous mon code

const byte pinCapteur = 2;                    
volatile unsigned long previousMicros = 0;
volatile unsigned long currentMicros = 0;
volatile short flag =1 ; 
long rpm;

Mais pour le moment je ne capte aucun signal

Jef59
Effectivement la tension a la sortie de mon capteur RPM est de 9.2v je vais mettre un pont et
j'ai bien relier les 0V de l'arduino et du capteur.

Je vais essayer tous cela et je reviens vers vous
Bonne journée

En théorie la formule est
Vb = Va * (R1 / (R2 + R1))
Avec Va la tension à diviser, et Vb la tension souhaitée < ou = 5V

Je trouve étonnant une tension 5V USB = 4V!
Je n'ai jamais eu le cas dans mes montages en alimentant que un seul Arduino en 5V USB.

PS:
Ne pas choisir R1+R2 trop grands ni trop petits, R1+R2 autour de 10k Ohms c'est bien.

Bonsoir
D'abord un grand merci, en suivant vos conseilles j'ai appris quelques chose ce qui est pour moi primordiale et mon montage fonctionne.
Au niveau capteur j'avais une tension de 9.5v, j'ai mis un pont diviseur avec deux résistances de 5.6 kohms et j'ai retiré la résistances de pull up .
J'ai mis en parralléle un compte tour du commerce et j'obtiens les mêmes valeurs.

Dans un précédent message j'avais préciser que ma carte arduino sortait 4v branché en USB mais j'avais pris la mesure sur une breakboard, en rélalité elle sort 4.6v aux pins de la carte.
Est ce normal car j'ai l'impression que mon voltmètre est un peu défaillent même dans la mesure d'une résistance.

Encore Merci
Pascal

Les multimètres, même basiques, sont généralement d'une fiabilité tout à fait convenable, du moins pour nos usages de bidouilleurs. Par contre ils sont parfois sensibles à la décharge de leur pile : les deux modèles que j'utilise couramment dans mon collège font des erreurs de l'ordre de 10 à 15 % (un en excès, l'autre en défaut) quand la pile 9V commence à faiblir et avant même que le voyant de décharge ne s'allume pour l'un des deux modèles.

Bonsoir;

Tu peut éventuellement vérifier en mesurant une résistance et en vérifiant si ta mesure est conforme à la valeur théorique en tenant compte de la tolérance de ta résistance (indiquée par le code des couleurs)


Et si ta valeur mesurée par ton voltmètre est dans les tolérances, il y a des chances qu'il ne soit pas trop faux.

Sinon, comme dit plus haut, ca peut être la pile.

(j'ose pas te demander si tu fais bien ta mesure en DC!)

Bonjour à tous.

Pour faire suite j'ai changé la pile et ma mesure est resté la même.
Quand je mesure une résistance de 39 Kohms avec une tolérance de 5% j'obtiens une valeur de 38.8 Kohms.
Cette arduino est pas très récente, elle a peut-être mal vieillit.

Mes deus capteurs fonctionne parfaitement mais séparement, j'ai voulu les faire fonctionner en même temps et j'ai quelques soucis de décrochage.
Pensez vous que je doit créer un nouveau topic ou je peux continuer celui la

Bonne journée
Pascal

Bonjour,

Pour lever définitivement le doute, tu peux aussi mesurer le 3.3V de ta carte Arduino, il est fabriqué indépendemment du 5V.

Quand tu écris:

Qu'est ce que cela veut dire STP?
Comment connecte tu les 2 capteurs?
Qu'est ce qui décroche?
Quel code?

Bonjour
Effectivement je mesure bien 3.3v et 4.6v au lieu des 5v sur ma carte arduino.
Je vais tester une autre carte pour voir mais la mienne est très vieille et au début j'ai peut-être fait n'importe quoi.

Voila ou j'en suis
image

const byte pinEngineRpm = 3;                  //Rpm

volatile unsigned long previousRMicros = 0;
volatile unsigned long currentRMicros = 0;
volatile unsigned long previousEMicros = 0;
volatile unsigned long currentEMicros = 0;
volatile short flag =1 ; 
long rpmRoller;
long rpmEngine;

void setup() 
{
  //Initialisation liaison serie
  Serial.begin(9600);
  //Initialisation Capteur
  pinMode(pinRollerRpm, INPUT);
  pinMode(pinEngineRpm, INPUT);
  attachInterrupt(digitalPinToInterrupt(pinRollerRpm), onEventRoller,FALLING);             
  attachInterrupt(digitalPinToInterrupt(pinEngineRpm), onEventEngine,FALLING);
}

void loop() 
{
  switch (flag)
  {
    case 2:
      rpmRoller = 60000000 /(currentRMicros - previousRMicros);
      Serial.print(F("r"));
      Serial.println(rpmRoller);
      previousRMicros = currentRMicros; 
      flag = 1;
      break;
    
    case 3:
      rpmEngine = 60000000 /(currentEMicros - previousEMicros);
      Serial.print(F("e"));
      Serial.println(rpmEngine);
      previousEMicros = currentEMicros; 
      flag = 1;
      break;
  }
}

void onEventRoller()
{
  noInterrupts();
  currentRMicros = micros();
  flag = 2;
  interrupts();
}

void onEventEngine()
{
  noInterrupts();
  currentEMicros = micros();
  flag = 3;
  interrupts(); 
}
saisissez ou collez du code ici

Je récupère ces données avec un programme en C#
Je vais créer une base pour enregistrer ces datas pour pouvoir mieux analyser.
Ce système fonctionne bien si je fais tourner les axes séparément.
Mais si ils tournent en même temps je perd des infos sur les deux canaux(mes compteurs retombent a zéro ou se bloquent a 20000trs/mn.
Mais je pourrais être plus précis avec les datas enregistrés.
Bonne journée
Pascal

Bonjour,

Sauf erreur de ma part, interdire puis autoriser toutes les interruptions dans une méthode d'interruption est inutile, voire dangereuse

Déjà, je supprimerais les appels à noInterrupts() et interrupts() dans ces 2 méthodes de traitement d'une interruption...

Bonjour,

1/ Je ne sais plus par quoi vous alimentais votre Arduino, sur votre schémas qui chez moi est trés peu lisible, je crois deviner que c'est par l'USB.

Si c'est le cas, ne cherchez pas plus pour votre 4,6V ou lieu de 5.

Si vous alimentiez votre Arduino par Vin avec du 12V par exemple, vous auriez sûrement 5V.

2/ pour votre code, comme dit plus haut, je ne vois pas quelles broches vous utilisez, D2 et D3 je présume?

vous n'en déclarez qu'une dans

Est-ce que votre code marche?

Je ne vois pas non plus le "case 1:", je ne dis pas que c'est faux, mais je ne sais pas si c'est juste.

Pour votre façon de gérer les interruptions, c'est juste je crois, il me semble que c'est ainsi que les experts d'ici conseillent de faire.
Pour ne pas avoir une demande d'interuption qui arrivent pendant le traitement d'une interruption.

C'est quoi SVP les 2 circuits 3 pattes que vous utilisez, avez vous une ref a proposer?

Par défaut sur avr les interruptions ne sont pas actives quand vous êtes déjà dans une interruption donc ce n’est pas nécessaire de les désactiver c’est déjà le cas,

On peut réactiver les interruptions au sein d’une interruption mais il faut bien comprendre ce que l’on fait, la notion de réentrance etc

Le plus simple c’est de laisser l’interruption se gérer toute seule, elle désactive et réactive les interruptions pour vous

Ce qui ne convient pas c’est l’absence de section critique dans la loop

Bonsoir
Sur la doc d'attachInteppupt il est écrit

Une ISR n’est pas interrompue par une nouvelle interruption. La nouvelle interruption ne sera prise en compte que lorsque l’ISR en cours se terminera.

J'avais juste qu'a lire , désolé..

Dans mon code il manquait

const byte pinRollerRpm = 2;                  //Rouleau PIN 2 OUT

Si il manque le Case 1 c'est pour un rajout ultérieur.

@jef59 Ce sont les deux capteurs du début du post et l'arduino est bien alimenté par usb

@J-M-L

Ce qui ne convient pas c’est l’absence de section critique dans la loop

Si c'est un traitement des erreurs je n'ai aucune idée sur la façon de procéder.

non dans la loop vous faites

➜ vous touchez donc deux fois currentRMicros qui est une variable qui tient sur 32 bits. Pour calculer rpmRoller ou même mettre à jour la variable previousRMicros votre arduino va nécessiter plusieurs cycles d'horloge (il travaille sur 8 bits) et rien ne vous dit que malheureusement une interruption ne va pas venir modifier currentRMicros en plein milieu de ces opérations

un exemple pour montrer ce qu'il peut se passer.
imaginez que currentRMicros vaut 4294967040 (soit 0xFFFFFF00)
votre code en est à effectuer previousRMicros = currentRMicros ce qui va se faire en 4 étapes pour copier les 4 octets de currentRMicros dans la mémoire de previousRMicros.

imaginons que le premier octets de poids fort ait été copié (0xFF) et que vous receviez l'interruption. currentRMicros est alors modifié et comme on a fait le rollover disons qu'il faut maintenant 0x000000FF

après l'interruption la copie des 3 octets restant continue et vous vous retrouvez avec previousRMicros qui vaut 0xFF0000FF qui n'a rien à voir ni avec celui d'origine 0xFFFFFF00 ni avec le nouveau 0x000000FF.
➜ lors du prochain calcul de RPM vous aurez une valeur erronée et si vous prenez des décision par rapport à la vitesse par exemple vous risquez de prendre une mauvaise décision.

conclusion : lorsque l'on travaille sur des variable partagées avec les interruptions, il ne suffit pas de les déclarer volatile pour que tout soit bien (volatile dit juste au compilateur d'utiliser les octets en RAM et pas un cache dans un registre lors des accès en lecture ou écriture). Il faut aussi s'assurer que la variable ne change pas pendant des opérations ➜ on dit qu'il faut conserver l'atomicité de l'opération.

Un moyen simple pour traiter cela et de faire une copie en section critique (protégée des interruptions) des variables concernées. Sur des architectures comme l'AVR on peut soit utiliser les macros dédiées au ATOMIC_BLOCK soit simplement bloquer les interruptions le temps de la copie. dans votre cas par exemple on ferait

void loop() 
{
  switch (flag)
  {
    case 2:
      { // <== On a besoin des accolades car on définit une nouvelle variable au sein du case
        // DEBUT SECTION CRITIQUE.
        noInterrupts();
        unsigned long copieCurrentRMicros = currentRMicros; // on effectue une copie
        interrupts();
        // FIN SECTION CRITIQUE

        // ensuite on utilise la copie
        rpmRoller = 60000000 /(copieCurrentRMicros - previousRMicros);
        Serial.print(F("r"));
        Serial.println(rpmRoller);
        previousRMicros = copieCurrentRMicros; 
        flag = 1;
        break;
      } <== 
    
    case 3:
    •••

sur des architectures plus complexes comme un ESP32 multicore, on utilisera des sémaphores / mutex

Il n'y a qu'une impulsion par tour pour chacun des capteurs?

c'est ce que j'évoquais ici