Menjaga waktu Arduino menggunakan millis () tidak akurat atau benar?

9

Saya telah menggunakan Arduino untuk merekam beberapa data. Dalam sketsa Arduino saya, saya juga menggunakan millis()fungsi tersebut sehingga saya dapat melacak waktu di mana setiap nilai yang saya ukur diambil. Namun, saya perhatikan waktunya tidak tepat. Misalnya 30 detik dalam kehidupan nyata hanya keluar sebagai 10 detik (contoh dibuat-buat).

Apakah saya benar mengatakan bahwa fungsi penundaan Arduino memengaruhi penggunaan waktu millis()? Dengan kata lain seandainya saya memiliki keterlambatan 50 ms, apakah itu berarti millis()fungsi berhenti untuk durasi itu juga dan kemudian berlanjut dan seterusnya selama durasi koneksi? Saya perhatikan ini ketika saya mencoba merencanakan beberapa data dan menemukan bahwa frekuensi puncak dalam data saya terlalu sering mengingat waktu yang telah berlalu. Jadi saya ingin tahu apakah itu alasan ketidakcocokan waktu ini dan jika demikian, bagaimana cara memperbaikinya sehingga saya dapat menjaga waktu setiap sampel terjadi?

Untuk memberikan beberapa konteks di sini adalah sketsa saya:

#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);
}
pengguna3284376
sumber
Apakah Anda menggunakan salah satu papan Uno resmi?
Peter Bloomfield
1
Waktu aktual dan bukan nilai-nilai yang dibuat (monitor serial yang memberi cap waktu pada garis ideal) mungkin akan membantu mencari tahu apa yang terjadi.
Ignacio Vazquez-Abrams
3
Perhitungannya millis()adalah interrupt driven, jadi delay()seharusnya tidak mempengaruhinya.
microtherion
Saya memiliki masalah yang sama tetapi hanya ketika saya mengintegrasikannya (millis ()) ke dalam kode yang kompleks. Saya kira kompleksitas kode mempengaruhi keakuratannya dalam hal keterlambatan dengan kompleksitas kode. Apakah ada cara untuk menghindari hal ini? mungkin menggunakan modul RTC yang terpisah?
Josip7171

Jawaban:

10

millis()didorong interupsi sehingga delay()tidak akan berdampak, setidaknya tidak pada papan berbasis ATmega.

Itu tidak berarti itu millis()juga benar-benar akurat. Setiap centang timer tidak tepat 1 ms, tetapi 1.024 ms. Kesalahan ini berakumulasi secara bertahap sampai koreksi dilakukan. Ini dapat dilihat dalam implementasi dari interrupt handler TIMER0_OVF (timer 0).

Sumber ketidakakuratan lain adalah osilator / kristal itu sendiri, yang tidak persis 16MHz. Itu cukup dekat, dan selama suhu tidak berubah terlalu banyak, relatif stabil.

Di atas berarti bahwa Anda mungkin keluar sekitar 1 ms saat menggunakan millis(). Ini tidak terdengar seperti masalah Anda.

Masalah potensial lainnya getECG()adalah apa yang sedang dilakukan - mungkin sangat lambat.

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() lambat, tetapi tidak terlalu lambat untuk memengaruhi loop seperti ini.

Masalah lain yang saya lihat orang miliki adalah ketika mereka mengubah kecepatan jam tetapi tidak dengan benar mengubah boards.txt. Ini berarti bahwa konstanta yang digunakan dalam millis()implementasi salah dan waktunya salah.

Jika Anda benar-benar ingin membaca nilai setiap 50 ms, cara yang lebih baik untuk mengimplementasikan ini adalah dengan melakukan hal berikut

static long lastUpdate;

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

Kami benar-benar perlu melihat cap waktu yang Anda dapatkan. Jika Anda benar-benar melihat 30-an ditampilkan sebagai 10-an, maka ada sesuatu yang lain sedang bekerja.

Cybergibbons
sumber
2
Harap dicatat bahwa, untuk Uno, jam tidak digerakkan oleh kristal, tetapi hanya menggunakan resonator keramik yang kurang akurat daripada kristal.
jfpoilpret
@ jfpoilpret Ah senang tahu. Melihat skema , ini akan menjadi perangkat CSTCE16M0V53-R0 Murata CERALOCK .
Chris O
Resonator memiliki toleransi awal yang buruk (sering 0,5-2%) dan stabilitas suhu yang buruk, tetapi jika Anda mengkalibrasi loop waktu ketika menggunakannya, mereka bisa baik-baik saja selama suhu tidak bergerak.
Cybergibbons
2
Millis () masih bekerja pada timer yang berdetak setiap 1,024 ms, tetapi mereka menambahkan kompensasi kesalahan, dalam bentuk bertambah setiap kali variabel meter kesalahan menjadi terlalu tinggi. Saya pikir sebenarnya algoritma Roman Black. Jadi waktunya harus jauh lebih dekat ke 1 ms tepatnya. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest
Bagi mereka yang masih tertarik, lihat komentar yang saya posting di jawaban JRobert, saya tidak ingin menjawab dengan jawaban saya sendiri karena saya tidak punya jawaban, saya baru saja mengulangi masalahnya.
user3284376
2

Jika interupsi dimatikan untuk eHealth.getECG()durasi panggilan fraksi yang signifikan , millis()hitungan bisa tertinggal. Kalau tidak, millis()seharusnya mengembalikan waktu yang jauh lebih akurat daripada kesalahan 3x yang Anda gambarkan.

Anda mengatakan sinyal sampel Anda muncul dalam frekuensi yang lebih tinggi daripada yang Anda harapkan, yang bisa terjadi jika laju sampel Anda lebih rendah dari yang Anda inginkan. Apakah Anda mengasumsikan laju sampel 20Hz? Perulangan Anda mungkin memakan waktu agak lebih lama dari 50 ms, yang akan Anda lihat pada waktu dicetak, tetapi itu masih harus melacak waktu jam. Jika Anda tidak memperhitungkannya tetapi mengasumsikan 50 ms / sampel, Anda akan melihat kecepatan jelas dari data.

Jika ini bukan masalahnya, maka langkah selanjutnya adalah beralih output ketika Anda berada di loop(), dan mengukur frekuensi gelombang persegi yang dihasilkan dengan pengukur frekuensi (beberapa DVM murah dapat melakukan ini) atau cakupan. Lakukan hal yang sama dengan yang kosong loop(). Eksperimen pertama adalah laju atau interval sampling nyata Anda; yang kedua akan memberi tahu Anda apakah millis()(mis., frekuensi timer0) adalah yang Anda harapkan.

JRobert
sumber
1
Saya telah mempermainkannya lebih jauh dan menyadari bahwa masalahnya bukan pada Arduino, fungsi millis () sebagian besar berfungsi dengan sangat baik, beberapa nilai tidak persis 8ms (delay) terpisah tetapi dari apa Anda telah mengatakan bahwa itu diharapkan. Kesalahan 3x yang saya jelaskan benar pada sisi Python yang saya gunakan untuk menerima data. Tahu apa itu akibatnya, saya menggunakan pyserial Python dan lambat sekali.
user3284376
Saya tidak cukup tahu tentang implementasi Anda untuk memberi Anda lebih dari 1/2 @ 'd tebak, tetapi apakah sisi Python cukup lambat untuk menjatuhkan sampel?
JRobert
0

Sama disini. Saya dapat menambahkan bahwa jika gangguan dimatikan, waktu yang diukur adalah "waktu nyata". Lagi pula, saya tidak mengerti mengapa penundaan ini, karena jika loop terlalu lama, bagaimanapun juga millis () harus mengembalikan nilai waktu nyata (hanya dengan jarak yang lebih jauh antara setiap nilai)

pengguna48711
sumber
1
Apa yang dimaksud dengan "sama di sini"? Jawaban harus berdiri sendiri, karena StackExchange dapat memesan ulang hal-hal (tidak seperti forum). Jadi "sama di sini" dapat berarti apa saja tergantung pada jawaban / pertanyaan apa yang muncul di bawahnya.
Nick Gammon
Posting ini akan lebih tepat sebagai komentar, meskipun (harus diakui) Anda kurang memiliki reputasi yang cukup.
Greenonline
Maaf, saya pikir ketika Anda menjawab sesuatu, itu jelas merujuk ke pos utama, kalau tidak itu akan menjadi komentar
user48711