Проблема дрейфа таймера Arduino

У меня есть два Arduino, которые обмениваются данными через последовательный порт. Каждое Arduino использует алгоритм TDMA, в котором каждому Arduino назначается один временной интервал. Ардуино должны передавать во время своего временного интервала, что работает нормально. Однако со временем, скажем, через 10-15 минут, ардуины начинают перекрывать свои временные интервалы. Чтобы сохранить время, я использую функцию millis(), чтобы определить, когда отправлять сообщение. Я пробовал как delay(), так и ручную настройку счетчиков, но получаю такое же дрейфующее поведение. На одной из итераций кода у меня была функция Serial.readStringUntil(), которая вызывалась во время каждого временного интервала, и Arduino оставалась синхронизированной более 13 часов без обнаруженного дрейфа с точным временным интервалом 1000 мс. Теперь я знаю, что задержка, которую я наблюдал, вызвана параметром тайм-аута, который по умолчанию равен 1000 мс, в функции readStringUntil, но не имеет смысла то, что при такой задержке Arduino не будет дрейфа. Я знаю, что стабильность керамического резонатора на каждом ардуино вызовет дрейф.

Почему функции delay(), millis() и ручная настройка timer1 вызывают дрейф, а функция тайм-аута функции readStringUntil, по-видимому, не дрейфует?

Кроме того, есть ли лучший способ настроить функции синхронизации, цель которого состоит в том, чтобы узлы работали как можно дольше без необходимости повторной синхронизации?

Следующий псевдоним показывает, как в настоящее время настроено время.

count = 0;

if(millis() - previous_action >= TIME_SLOT) {
   count++;
   if(correct timeslot) {
       TRANSMIT();
   }
   previous_action = millis();
   loop_back_to_check;
}

ИЗМЕНИТЬ: RTC и другие устройства, которые могут поддерживать синхронизацию устройств, не входят в рамки проекта.

, 👍0

Обсуждение

с каким устройством общаются ардуино?, @jsotola

@jsotola они отправляют сообщения получателю, который собирает пакеты и печатает в файл., @roberthayek

Какой тип Ардуино вы используете? Он тактируется кристаллом или керамическим резонатором?, @Edgar Bonet

Подобно @NickS ниже. Измените алгоритм TDM, работающий на обоих устройствах, чтобы передачи также можно было использовать для синхронизации устройств. Например, если каждая передача начинается ровно через 100 мс во временном интервале, другая сторона может настроить свои часы так, чтобы они совпадали. В этом случае следующее полное второе обновление должно произойти через 900 мс., @6v6gt


3 ответа


Лучший ответ:

3

Любые платные часы сбиваются. Что еще хуже: его скорость дрейфа не будет постоянный. Это факт жизни, и с этим ничего не поделаешь. это. Не существует программного решения, хотя ваше программное обеспечение, безусловно, может сделать ситуацию хуже. Что вы можете сделать, это:

  1. Синхронизируйте часы обоих Arduino с внешним источником времени. Источник времени не должен быть точным, если вы заботитесь только о относительный дрейф между ардуино. Это может быть приемник сообщения, отправляющие подтверждение в определенное время, или тот же приемник отправляет импульс по выделенной линии или NTP-серверу если ваши Arduino подключены к сети, или сигнал времени радиовещания, или GPS-приемник...

  2. Используйте часы, которые настолько хороши, что их дрейф становится неактуальным. скорость дрейфа должна быть меньше, чем допуск синхронизации вашего TDMA протокол, разделенный на ожидаемый срок службы ваших устройств. Хороший RTC может дать вам скорость дрейфа менее 2 частей на миллион (т.е. 2×10−6). Если этого недостаточно, вы можете рассмотреть OCXO (управляемый печью кварцевый осциллятор) или атомные часы ( на основе рубидия самые доступные).

  3. Выполните калибровку часов. Поскольку вариации скорости дрейфа обычно намного меньше, чем средняя скорость дрейфа, вы можете удалить большинство дрейфа, сначала измерив его, а затем удалив в программном обеспечении значение, которое вы измерили. Затем вы остаетесь со случайными вариациями скорость дрейфа («частотный дрейф»). Я предлагаю вам прочитать Arduino точность тактовой частоты, чтобы получить представление о типе точность, которую вы можете ожидать.

  4. Используйте контроль температуры: держите часы при постоянной температуре, т.к. колебания температуры часто являются основной причиной дрейфа частоты. В качестве альтернативы используйте температурную компенсацию: измерьте скорость дрейфа при нескольких температурах, провести непрерывную кривую через эти измерений и использовать эту кривую (и непрерывную температуру измерения), чтобы исправить дрейф в программном обеспечении.

  5. Не делайте хуже с вашим программным обеспечением: обновите previous_action с

    previous_action += TIME_SLOT;
    

    вместо того, чтобы назначать ему millis(), так как этот подход гарантированно приведет к дрейфу.

Почему [...] функция тайм-аута функции readStringUntil вроде не дрейфует?

Никто не может ответить на этот вопрос без тщательного изучения обоих конкретная версия программного обеспечения, которое вы использовали, и физическая установка. Это может быть, вы случайно внедрили механизм синхронизации. Возможно, ваши измерения были недостаточно точными, чтобы выявить дрейф.

,

1

Вы можете использовать один из множества модулей RTC (часы реального времени), они управляются кристаллом, а ваш Arduino — нет. Разновидностей несколько, выбирайте понравившуюся. Существует большая разница в стабильности между синхронизацией Arduino и синхронизацией RTC. Вы можете регулярно обновлять часы. Использование NTP (протокол сетевого времени), который был бы очень точным и надежным. Вы можете регулярно выполнять NTP на одном устройстве и обновлять остальные. Дополнительное преимущество: они не будут сильно дрейфовать при изменении температуры.

,

См. редактирование. Цель этого состоит в том, чтобы избежать RTC и использовать только внутренние часы Arduino для сохранения времени устройства., @roberthayek

@roberthayek, тогда похоже, что вам придется расширить свой алгоритм TDMA и заставить два устройства генерировать накладные расходы и, возможно, сделать одно «сервером», а другое «клиентом» - клиентское устройство будет выполнять все планирование и серверное устройство должно будет настроить свои временные интервалы на основе информации, предоставленной клиентом. Два устройства имеют разные часы и, следовательно, разные дрейфы, вы действительно не можете просто исправить это., @Nick S.


1

Согласно моему опыту, это клиенты, которые должны следовать за синхронизацией сервера. На стороне клиента:

  1. При каждой передаче на сервер вычисляйте ETA (расчетное время прибытия и ATA (фактическое время прибытия) сообщения) на основе расписания вашего временного интервала и соответствующим образом корректируйте местные часы.
  2. Было бы неплохо рассматривать все передачи как сообщения синхронизации, независимо от адресата, просто игнорируя полезную нагрузку.
  3. Очевидно, что время клиента должно немного увеличиваться, если часы сервера немного быстрее.
  4. В случае длительных периодов бездействия сервер должен периодически передавать сообщение о времени, чтобы ошибка была сведена к минимуму.
  5. Было бы удобно добавить "преамбулу времени" в каждой передаче.

Учитывая все это, функция readStringUntil станет вашим другом во многих отношениях. Дождавшись преамбулы синхронизации, вы можете легко определить ошибку синхронизации. Ожидая известного постамбула, вы читаете "поздно" сообщений (не забудьте значение времени ожидания!).

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

Несмотря на то, что это гибкий коммуникационный носитель, я бы рекомендовал такую преамбулу:

$FF $00 $FF $00 AA BB CC DD EE $FF $FF FF с указанием PAYLOAD, где:

AA = 1 байт, адрес получателя, $00 = широковещательный

BB = 1 байт, тип сообщения (необязательно, $00 для сообщения синхронизации)

CC = 1..2 байта, номер сообщения. к этому назначению.

DD = 1..2 (3?) байта, общий номер сообщения, независимо от адресата.

EE = 1 байт, длина полезной нагрузки, если переменная. (EE<$FF, $00 за сообщение о времени)

FF = 1 байт контрольной суммы (необязательно). (Почему вы должны размещать его ПОСЛЕ "$FF
$FF" сочетание?)

Используйте время, когда вы получаете "$FF $FF" комбинация в качестве метки времени для расчета ATA & Расчетное время прибытия. (Здравый смысл относится к вашим значениям так, чтобы эта комбинация не отображалась до конца преамбулы)

Рассмотрите возможность использования постамбулы с избыточной информацией & Метка конца сообщения.

,