Высокая точность синхронизации на Arduino для последовательной связи


11

Я использую Arduino Uno для отправки информации о времени и напряжении через последовательный порт на Python для построения графика. Однако временные интервалы между последовательными метками времени, по-видимому, со временем увеличиваются, что сказывается на моем графике. Это особенно верно, когда скорость передачи данных установлена ​​на 9600, где моя начальная разница во времени может быть 1320 и увеличивается до 16400 после относительно короткого периода времени. Когда эта скорость установлена ​​на максимум 115200 бит / с, изменение происходит медленнее и менее заметно, с 1340 до 1500, даже после относительно долгой отправки. Время указывается в микросекундах.

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

  1. Могу ли я получить большую точность в сроках?
  2. Что вызывает это изменение во времени?

Вот что у меня сейчас есть:

#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;
        }
      }
    }
  }
}

Что вы подразумеваете под "точным"? Время, указанное счетчиком, будет достаточно точным, точным и с хорошим разрешением. Вы хотите, чтобы время было детерминированным (то есть всегда одинаковым)?
Cybergibbons

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

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

Что делает eHealth.getECG()? Этот звонок всегда длится одинаковое количество времени?
jfpoilpret

Можете ли вы указать, как долго длится «относительно короткий период времени»? Это всегда то же самое после перезапуска Arduino?
jfpoilpret

Ответы:


4

Используйте таймер и ISR (процедуру обработки прерываний), чтобы сделать синхронизацию более точной.

Взгляните на мое доказательство концепции прерывания по времени 1 мс . Идея состоит в том, чтобы в системе было достаточно точное «сердцебиение» в 1 мс, которое можно использовать для запуска других событий. В PoC он используется для мигания светодиода с частотой 1/2 Гц, но имеет доступ к новым переменным millisecondCounterи secondCounterпозволяет запускать события в основном цикле в произвольные (но точно синхронизированные) моменты.


2
Ваш PoC очень интересен, но у него есть недостаток (его легко исправить) в том, что он читает 2-байтовое значение, в то время как прерывания включены (в loop()), причем это значение изменяется ISR. Может случиться так, что loop()читается неверное значение (в середине модификации ISR). Я разместил комментарий в вашем блоге об этом.
jfpoilpret

@jfpoilpret интересный момент, который вы делаете, никогда не задумываясь о прерывании, происходящем на полпути при извлечении значения из RAM. Я собираюсь проверить разборку этим вечером и обновить статью. Может быть, хорошая причина написать еще одну статью: о)
Джиппи

Я создал образец из вашего PoC и мог видеть, что проблема возникает по крайней мере раз в 10 секунд в моем UNO. Но, конечно, в действительности это сильно зависит от того, что вы делаете в своем loop(): мой пример только что получил значение в миллисекундах, сравните его с предыдущим значением чтения и, если разница> 0 (кроме сброса счетчика в 0), отобразите сообщение.
jfpoilpret

@jfpoilpret никогда не замечал этого. Я просто использую его как сердцебиение, чтобы контролировать ведра еды для моих кошек и сделать светодиодную вспышку, когда мои кошки будут потенциально разочарованы ...; o) Это определенно изменит способ, которым я буду использовать ISR в будущем.
Джиппи

1
На нем изображен кристалл, подключенный к блоку ATMEGA16U2, и резонатор, подключенный к ATMEGA328P-PU. 16U2 для последовательного интерфейса, 328P - "Arduino". Интересно, что 16U2 сможет передавать свои часы другому чипу, например, 328P.
Удо Кляйн

3

Я могу подумать о нескольких вещах, которые могут повлиять на «согласованность» времени последовательной записи:

  • размер данных для распечатки

Это может быть самой очевидной вещью, о которой нужно думать, но на самом деле, чем больше вы печатаете, тем больше вам понадобится справиться с этим.

Решение: напечатайте формат строки в строку известной длины.

  • используя буферизованный сериал

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

Решение: используйте небуферизованную последовательную линию ( например , /dev/cu.usbmodemXXXвместо Darwin / OSX /dev/tty.usbmodemXXX)

  • приоритет таймеров

похоже, что вы используете прерывание TC, и AVR имеют приоритеты в обработке прерываний, я не знаю порядок приоритетов для Atmega328, и это не одна из самых документированных функций, поэтому я не знаю насколько безопасен TC0 по сравнению с прерыванием UART.

Решение: посмотрите далее в документации / таблице данных о приоритетах прерываний и при необходимости измените таймер; и / или выполнить тест, не запустив другой таймер.

  • данные, из которых вы читаете, со временем читают больше

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

Решение: посмотрите на исходный код библиотеки, которую вы используете, и либо оптимизируйте ее, удалите калькуляцию, если она есть, или учтите это увеличивающееся время обработки.

  • избегая накладных расходов на платформе Arduino

но если вы действительно хотите оптимизировать последовательный вывод от Arduino, вам следует избегать использования накладных расходов Arduino ... Но это менее элегантно и удобно в использовании.

Я почти уверен, что есть другие моменты, которые мне не хватает, но это первое, что я хотел бы проверить, прежде чем копать дальше.

НТН


2

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

Следующая проблема заключается в том, что у ООН очень плохое время. Посмотрите здесь для сравнения различных типов Arduino и отсчета времени DCF77.

Вывод: если вам нужно точное время, возьмите Arduino с кристаллом или отправляйтесь на RTC. Я настоятельно рекомендую RTC DS3231 / DS3232, поскольку они обычно достигают точности 2 промилле из коробки.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.