Использование функции micros()

Black_Cat
Offline
Зарегистрирован: 27.06.2015

Доброго времени суток!

Стоит задача определить момент наступления двух разных событий на двух разных платах Arduino Nano 3.0, и затем понять произошли эти события в одно и тоже время или же в разное. Надо сказать, что события периодически повторяются и значения периода можно удалить без влияния на результат измерения. Короче говоря, нужно сделать прибор, который определяет подключены две коробки с платами Arduino к одной фазе 220В или это разные фазы.

Идея была следующая: сначала определить момент наступления события (момент перехода через нуль) на одной плате с помощью функции micros(), далее передать это значение по радиоканалу RF24 на другую плату. На другой плате определить момент наступления события и вычесть из него принятое значение, получив таким образом значение, из которого можно вычесть лишнее число периодов. И получить либо нуль - если фазы совпадают, либо не нуль - и определить по полученному числу сдвиг фазы. Да, ещё необходимо в начале вычислить разницу между часами на разных платах, ведь функция micros() отсчитывает время от момента включения. Для этого используется функция синхронизации: по проводу от одной платы Arduino к другой передаётся импульс и фиксируется время на каждой плате в момент передачи импульса, после чего эта дельта используется в формуле при вычислении. Используется именно проводное соединение для синхронизации, так как задержка при передачи по цифровому радиоканалу RF24 может достигать 5-10 мс, при этом период регистрируемого сигнала 20 мс.

Так вот, при использовании функции синхронизации было замечено, что время дельты постоянно различается.

После чего был проведён следующий эксперимент: производится фиксация времени на первой плате, отправка импульса (перепада 0-1), приём имульса и фиксация времени на второй плате, далее эксперимент повторяется через 1 секунду. Разница между временами на первой и второй платах растёт с каждым измерением на 800 мкс. То есть получается, что через 1000 секунд разница времени между платами составит 0,8 секунды. Неужели, это нормально??

Скетч первой платы:

...
unsigned long Time_Fix = 0;
int cnt = 1;

void loop(void)
{
    // Начальная задержка
    delay(2000); 
   
    while (true)
    {
    digitalWrite(PIN_TEST_TX, HIGH);
    Time_Fix = micros();
    delay(10);
    digitalWrite(PIN_TEST_TX, LOW);
    
    Serial.print(cnt);
    Serial.print(" / Time = ");
    Serial.println(Time_Fix);
    
    delay(900);
    cnt++;
    }
}

Результат для первой платы:

1 / Time = 2907648
2 / Time = 3818200
3 / Time = 4728756
4 / Time = 5639316
5 / Time = 6549872
6 / Time = 7460424
7 / Time = 8370980
8 / Time = 9281532
9 / Time = 10192100
10 / Time = 11102712

Скетч второй платы:

...
unsigned long Time_Fix = 0;
int cnt = 1;

void loop(void)
{
  // Начальная задержка
  delay(500); 
  
  while (true)
  {   
    while (digitalRead(PIN_TEST_RX) == LOW);
    Time_Fix = micros();
         
    Serial.print(cnt);
    Serial.print(" / Time = ");
    Serial.println(Time_Fix);
    delay(100);
    cnt++;
    }
}

Результат для второй платы:

1 / Time = 1654700
2 / Time = 2564392
3 / Time = 3474084
4 / Time = 4383784
5 / Time = 5293472
6 / Time = 6203160
7 / Time = 7112852
8 / Time = 8022548
9 / Time = 8932248
10 / Time = 9842000

Разница времени через 10 секунд:

(9842000 - 1654700) - (11102712 - 2907648) = 8195064 - 8187300 = 7764 мс,
то есть ~ 800 мс за 1 секунду

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

1. Первая мысль: раз от одной платы можно бросить провод ко второй плате, не проще ли все измерения проводить на одном устройстве?

2. Синхронизироваться можно и по каналу передачи, имеющему задержку. Естественно, передача должна осуществляться поочередно в обоих направлениях с целью вычисления времени задержки.

3. Часы устройств работают от кварцевых генераторов, а последние имеют погрешность. Нужно смотреть по даташтиам, 0.08% - это много или мало. Для часового уварца безусловно много, а для обычного, может, и нормально.

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

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

А подать с одной платы на другую "синхроимпульс" ?

maksim
Offline
Зарегистрирован: 12.02.2012

Первая, а возможно и последняя, причина такой погрешности это ваш метод измерений.

void setup() 
{
  Serial.begin(9600);
  uint32_t start_time = 0, stop_time = 0;
  digitalWrite(PIN_TX, 1);
  start_time = micros();
  delay(10000);
  digitalWrite(PIN_TX, 0);
  stop_time = micros();
  Serial.println(stop_time-start_time);
}
void setup() 
{
  Serial.begin(9600);
  uint32_t start_time = 0, stop_time = 0;
  while(!digitalRead(PIN_RX));
  start_time = micros();
  while(digitalRead(PIN_RX));
  stop_time = micros();
  Serial.println(stop_time-start_time);
}

Вторая это то, что на Nano 3.0 стоит не кварцевый резонатор, а керамический.

Black_Cat
Offline
Зарегистрирован: 27.06.2015

maksim пишет:

Первая, а возможно и последняя, причина такой погрешности это ваш метод измерений.

Согласен код был не идеален, написал другой на основе вашего.

maksim пишет:

Вторая это то, что на Nano 3.0 стоит не кварцевый резонатор, а керамический.

Проблема была, действительно, в этом. Заменил керамический резонатор на кварцевый (+ два конденсатора по 20 пФ для надёжного старта), теперь разница между двумя платами стала существенно меьше. Если раньше показания функции micros() уже спустя 1 секунду (то есть миллион значений микроса) отличались на 800-900 единиц (порядка 1 миллисекунды), то с кварцевым резонатором значение стало равно всего 2 единицам, то есть 2 микросекундам. В 500 раз лучше :))

Вывод: если необходимо работать с временными интервалами менее 1 мс, или необходимо сделать часы на Arduino Nano без использования внешнего RTC, необходимо заменить керамический резонатор на кварцевый.