Synchronisation de haute précision sur Arduino pour la communication série

11

J'utilise un Arduino Uno pour envoyer des informations de temps et de tension sur le port série à Python pour tracer. Cependant, les intervalles de temps entre les horodatages successifs semblent augmenter au fil du temps, affectant mon tracé. Cela est particulièrement vrai lorsque le débit en bauds est défini sur 9600, où mes différences de temps initiales peuvent être de 1320 et augmenter à 16400 après une période de temps relativement courte. Lorsque ce taux est mis au maximum à 115200 bps, le changement est plus lent et moins perceptible, d'environ 1340 à 1500, même après un cycle d'envoi relativement long. Toutes les heures sont données en microsecondes.

Je voudrais savoir si je peux réduire ou éliminer cet effet, et sinon comprendre pourquoi il existe. J'ai lu des choses sur les interruptions et les retards provoquant cela, mais je n'apprécie pas pleinement la complexité de l'électronique à portée de main et je voudrais savoir:

  1. Puis-je obtenir une plus grande précision dans le timing?
  2. Qu'est-ce qui cause ce changement de timing?

Voici ce que j'ai actuellement:

#include <eHealth.h>

extern volatile unsigned long timer0_overflow_count;
float fanalog0;
int analog0;
unsigned long time;    

byte serialByte;
void setup() {
  Serial.begin(9600);
}

void loop() { 
  while (Serial.available()>0){  
    serialByte=Serial.read();
    if (serialByte=='S'){        
      while(1){
        fanalog0=eHealth.getECG();  
        // Use the timer0 => 1 tick every 4 us
        time=(timer0_overflow_count << 8) + TCNT0;        
        // Microseconds conversion.
        time=(time*4);   
        //Print in a file for simulation
        //Serial.print(time);
        //Serial.print(" ");
        Serial.print(fanalog0,5);
        Serial.print("\n");

        if (Serial.available()>0){
          serialByte=Serial.read();
          if (serialByte=='F')  break;
        }
      }
    }
  }
}
user3284376
la source
Qu'entendez-vous par «précis»? Les temps donnés par le compteur vont être assez précis, précis et avec une bonne résolution. Voulez-vous que les temps soient déterministes (c'est-à-dire toujours les mêmes)?
Cybergibbons
Toutes mes excuses, oui, je suppose que c'est ce que je voulais dire, pour que la différence entre elles soit cohérente et sinon, la raison pour laquelle elles ne le sont pas
user3284376
Ajoutez l'horodatage à la fin du PC plutôt qu'à la fin de l'Arduino, ou utilisez un module RTC (horloge en temps réel). Les modules RTC sont assez bon marché à trouver sur diverses boutiques en ligne, assurez-vous simplement que le magasin renvoie à la fiche technique. Une autre méthode consiste à programmer une minuterie et à utiliser une routine de service d'interruption pour obtenir une synchronisation raisonnablement précise.
jippie
Que fait eHealth.getECG()-il? Cet appel dure-t-il toujours le même temps?
jfpoilpret
Pouvez-vous spécifier la durée d'une "période relativement courte"? Est-ce toujours le même après le redémarrage de l'Arduino?
jfpoilpret

Réponses:

4

Utilisez une minuterie et un ISR (routine de service d'interruption) pour rendre le chronométrage plus précis.

Jetez un œil à ma preuve de concept d'interruption temporisée de 1 ms . L'idée est d'avoir un rythme cardiaque de 1 ms raisonnablement précis dans le système qui peut être utilisé pour déclencher d'autres événements. Dans le PoC, il est utilisé pour faire clignoter une LED à ½ Hz, mais ayant accès aux nouvelles variables millisecondCounteret secondCountervous permet de déclencher des événements dans la boucle principale à des moments arbitraires (mais précisément synchronisés).

jippie
la source
2
Votre PoC est très intéressant mais il a un défaut (facile à corriger) dans le fait qu'il lit une valeur de 2 octets alors que les interruptions sont activées (en loop()), cette valeur étant modifiée par un ISR. Il peut arriver que se loop()lit une mauvaise valeur (au milieu d'une modification par l'ISR). J'ai publié un commentaire à ce sujet sur votre blog.
jfpoilpret
@jfpoilpret point intéressant que vous faites là, jamais pensé à une interruption survenant à mi-chemin pour récupérer la valeur de la RAM. Je vais vérifier le démontage ce soir et mettre à jour l'article. Peut-être une bonne raison d'écrire aussi un autre article: o)
jippie
J'ai créé un échantillon à partir de votre PoC et j'ai pu voir le problème se produire au moins une fois toutes les 10 secondes sur mon UNO. Mais bien sûr, en réalité, cela dépend fortement de ce que vous faites dans votre loop(): mon échantillon vient d'obtenir la valeur en millisecondes, la comparer à la valeur de lecture précédente et si la différence> 0 (autre que réinitialiser le compteur à 0), afficher un message.
jfpoilpret
@jfpoilpret ne l'a jamais vraiment remarqué. Je l'utilise juste comme un battement de coeur pour surveiller les seaux de nourriture pour mes chats et faire un flash LED lorsque mes chats seront potentiellement déçus ...; o) Cela changera certainement la façon dont j'utiliserai les ISR à l'avenir.
jippie
1
Il montre un cristal connecté à un bloc ATMEGA16U2 et un résonateur connecté à ATMEGA328P-PU. Le 16U2 est pour l'interface série, le 328P est "L'Arduino". Il est intéressant de noter que le 16U2 pourrait pousser son horloge vers une autre puce, par exemple le 328P.
Udo Klein
3

Je peux penser à quelques éléments qui peuvent avoir un impact sur la "cohérence" des horaires d'écriture en série:

  • taille des données à imprimer

c'est peut-être la chose la plus évidente à laquelle penser, mais en effet, plus vous imprimez, plus il vous faudra pour le gérer.

Solution: imprimez le format de la chaîne en une chaîne de longueur connue.

  • en utilisant une série tamponnée

sous unix, vous pouvez accéder au port série en utilisant une méthode tamponnée ou non tamponnée. L'utilisation de la méthode tamponnée pendant une longue période peut ralentir un peu le remplissage du tampon, cela se produit généralement lorsque les données arrivent plus rapidement que vous ne les lisez…

Solution: utilisez la ligne série sans tampon ( par exemple : sur Darwin / OSX c'est à la /dev/cu.usbmodemXXXplace de /dev/tty.usbmodemXXX)

  • priorité des chronomètres

il semble que vous utilisiez une interruption TC, et les AVR ont des priorités dans la façon dont les interruptions sont gérées, je ne connais pas l'ordre de priorité pour l'Atmega328, et ce n'est pas l'une des fonctionnalités les plus documentées, donc je ne sais pas la sécurité de TC0 par rapport à l'interruption UART.

Solution: cherchez plus loin dans la documentation / fiche technique sur les priorités d'interruption et modifiez la minuterie si nécessaire; et / ou faire un test sans que l'autre minuterie ne fonctionne.

  • les données que vous lisez prennent plus de temps à lire au fil du temps

certains pilotes doivent faire la moyenne ou effectuer certaines opérations sur les valeurs précédentes, donc plus vous mesurez de valeurs, plus le tampon est long et plus il faut de temps pour calculer la valeur, jusqu'à ce que vous ayez atteint la taille maximale du tampon.

Solution: regardez le code source de la bibliothèque que vous utilisez et optimisez-le, supprimez le calcul s'il y en a un ou tenez compte de ce temps de traitement croissant.

  • éviter les frais généraux du cadre Arduino

mais si vous voulez vraiment optimiser la sortie série de l'arduino, vous devriez éviter d'utiliser la surcharge de l'arduino… Mais c'est beaucoup moins élégant et confortable à utiliser.

Je suis sûr qu'il y a d'autres points qui me manquent, mais ce sont les premières choses que je vérifierais avant de creuser davantage.

HTH

zmo
la source
2

Votre code inclut la durée de la sortie dans les mesures suivantes. Ainsi, en fonction de la longueur de la sortie, vous mesurerez différents moments. Cela peut être corrigé en formatant sur une sortie de longueur fixe.

Le problème suivant est que l'ONU a une très mauvaise base de temps. Jetez un œil ici pour une comparaison des différents types d'Arduino par rapport à la référence temporelle DCF77.

Conclusion: si vous avez besoin d'un timing précis, procurez-vous un Arduino avec cristal ou optez pour un RTC. Je peux fortement recommander les RTC DS3231 / DS3232 car ceux-ci atteignent généralement une précision de 2 ppm hors de la boîte.

Udo Klein
la source