Utrzymywanie czasu Arduino za pomocą millis () nie jest dokładne lub prawidłowe?

9

Używam Arduino do rejestrowania niektórych danych. W moim szkicu Arduino również użyłem tej millis()funkcji, dzięki czemu mogę śledzić czas, w którym pobierana jest każda mierzona wartość. Zauważyłem jednak, że czas nie jest prawidłowy. Na przykład 30 sekund w prawdziwym życiu pojawia się tylko jako 10 sekund (wymyślony przykład).

Czy mam rację mówiąc, że funkcja opóźnienia Arduino wpływa na czas używania millis()? Innymi słowy, załóżmy, że mam opóźnienie 50 ms, czy to oznacza, że millis()funkcja zatrzymuje się również na ten czas, a następnie trwa i tak dalej na czas połączenia? Zauważyłem to, kiedy próbowałem wykreślić jakieś dane i stwierdziłem, że częstotliwość pików w moich danych była zbyt częsta, biorąc pod uwagę upływający czas. Więc chcę wiedzieć, czy to jest przyczyna tego niedopasowania czasowego, a jeśli tak, to jak to naprawić, aby zachować czas każdej próbki?

Aby podać kontekst, oto mój szkic:

#include <eHealth.h>    

unsigned long time;
// The setup routine runs once when you press reset:
void setup() {
  Serial.begin(9600);  
}

// The loop routine runs over and over again forever:
void loop() {

  float ECG = eHealth.getECG();
  time = millis();
  Serial.print(time);
  Serial.print(" ");
  Serial.print(ECG, 5); 
  Serial.println("");    

  delay(50);
}
użytkownik3284376
źródło
Czy korzystasz z jednej z oficjalnych tablic Uno?
Peter Bloomfield,
1
Rzeczywisty czas zamiast skompilowanych wartości (idealny jest monitor szeregowy, który stempluje czasowo linie) prawdopodobnie pomogłby ustalić, co się dzieje.
Ignacio Vazquez-Abrams,
3
Obliczenia millis()są oparte na przerwie, więc delay()nie powinny na to wpływać.
microtherion
Mam ten sam problem, ale tylko wtedy, gdy zintegruję go (millis ()) ze złożonym kodem. Wydaje mi się, że złożoność kodu wpływa na jego dokładność, ponieważ coraz bardziej opóźnia się wraz ze złożonością kodu. Czy jest jakiś sposób, aby tego uniknąć? może używając oddzielnego modułu RTC?
Josip7171

Odpowiedzi:

10

millis()jest sterowany przerwaniami, więc delay()nie wpłynie na to, przynajmniej nie na płycie ATmega.

Nie oznacza to również, że millis()jest to całkowicie dokładne. Każdy tyknięcie timera nie jest dokładnie 1 ms, ale 1,024 ms. Ten błąd stopniowo narasta do momentu dokonania korekty. Można to zobaczyć w implementacji procedury obsługi przerwań TIMER0_OVF (timer 0 overflow).

Innym źródłem niedokładności jest sam oscylator / kryształ, który nie jest dokładnie 16 MHz. Jest jednak dość blisko i dopóki temperatura nie zmienia się za bardzo, jest względnie stabilna.

Powyższe oznacza, że ​​możesz mieć około 1 miliona czasu podczas używania millis(). To nie brzmi jak twój problem.

Innym potencjalnym problemem może być to, co getECG()się dzieje - może być bardzo powolne.

float eHealthClass::getECG(void)
    {
        float analog0;
        // Read from analogic in. 
        analog0=analogRead(0);
        // binary to voltage conversion
        return analog0 = (float)analog0 * 5 / 1023.0;   
    }

analogRead() jest wolny, ale nie tak wolny, aby wpływał na taką pętlę.

Innym problemem, jaki widziałem, jest to, że ludzie zmieniają częstotliwość zegara, ale nie zmieniają poprawnie board.txt. Oznacza to, że stałe użyte w millis()implementacji są niepoprawne, a czasy są nieprawidłowe.

Jeśli rzeczywiście chcesz odczytywać wartości co 50 ms, o wiele lepszym sposobem realizacji tego jest wykonanie następujących czynności

static long lastUpdate;

if (millis() - lastUpdate > 50)
{
    lastUpdate = millis();
    //Do stuff
}

Naprawdę musielibyśmy zobaczyć otrzymywane znaczniki czasu. Jeśli faktycznie widzisz 30s jako 10s, oznacza to, że jest coś jeszcze w pracy.

Cybergibbons
źródło
2
Należy pamiętać, że w przypadku Uno zegar nie jest napędzany kryształami, a jedynie wykorzystuje ceramiczny rezonator, który jest mniej dokładny niż kryształ.
jfpoilpret
@jfpoilpret Ah dobrze wiedzieć. Patrząc na schemat , byłoby to urządzenie Murata CERALOCK CSTCE16M0V53-R0 .
Chris O
Rezonatory mają słabą tolerancję początkową (często 0,5–2%) i niską stabilność temperaturową, ale jeśli skalibrujesz pętle czasowe podczas ich używania, mogą działać dobrze, dopóki temperatura się nie zmieni.
Cybergibbons,
2
Millis () nadal działa na zegarze, który tyka co 1,024 ms, ale dodali kompensację błędów, w postaci zwiększania, gdy zmienna miernika błędów staje się zbyt wysoka. Myślę, że to właściwie algorytm Romana Blacka. Tak więc czas powinien być znacznie bliższy 1 ms. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest
Dla tych, którzy nadal są zainteresowani, zobacz komentarz, który zamieściłem w odpowiedzi JRoberta, nie chciałem odpowiadać własną odpowiedzią, ponieważ nie mam, właśnie sformułowałem problem.
user3284376,
2

Jeśli przerwania są wyłączone na jakikolwiek znaczący ułamek eHealth.getECG()czasu trwania połączenia, millis()liczba może pozostać w tyle. W przeciwnym razie millis()powinien zwrócić znacznie dokładniejszy czas niż trzykrotnie opisane błędy.

Powiedziałeś, że próbkowany sygnał ma wyższą częstotliwość niż się spodziewałeś, co może się zdarzyć, jeśli częstotliwość próbkowania będzie niższa niż zamierzałeś. Czy zakładasz częstotliwość próbkowania 20 Hz? Twoja pętla może zająć nieco więcej niż 50 ms, co można zobaczyć w drukowanych czasach, ale te powinny nadal śledzić czas zegara. Jeśli nie uwzględniłeś tego, ale zakładałeś 50 ms / próbkę, zobaczysz pozorne przyspieszenie danych.

Jeśli to nie jest problem, następnym krokiem byłoby przełączenie wyjścia, gdy jesteś w loop()środku, i zmierzenie częstotliwości wynikowej fali prostokątnej za pomocą miernika częstotliwości (niektóre niedrogie DVM mogą to zrobić) lub zakresu. Zrób to samo z pustym loop(). Pierwszy eksperyment będzie twoją rzeczywistą częstotliwością próbkowania lub interwałem; drugi powie ci, czy millis()(tj. częstotliwość timera0) jest taka, jakiej się spodziewałeś.

JRobert
źródło
1
Bawiłem się nim dalej i zdałem sobie sprawę, że problem nie dotyczy Arduino, funkcja millis () w przeważającej części działa bardzo dobrze, niektóre wartości nie są dokładnie 8 ms (opóźnienie) oprócz tego, co powiedziałeś, że należy się tego spodziewać. Opisany przeze mnie błąd 3x dotyczy strony Pythona rzeczy, których używam do odbierania danych. Jakikolwiek pomysł, co może być wynikiem, używam pyserial Pythona i jest powolny jak diabli.
user3284376,
Nie wiem wystarczająco dużo o twojej implementacji, aby dać ci więcej niż 1/2 @ zgadywania, ale czy strona Pythona jest wystarczająco wolna, aby upuszczać próbki?
JRobert
0

To samo tutaj. Mogę dodać, że jeśli przerwy są wyłączone, mierzony czas to „czas rzeczywisty”. W każdym razie nie rozumiem, dlaczego to opóźnienie, ponieważ jeśli pętla trwa zbyt długo, i tak millis () powinna zwracać wartości w czasie rzeczywistym (tylko z większą odległością między każdą wartością)

użytkownik48711
źródło
1
Co oznacza „to samo tutaj”? Odpowiedzi powinny być samodzielne, ponieważ StackExchange może zmieniać kolejność (w przeciwieństwie do forum). Tak więc „to samo tutaj” może znaczyć wszystko, w zależności od odpowiedzi / pytania, pod którą pojawia się twoja odpowiedź.
Nick Gammon
Ten post byłby bardziej odpowiedni jako komentarz, chociaż (co prawda) brakuje ci wystarczającej reputacji.
Greenonline,
Przepraszam, ale kiedy coś odpowiesz, to oczywiste, że odnosi się to do głównego postu, w przeciwnym razie byłby to komentarz
user48711,