Wysoka precyzja synchronizacji w Arduino do komunikacji szeregowej

11

Używam Arduino Uno do wysyłania informacji o czasie i napięciu przez port szeregowy do Pythona w celu wykreślenia. Wydaje się jednak, że odstępy czasu między kolejnymi znacznikami czasu z czasem się zwiększają, co wpływa na moje planowanie. Jest to szczególnie prawdziwe, gdy szybkość transmisji jest ustawiona na 9600, gdzie moje początkowe różnice czasowe mogą wynosić 1320 i wzrastają do 16400 po stosunkowo krótkim czasie. Gdy ta szybkość jest ustawiona na maksymalnie 115200 bps, zmiana jest wolniejsza i mniej zauważalna, od około 1340 do 1500 nawet po stosunkowo długim okresie wysyłania. Wszystkie czasy podawane są w mikrosekundach.

Chciałbym wiedzieć, czy mogę zmniejszyć lub wyeliminować ten efekt, a jeśli nie rozumiem, dlaczego on istnieje. Czytałem już o przerwaniach i opóźnieniach powodujących to, ale nie w pełni doceniam złożoność elektroniki i chciałbym wiedzieć:

  1. Czy mogę uzyskać większą precyzję pomiaru czasu?
  2. Co powoduje tę zmianę czasu?

Oto, co obecnie mam:

#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;
        }
      }
    }
  }
}
użytkownik3284376
źródło
Co rozumiesz przez „precyzyjny”? Czasy podane przez licznik będą dość dokładne, dokładne i z dobrą rozdzielczością. Czy chcesz, aby czasy były deterministyczne (tj. Zawsze takie same)?
Cybergibbons,
Przepraszam, tak, myślę, że o to mi chodziło, aby różnica między nimi była spójna, a jeśli nie, to powód, dla którego nie są
użytkownik3284376
Dodaj znacznik czasu na końcu komputera zamiast Arduino lub użyj modułu RTC (zegar czasu rzeczywistego). Moduły RTC są dość tanie do znalezienia w różnych sklepach internetowych, po prostu upewnij się, że sklep prowadzi do arkusza danych. Inną metodą jest zaprogramowanie timera i użycie procedury obsługi przerwania w celu uzyskania rozsądnie dokładnego pomiaru czasu.
jippie
Co ma eHealth.getECG()zrobić? Czy to połączenie zawsze trwa tyle samo?
jfpoilpret
Czy możesz określić, jak długo trwa „stosunkowo krótki okres”? Czy zawsze jest tak samo po ponownym uruchomieniu Arduino?
jfpoilpret

Odpowiedzi:

4

Użyj timera i ISR ​​(procedura obsługi przerwań), aby zwiększyć dokładność pomiaru czasu.

Spójrz na moje 1ms czasowe przerwanie Proof of Concept . Chodzi o to, aby mieć dość dokładny „puls” 1 ms w systemie, który można wykorzystać do wywołania innych zdarzeń. W PoC służy do mrugania diody LED przy częstotliwości ½Hz, ale ma dostęp do nowych zmiennych millisecondCounteri secondCounterumożliwia wyzwalanie zdarzeń w głównej pętli w dowolnych (ale dokładnie taktowanych) momentach.

jippie
źródło
2
Twój PoC jest bardzo interesujący, ale ma wadę (łatwą do naprawy), ponieważ odczytuje 2-bajtową wartość, podczas gdy przerwania są włączone (in loop()), ta wartość jest modyfikowana przez ISR. Może się zdarzyć, że loop()odczyta złą wartość (w trakcie modyfikacji przez ISR). Zamieściłem na Twój temat komentarz na Twoim blogu.
jfpoilpret
@ jfpoilpret interesujący punkt, który tam robisz, nigdy nie myślałem o przerwaniu występującym w połowie pobierania wartości z pamięci RAM. Dziś wieczorem sprawdzę demontaż i zaktualizuję artykuł. Być może dobry powód, aby napisać również inny artykuł: o)
jippie
Utworzyłem próbkę z twojego PoC i mogłem zobaczyć, że problem występuje co najmniej raz na 10 sekund w moim UNO. Ale oczywiście w rzeczywistości zależy to w dużej mierze od tego, co robisz w swoim loop(): moja próbka właśnie otrzymała wartość milisekund, porównaj ją z poprzednią wartością odczytu i jeśli różnica> 0 (inna niż zerowanie licznika na 0), wyświetl komunikat.
jfpoilpret
@jfpoilpret nigdy tak naprawdę tego nie zauważył. Po prostu używam go jako bicia serca do monitorowania wiader żywności dla moich kotów i robię diodę LED, gdy moje koty będą potencjalnie rozczarowane ...; o) To zdecydowanie zmieni sposób, w jaki używam ISR w przyszłości.
jippie
1
Pokazuje kryształ podłączony do bloku ATMEGA16U2 i rezonator podłączony do ATMEGA328P-PU. 16U2 jest dla interfejsu szeregowego, 328P to „Arduino”. Co ciekawe, 16U2 byłby w stanie przesunąć swój zegar na inny układ, np. 328P.
Udo Klein
3

Mogę wymyślić kilka rzeczy, które mogą wpłynąć na „spójność” szeregów czasowych zapisu:

  • rozmiar danych do wydrukowania

może to być najbardziej oczywista rzecz do przemyślenia, ale w rzeczywistości im więcej drukujesz, tym więcej czasu zajmie ci to.

Rozwiązanie: wydrukuj formatuj ciąg na ciąg o znanej długości.

  • za pomocą buforowanego numeru seryjnego

w systemie Unix można uzyskać dostęp do portu szeregowego w sposób buforowany lub niebuforowany. Używanie buforowanego sposobu przez długi czas może sprawić, że będzie on nieco wolniejszy w miarę zapełniania się bufora, zwykle dzieje się tak, gdy dane przychodzą szybciej niż je czytasz…

Rozwiązanie: użyj niebuforowanej linii szeregowej ( np . : w systemie Darwin / OSX /dev/cu.usbmodemXXXzamiast /dev/tty.usbmodemXXX)

  • priorytet timerów

wygląda na to, że używasz przerwań TC, a AVR mają priorytety w sposobie obsługi przerwań, nie znam kolejności pierwszeństwa dla Atmega328 i nie jest to jedna z najlepiej udokumentowanych funkcji, więc nie wiem jak bezpieczne jest TC0 w porównaniu z przerwaniem UART.

Rozwiązanie: poszukaj dalej w dokumentacji / arkuszu danych dotyczących priorytetów przerwania i w razie potrzeby zmień licznik czasu; i / lub wykonaj test bez uruchamiania drugiego timera.

  • dane, z których czytasz, zajmują więcej czasu

niektóre sterowniki muszą uśredniać lub wykonywać pewne operacje w stosunku do poprzednich wartości, więc im więcej wartości zmierzysz, tym dłuższy jest bufor i im dłużej trwa obliczanie wartości, aż do osiągnięcia maksymalnego rozmiaru bufora.

Rozwiązanie: spójrz na kod źródłowy używanej biblioteki i zoptymalizuj go, usuń obliczenia, jeśli takie istnieją, lub weź pod uwagę rosnący czas przetwarzania.

  • unikając narzutów ramowych arduino

ale jeśli naprawdę chcesz zoptymalizować wyjście szeregowe z arduino, powinieneś unikać korzystania z narzutu arduino… Ale jest o wiele mniej elegancki i wygodny w użyciu.

Jestem prawie pewien, że brakuje mi innych punktów, ale to pierwsze rzeczy, które sprawdziłem przed dalszym kopaniem.

HTH

zmo
źródło
2

Twój kod zawiera czas trwania danych wyjściowych w kolejnych pomiarach. Zatem w zależności od długości wyniku będziesz mierzyć różne czasy. Można to naprawić, formując na wyjście o stałej długości.

Kolejną kwestią jest to, że UNO ma bardzo słabą podstawę czasową. Spójrz tutaj, aby porównać różne typy Arduino z odniesieniem czasowym DCF77.

Wniosek: jeśli potrzebujesz precyzyjnego pomiaru czasu, kup Arduino z kryształem lub wybierz RTC. Mogę bardzo polecić RTC DS3231 / DS3232, ponieważ zazwyczaj osiągają one dokładność 2 ppm po wyjęciu z pudełka.

Udo Klein
źródło