Mesure de la durée d'un pulse

Bonjour

Je cherche à résoudre (sans succès) le problème suivant. J'espère que quelqu'un sur ce forum pourra m'aider.
J'utilise une ArduinoMega.

Je voudrai mesurer la durée d'un pulse parmi une série de pulses identiques, j'ai plusieurs entrées à gérer "simultanément".

Je souhaitai réaliser un programme à base de routine d'interruption plutôt que de lire en boucle les entrées car d'autres actions sont à réaliser dans la boucle et je ne veux pas rater d'entrées.
J'utilise les interruptions, déclenchées sur le premier changement d'état (qui indique une nouvelle série de pulses) je mesure la durée du prochain pulse avec PulseIn, cela fonctionne. Ce que je ne sais pas faire c'est comment "sortir" de cette routine d'interruption pour libérer le calculateur jusqu'à l'arrivée de la série suivante... En effet comme je dois surveiller plusieurs entrées je ne veux pas rester "bloquer" dans une routine d'interruption et rater une autre interruption (une autre série de pulses).
La durée de la série d'un pulse est non déterminée et peut varier de quelques millisecondes millisecondes à quelques dizaines de millisecondes. La durée minimale d'un pulse est de 64 microsecondes, la durée maximale est de 512 microsecondes. Chaque série peut réapparaitre toutes les 5 à 15 secondes. Il y a en tout 6 entrées à surveiller (voir photo attachée).

Merci de m'aider si vous avez une idée ou des pistes à me suggérer.

Pour information, le projet consiste à identifier le signal (carré) émis par la diode embarquée sur une voiture de circuit électrique digital Carrera. Chaque voiture émet en permanence un signal carré dont la longueur du pulse identifie la voiture (la voiture numéro 1 présente un pulse de 64 microsecondes, la voiture suivante 64 microsecondes + 64 microsecondes et ainsi de suite pour un maximum de 8 voitures). Le signal est lu lors du passage de la voiture sur un photo-transistor placé sur la piste. Une fois l'identification des voitures réglé il sera facile de réaliser un compte-tours et une application de chronométrage.

En dépendance de ce que le reste de ton programme doit encore gérer, tu as les option de faire ceci par polling ou par des interruptions. La version avec le polling est plus simple et peut être plus efficace, mais elle nécessite que ta boucle central boucle suffisamment rapide et n'aie pas de parties qui prennent trop de temps. La version avec interruptions est plus compliquée à programmer et la précision de mesure est toujours dépendant des autres interruptions possibles simultanées. Pour les premiers essais la version avec le polling est préférable à mon avis.

Assumons que ta boucle centrale ne prend pas plus que 30 µs, soit quelques 400 instructions, tu devrais avoir assez de temps pour différencier entre la voiture avec le signal à 64µs et celle à 128µs. Cela devrait suffire comme précision.

Pour lire les entrées, je doute que tu puisses utiliser digitalRead. Une bonne idée est de brancher tous les signaux d'entrée sur broches du même port (par exemple port L, les broches 42 à 49). Cela te permets de lire tous les signaux simultanément en utilisant le registre PINL dans ce cas.

Le fragment de code pour tas boucle sera quelque chose comme:

unsigned long startTime[8];
...
void loop () {
...
  unsigned long isNow = micros();

  static uint8_t lastPinL = 0;
  uint8_t currPinL = PINL;  // Lire toutes les entrées
  unit8_t changed = currPinL ^ lastPinL;
  uint8_t newHigh = changedPinL & currPinL;
  uint8_t newLow = changedPinL & lastPinL;

  // Traiter les signaux qui viennent de commencer
  if (newHigh) {
    for (int8_t i = 0; i < 8; i++) {
      if (newHigh & (1 << i)) {
        startTime[i] = isNow;
      }   
    }
  }

  // Traiter les signaux qui viennent de finir
  if (newLow) {
    for (int8_t i = 0; i < 8; i++) {
      if (newLow & (1 << i)) {
        unsined long duree = isNow - startTime[i];
        // Identifier la voiture à base de la durée etc.
        ...
      }   
    }
  }
  lastPinL = currPinL;  // Sauver valeur pour prochain tour
  ...
}

Ce code n'est, comme toujours, pas testé de tout et peut être optimisé.

Peut-être ça t'aide un peu.

Korman

Juste un problème.... qu'advient t'il si les 8 voitures (ou même simplement 2) passent en même temps avec 0,01/1000 de secondes de décalage entre chaque (c'est purement hypothétique :grin:).

Mon avis est que tu devrais avoir une horloge RTC et autant d'Arduino que de voiture à contrôler, plus un Arduino qui te servirait à centraliser tout ça.
Tu peux faire des standalone pour limiter les couts.
Ainsi la programmation par module serait simplifiée et seul reste un peu compliqué la gestion de la communication et de l'horloge.

Jean-François:
Juste un problème.... qu'advient t'il si les 8 voitures passent en même temps avec 0,01/1000 de secondes de décalage entre chaque (c'est purement hypothétique :grin:).

Tout simple: si les signaux ont commencé dans la même itération de la boucle, les voitures sont passées en même temps. La valeur de startTime te donnes le temps de passage. Mais c'est plutôt hypothétique, comme 10µs à 100km/h sont une différence de 0.28mm.
Korman

Oui, comme je le disais cela reste hypothétique, mais c'est un constat d'une lacune du système envisagé et c'est une question à soulever.

On est pas en F1.... mais dans un grand prix ces 0,28 mm ferait toute la différence, après il faut voir si l'on se contente de cette précision.

Je maintiens que la méthode envisagée n'est pas la bonne.

Combien de temps dure la reconnaissance d'une voiture au moment de son passage ?
Ou plutôt combien de temps met l'Arduino pour être prêt à détecter la voiture suivante lors du passage d'une voiture.
Si les voitures sont trop près l'une de l'autre, la seconde voiture pourrait ne pas être prise en compte.....

C'est toujours hypothétique.... XD

Jean-François:
Oui, comme je le disais cela reste hypothétique, mais c'est un constat d'une lacune du système envisagé et c'est une question à soulevé.
...
Je maintiens que la méthode envisagée n'est pas la bonne.

Jean-François,

si la solution n'est pas assez bonne, il ne reste que de faire la détection par un circuit logique en hard. Et même dans ce cas ci, le problème n'a pas disparut. Comme avec tout les projets il est important de comprendre quelle sont les erreurs acceptables et quelle précision est nécessaire. Si la précision des détecteurs et du montage est inférieure à la précision obtenue par la mesure, la méthode est suffisamment bonne.

Korman

Avec un programme,

Korman:
Comme avec tout les projets il est important de comprendre quelle sont les erreurs acceptables et quelle précision est nécessaire.

C'est bien pour cela que je mentionne cette éventualité :D, maintenant c'est fait, j'ai donné mon avis et ce n'est pas mon projet.... donc ce n'est pas moi qui décide :wink:

Ce qui est amusant c'est que tu ne cites pas ce passage :

Jean-François:
...après il faut voir si l'on se contente de cette précision.

PS. je n'ai toujours pas de réponse, à quelle distance mini doivent être chaque voiture pour que l'Arduino les repère les deux ?

Merci à vous 2 pour les idées et le débat.

Je vais essayer de préciser un peu mieux le contexte. EN théorie 8 voitures peuvent circuler en même temps, en pratique sur un circuit de 10/12m il est difficile de dépasser 5/6 voitures. Le circuit comporte 2 voies seulement et les voitures peuvent changer de voie lors du passage sur un aiguillage et donc se doubler…

Si les 2 voitures à identifier se suivent et se collent, elles passeront sur le détecteur avec un écart d’environ 5 à 10 ms (pour une vitesse de 2m/s à 1m/s). Le cas le plus critique reste celui où 2 voitures circulent sur des voies différentes et franchissent le capteur en même temps celui de droite ou celui de gauche).
Je comprends que la solution de Korman « assure » de na pas rater une voiture en lisant tous les détecteurs simultanément (le temps où une voiture est visible par le capteur est sans doute largement supérieur à 10ms (hypothèse de vitesse 2m/s, surface de capteur 2mm)…
Comme il ne s’agit que d’un jeu je serai satisfait d’une précision de chronométrage du centièmes de secondes voir dans le pire des cas le dixième.

Ce qui me pose problème dans la solution proposée est qu’il me semble que tous les pulses d’une série sont traités à l’identique… Alors qu’un seul pulse est nécessaire pour identifier la voiture (par la durée du pulse), chronométrer le tour et compter les tours. Il faut ignorer les pulse suivants d’une même série (même voiture) dans le process de chronométrage et comptage des tours…
C’est pourquoi je recherche une solution permettant de :

  • détecter une nouvelle série (passage d’une voiture)
  • mesurer la durée d’un pulse (pour identifier la voiture)
  • calculer le temps au tour (durée entre 2 passages)
  • compter les tours (à chaque passage de la même voiture sur un des 2 détecteurs placés sur la ligne d’arrivée)
  • ignorer les pulses suivants
  • attendre l’arrivée d’une nouvelle série

Jean-François, le projet que je développe utilise déjà plusieurs Arduino :

  • une par pilote pour contrôler la vitesse, simuler la gestion du carburant, gérer les arrêts au stand..
  • une unité centrale qui gère le protocole avec les voitures (protocole Carrera)
  • une pour le comptage et le chronométrage (objet de ce post)
  • les bebetes devront communiquer par I2C
    C’est pourquoi je voudrai éviter si possible de rajouter du hard.

En tout cas merci à vous 2, j’espère avoir été plus clair sur mes intentions.
N’hésitez pas à réagir et commenter.javascript:void(0);
Raoul.

Petite question qui me trotte dans la tête; à quelle vitesse roulent au maximum les voitures lorsqu'elles passent devant le compteur ?

Raoul,

mon pseudo-code s'occupe del a parie lecture d'un signal. À la place indiquée par:

// Identifier la voiture à base de la durée etc.
...

tu devras implementer la logique pour identifier la voiture et t'occuper des autres signaux. Une bonne idée serait de vérifier sur 3 ou 4 impulsion si la bonne voiture a été identifié. Au début de la première impulsion tu sauves le temps et après la troisième les tours sont compté etc. Le reste des impulsions de cette durée seraient ignoré pour une demi seconde. Tout simple.

Korman

Raoul6391:
C’est pourquoi je voudrai éviter si possible de rajouter du hard.

Cette suggestion ne vient pas de moi :wink:

Korman:
...si la solution n'est pas assez bonne, il ne reste que de faire la détection par un circuit logique en hard...

Korman.

Merci pour ces précisions.

Il me reste cependant un point qui n'est pas clair pour moi (sans doute par manque de pratique) : je ne vois pas comment "ignorer" les pulses pendant une 1/2 seconde?
Faut-il utiliser un flag indiquant que la voiture a déjà été identifiée puis un timer générant une interruption pour "reseter" le flag en vue de la série suivante ? Il y a potentiellement 5 entrées à surveiller ce qui voudrait dire un maximum de 5 timers (ce qui dépasse je crois la capacité de l'Arduino) ?
Ou bien le traitement (filtrage) doit-il se faire dans la boucle? Mais dans ce cas comment reconnaitre que la série est terminée (c'est à dire ne pas considérer la série suivante comme faisant partie de la série précédente)?

Dans les temps morts (entre 2 identifications) quelques octets sont à envoyer via I2C aux autres Arduino.

J'espère que mes questions ne sont pas trop triviales.
Encore merci.

Raoul.

En principe c'est simple. Tu définies un array contenant chaque voiture et actualise les données que la première fois que tu récupères un signal. Quelque chose comme ça:

// Toute info sur une voiture
const int maxVoiture = 5;
class Voiture {
  public:
    int laps;
    unsigned long lastMeasure;
    unsigned long pulseId;
} course[maxVoiture];
...
void loop() {
...
      if (newLow & (1 << i)) {
        unsined long duree = isNow - startTime[i];
        // Identifier la voiture à base de la durée etc.
        for (int v = 0; v < maxVoiture; v++) {
          if (abs (duree - course[v].pulseId) <=  course[v].pulseId / 4) {
             // La bonne voiture trouvé
             if (startTime[i] - course[v].lastMeasure > 500000) {
               // Premier signal. Ne pas ignorer
               course[v].lastMeasure = startTime[i];
               course[v].laps++; 
             }
             break;
          }
        }
      }   
...
}

Korman

Merci Korman.
Je vais m'y attaquer ce week-end.
Raoul.