Время Arduino, использующее millis (), не является точным или правильным?

9

Я использовал Arduino для записи некоторых данных. В моем наброске Arduino я также использовал millis()функцию, чтобы я мог отслеживать время, в которое берется каждое измеряемое значение. Тем не менее, я заметил, что выбор времени не правильный. Например, 30 секунд в реальной жизни выходят как 10 секунд (выдуманный пример).

Правильно ли я сказал, что функция задержки Arduino влияет на время использования millis()? Другими словами, предположим, что у меня задержка 50 мс. Означает ли это, что millis()функция также останавливается на эту продолжительность, а затем продолжается и так далее на время соединения? Я заметил это, когда попытался построить некоторые данные и обнаружить, что частота пиков в моих данных была слишком частой, учитывая прошедшее время. Итак, я хочу знать, является ли это причиной такого несоответствия времени, и если да, то как я могу это исправить, чтобы я мог сохранить время каждого образца?

Чтобы дать некоторый контекст, вот мой набросок:

#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);
}
user3284376
источник
Вы используете одну из официальных досок Uno?
Питер Блумфилд
1
Фактическая синхронизация вместо выдуманных значений (идеальным является последовательный монитор с метками времени линий), вероятно, поможет выяснить, что происходит.
Игнасио Васкес-Абрамс
3
Вычисление millis()управляется прерыванием, поэтому delay()не должно влиять на него.
микротерион
У меня та же проблема, но только когда я интегрирую ее (millis ()) в сложный код. Я предполагаю, что сложность кода влияет на его точность в том смысле, что он все больше и больше задерживается из-за сложности кода. Есть ли способ избежать этого? может быть, используя отдельный модуль RTC?
Josip7171

Ответы:

10

millis()управляется прерыванием, поэтому delay()не будет влиять на него, по крайней мере, на плате на базе ATmega.

Это не значит, что millis()это абсолютно точно. Каждый такт таймера не точно равен 1 мс, но равен 1,024 мс. Эта ошибка постепенно накапливается, пока не будет произведена коррекция. Это можно увидеть в реализации обработчика прерываний TIMER0_OVF (переполнение таймера 0).

Другим источником неточностей является сам осциллятор / кристалл, который не совсем 16 МГц. Это довольно близко, хотя, и пока температура не меняется слишком сильно, она относительно стабильна.

Вышесказанное означает, что вы можете быть около 1 мс при использовании millis(). Это не похоже на твою проблему.

Другой потенциальной проблемой может быть то, что getECG()происходит, - это может быть очень медленно.

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() медленный, но не такой медленный, чтобы воздействовать на такую ​​петлю.

Еще одна проблема, с которой я сталкивался у людей, это когда они меняют тактовую частоту, но неправильно меняют board.txt. Это означает, что константы, используемые в millis()реализации, неправильные, а времена неправильные.

Если вы действительно хотите читать значения каждые 50 мс, гораздо лучший способ реализовать это - сделать следующее

static long lastUpdate;

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

Нам действительно нужно увидеть метки времени, которые вы получаете. Если вы на самом деле видите 30-е годы, которые показывают как 10-е, значит, на работе есть что-то еще.

Cybergibbons
источник
2
Обратите внимание, что для Uno часы не работают на кристалле, а просто используют керамический резонатор, который менее точен, чем кристалл.
jfpoilpret
@jfpoilpret Ах, приятно знать. Если посмотреть на схему , то это будет CSTCE16M0V53-R0 Murata CERALOCK .
Крис О
Резонаторы имеют плохую начальную погрешность (часто 0,5-2%) и плохую температурную стабильность, но если вы калибруете циклы синхронизации при их использовании, они могут быть в порядке, пока температура не меняется.
Cybergibbons
2
Millis () по-прежнему работает с таймером, который тикает каждые 1,024 мс, но они добавили компенсацию ошибок в форме приращения всякий раз, когда переменная измерителя ошибок становится слишком высокой. Я думаю, что это алгоритм Романа Блэка на самом деле. Так что время должно быть намного ближе к 1 мс точно. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest
Для тех, кто все еще заинтересован, смотрите комментарий, который я разместил в ответе JRobert, я не хотел отвечать своим собственным ответом, поскольку у меня его нет, я только перефразировал проблему.
user3284376
2

Если прерывания отключаются в течение какой-либо значительной eHealth.getECG()длительности вызова, millis()счет может отставать. В противном случае millis()должно возвращаться гораздо более точное время, чем 3х ошибочные, которые вы описали.

Вы сказали, что ваш сэмплированный сигнал имеет более высокую частоту, чем вы ожидали, что может произойти, если ваша частота сэмплирования ниже, чем вы предполагали. Вы предполагаете частоту дискретизации 20 Гц? Ваш цикл может занимать чуть больше 50 мс, что вы увидите в распечатанном времени, но оно все равно должно отслеживать время. Если вы не учли это, но предположили 50 мс / сэмпл, вы бы увидели очевидное ускорение данных.

Если это не проблема, то следующим шагом будет переключение выхода, когда вы находитесь loop(), и измерение частоты результирующей прямоугольной волны с помощью измерителя частоты (некоторые недорогие DVM могут сделать это) или «область действия». Сделайте то же самое с пустым loop(). Первый эксперимент будет вашей реальной частотой дискретизации или интервалом; вторая скажет вам, является ли millis()(то есть, частота таймера 0) тем, что вы ожидали.

JRobert
источник
1
Я поиграл с этим дальше и понял, что проблема не в Arduino, функция millis () по большей части работает очень хорошо, некоторые значения находятся не совсем на расстоянии 8 мс (задержка), но от того, что Вы сказали, что это следовало ожидать. Ошибка 3x, которую я описал, верна для Python, который я использовал для получения данных. Любая идея, к чему это может привести, я использую Pyserial Python, и он медленный, как ад.
user3284376
Я не знаю достаточно о вашей реализации, чтобы дать вам больше, чем 1/2 @ думаю, но достаточно ли медленна сторона Python, чтобы отбрасывать сэмплы?
JRobert
0

Тоже самое. Я могу добавить, что если прерывания отключены, измеренное время является «реальным временем». Во всяком случае, я не понимаю, почему эта задержка, потому что если цикл занимает слишком много времени, в любом случае millis () должен возвращать значения в реальном времени (только с большим расстоянием между каждым значением)

user48711
источник
1
Что означает «то же самое здесь»? Ответы должны стоять самостоятельно, так как StackExchange может переупорядочивать вещи (в отличие от форума). Так что «то же самое здесь» может означать что угодно, в зависимости от того, под каким ответом / вопросом находится ваш ответ.
Ник Гэммон
Этот пост будет более уместным в качестве комментария, хотя (по общему признанию) вам не хватает репутации.
Greenonline
Извините, хотя, когда вы что-то отвечаете, очевидно, что это ссылка на основной пост, в противном случае это будет комментарий
user48711