Hochpräzises Timing auf Arduino für die serielle Kommunikation

11

Ich verwende ein Arduino Uno, um Zeit- und Spannungsinformationen über die serielle Schnittstelle an Python zu senden, um sie zu zeichnen. Die Intervallzeiten zwischen aufeinanderfolgenden Zeitstempeln scheinen jedoch mit der Zeit zuzunehmen, was sich auf meine Darstellung auswirkt. Dies gilt insbesondere dann, wenn die Baudrate auf 9600 eingestellt ist, wobei meine anfänglichen Zeitunterschiede möglicherweise 1320 betragen und nach relativ kurzer Zeit auf 16400 ansteigen. Wenn diese Rate auf maximal 115200 Bit / s eingestellt wird, ist die Änderung langsamer und weniger auffällig, von etwa 1340 auf 1500, selbst nach einer relativ langen Sendezeit. Alle Zeiten sind in Mikrosekunden angegeben.

Ich würde gerne wissen, ob ich diesen Effekt reduzieren oder eliminieren kann und wenn ich nicht verstehe, warum er existiert. Ich habe Dinge über Interrupts und Verzögerungen gelesen, die dies verursachen, aber ich schätze die Komplexität der vorhandenen Elektronik nicht ganz und möchte wissen:

  1. Kann ich das Timing präziser gestalten?
  2. Was verursacht diese zeitliche Änderung?

Folgendes habe ich derzeit:

#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
quelle
Was meinst du mit "präzise"? Die vom Zähler angegebenen Zeiten werden ziemlich genau, genau und mit einer guten Auflösung sein. Möchten Sie, dass die Zeiten deterministisch sind (dh immer die gleichen)?
Cybergibbons
Entschuldigung, ja, ich denke, das habe ich gemeint, damit der Unterschied zwischen ihnen konsistent ist und wenn nicht, der Grund, warum sie es nicht sind
user3284376
Fügen Sie den Zeitstempel am PC-Ende und nicht am Arduino-Ende hinzu oder verwenden Sie ein RTC-Modul (Real Time Clock). RTC-Module sind in verschiedenen Webstores recht günstig zu finden. Stellen Sie lediglich sicher, dass der Store mit dem Datenblatt verknüpft ist. Eine andere Methode besteht darin, einen Timer zu programmieren und eine Interrupt-Serviceroutine zu verwenden, um ein einigermaßen genaues Timing zu erhalten.
Jippie
Was macht eHealth.getECG()das Dauert dieser Anruf immer genauso lange?
Jfpoilpret
Können Sie angeben, wie lange ein "relativ kurzer Zeitraum" dauert? Ist es nach dem Neustart des Arduino immer dasselbe?
Jfpoilpret

Antworten:

4

Verwenden Sie einen Timer und ISR (Interrupt Service Routine), um das Timing genauer zu gestalten.

Schauen Sie sich meinen 1ms zeitgesteuerten Interrupt Proof of Concept an . Die Idee ist, einen einigermaßen genauen 1-ms-Herzschlag im System zu haben, der zum Auslösen anderer Ereignisse verwendet werden kann. Im PoC wird es verwendet, um eine LED mit ½ Hz zu blinken, hat jedoch Zugriff auf die neuen Variablen millisecondCounterund secondCounterermöglicht es Ihnen, Ereignisse in der Hauptschleife zu beliebigen (aber genau zeitgesteuerten) Zeitpunkten auszulösen.

Jippie
quelle
2
Ihr PoC ist sehr interessant, weist jedoch einen Fehler auf (der leicht zu beheben ist), da er einen 2-Byte-Wert liest, während Interrupts aktiviert sind (in loop()), wobei dieser Wert von einem ISR geändert wird. Es kann vorkommen, dass loop()ein schlechter Wert gelesen wird (mitten in einer Änderung durch den ISR). Ich habe einen Kommentar dazu in Ihrem Blog gepostet.
jfpoilpret
@jfpoilpret interessanter Punkt, den Sie dort machen, nie an einen Interrupt gedacht, der auf halbem Weg auftritt, um den Wert aus dem RAM abzurufen. Ich werde heute Abend die Demontage überprüfen und den Artikel aktualisieren. Vielleicht ein guter Grund, auch einen anderen Artikel zu schreiben: o)
Jippie
Ich habe ein Beispiel aus Ihrem PoC erstellt und konnte feststellen, dass das Problem auf meiner UNO mindestens alle 10 Sekunden auftritt. Aber in Wirklichkeit hängt es natürlich stark davon ab, was Sie in Ihrem loop()Beispiel tun : Mein Beispiel hat gerade den Millisekundenwert erhalten, vergleicht ihn mit dem vorherigen Lesewert und zeigt eine Meldung an, wenn die Differenz> 0 ist (außer den Zähler auf 0 zurückzusetzen).
jfpoilpret
@jfpoilpret hat es nie wirklich bemerkt. Ich benutze es nur als Herzschlag, um die Futtereimer für meine Katzen zu überwachen und eine LED zu blinken, wenn meine Katzen möglicherweise enttäuscht werden ...; o) Es wird definitiv die Art und Weise ändern, wie ich ISRs in Zukunft verwende.
Jippie
1
Es zeigt einen Kristall, der mit einem Block ATMEGA16U2 verbunden ist, und einen Resonator, der mit ATMEGA328P-PU verbunden ist. Der 16U2 ist für die serielle Schnittstelle der 328P ist "The Arduino". Interessanterweise könnte der 16U2 seine Uhr auf einen anderen Chip schieben, z. B. den 328P.
Udo Klein
3

Ich kann mir einige Dinge vorstellen, die die "Konsistenz" der seriellen Schreibzeiten beeinflussen können:

  • Größe der auszudruckenden Daten

Dies mag am offensichtlichsten sein, aber je mehr Sie drucken, desto mehr wird es dauern, um damit umzugehen.

Lösung: Drucken Sie die Zeichenfolge in eine Zeichenfolge bekannter Länge.

  • mit gepufferter serieller

Unter Unix können Sie gepuffert oder ungepuffert auf die serielle Schnittstelle zugreifen. Wenn Sie den gepufferten Weg für längere Zeit verwenden, wird er möglicherweise etwas langsamer, wenn sich der Puffer füllt. In der Regel geschieht dies, wenn Daten schneller eingehen als Sie sie lesen.

Lösung: Verwenden Sie die ungepufferte serielle Leitung ( z. B. unter Darwin / OSX /dev/cu.usbmodemXXXanstelle von /dev/tty.usbmodemXXX)

  • Priorität der Timer

Es sieht so aus, als würden Sie einen TC-Interrupt verwenden, und AVRs haben Prioritäten in der Art und Weise, wie Interrupts behandelt werden. Ich kenne die Prioritätsreihenfolge für den Atmega328 nicht und es ist keine der am meisten dokumentierten Funktionen, daher weiß ich es nicht Wie sicher ist TC0 gegenüber dem UART-Interrupt?

Lösung: Informieren Sie sich in der Dokumentation / im Datenblatt über Interrupt-Prioritäten und ändern Sie den Timer bei Bedarf. und / oder einen Test durchführen, ohne dass der andere Timer läuft.

  • Das Lesen der Daten, aus denen Sie lesen, dauert mit der Zeit länger

Einige Treiber müssen die vorherigen Werte mitteln oder einige Operationen ausführen. Je mehr Werte Sie messen, desto länger ist der Puffer und desto länger dauert die Berechnung des Werts, bis Sie die maximale Größe des Puffers erreicht haben.

Lösung: Sehen Sie sich den Quellcode der Bibliothek an, die Sie verwenden, und optimieren Sie ihn entweder, entfernen Sie den Kalkül, falls vorhanden, oder berücksichtigen Sie diese zunehmende Verarbeitungszeit.

  • Vermeidung des Arduino-Framework-Overheads

Wenn Sie jedoch die serielle Ausgabe des Arduino wirklich optimieren möchten, sollten Sie die Verwendung des Arduino-Overheads vermeiden. Die Verwendung ist jedoch weitaus weniger elegant und komfortabel.

Ich bin mir ziemlich sicher, dass mir noch andere Punkte fehlen, aber das sind die ersten Dinge, die ich überprüfen würde, bevor ich weiter grabe.

HTH

zmo
quelle
2

Ihr Code enthält die Dauer der Ausgabe in nachfolgenden Messungen. Je nach Länge der Ausgabe messen Sie also unterschiedliche Zeiten. Dies kann durch Formatieren auf eine Ausgabe mit fester Länge behoben werden.

Das nächste Problem ist, dass die UNO eine sehr schlechte Zeitbasis hat. Schauen Sie sich hier einen Vergleich verschiedener Arduino-Typen mit der DCF77-Zeitreferenz an.

Fazit: Wenn Sie ein genaues Timing benötigen, besorgen Sie sich entweder ein Arduino mit Kristall oder eine RTC. Ich kann DS3231 / DS3232-RTCs nur empfehlen, da diese normalerweise sofort eine Genauigkeit von 2 ppm erreichen.

Udo Klein
quelle