Высокая точность синхронизации на 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.