Синхронизация внутренних часов двух ардуин

Daemon2017
Offline
Зарегистрирован: 08.10.2013

Имеются 2 дуины, соединенные через I2C/Serial(научился и так, и так настраивать) и два датчика на них. Нужно сделать так, чтобы их внутренние часы были максимально(с точностью до десятков-сотен микросекунд) синхронизированы. На какждом из них стоит датчик и нужно, чтобы датчики включались/выключались одновременно. 

Возможно ли это сделать на голой ардуине? А с использованием сторонних деталей и каких? 

Читал о часах реального времени - с их помощью удастся реализовать такую точную синхронизацию?

 

axill
Offline
Зарегистрирован: 05.09.2011

Вы определитесь что вам нужно. Вам нужны часы (типа московское время...) или же отсчеты времени для синхронной работы (и все равно который час)?

Если второе то в чем проблема настроить одну ардуину на то, чтобы она периодически второй давала команду сброса часов? А между сбросами время считать по mills()?

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

микросхема ds1307 на которую вы ссылаетесь имеет дискретность отсчета одна секунда.

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Я бы попробовал протестировать такую связку: 2 дуины связаны между собой цифоровым пином (или может двумя!?), RTC подлючены только к одной (к master). Когда наступает нужное время работы с датчиком, главная выставляет на цифровом пине HIGH и начинает работу со своим датчиком, вторая дуина (без RTC, slave) в прерывании ловит сигнал мастера и тоже начинает работу со своим датчиком. Похоже нужна обратная связь по второму цифровому пину, когда слэйв поймал сигнал мастера надо отправить мастеру ответит и мастер должен снять свой сигнал (т.е. мастер тоже ловит сигнал в прерывании от слэйва). В общем логику общения по пинам надо проработать, сам не пойму пока как.

Так не получиться сделать?

Daemon2017
Offline
Зарегистрирован: 08.10.2013

axill, да, именно второе нужно: синхронизация между собой, а не относительно GMT.

А не подскажете, какой командой сбрасывать часы? Если честно, не очень понял, как мне поможет сброс времени. 

Я вот попробовал сделать как: одну дуину подключаю к батарейке типа "крона", а из её разьема 5V бросаю провод на вторую дуину и запитываю ей через Vin, ну и GND, само-собой, тоже общий. Получается, что как только стартует одна дуина - стартует и вторая и часы у них тикают синхронно.

Переписла код так, чтобы незаолго до момента, когда мне нужно сделать замер, я отправляю значение переменной "a"(в которую перед самой отправкой зашиваю значение micros()+1000 микросекунд время передачи данных + 50 микросекунд про запас), вторая дуина это значение принимает и сравнивает значение своих часов с этим до тех пор, пока оно не сравняется и тогда делает замер данных с датчика. Первая дуина поступает аналогично.

Синхронность отличная, но только в первые 3-5 минут, дальше разброс становится недопустимо большим и растет... Вычитал в Сети, что это вызвано малейшими перепадами напряжения, температуры и прочим.

Daemon2017
Offline
Зарегистрирован: 08.10.2013

Garry пишет:

Я бы попробовал протестировать такую связку: 2 дуины связаны между собой цифоровым пином (или может двумя!?), RTC подлючены только к одной (к master). Когда наступает нужное время работы с датчиком, главная выставляет на цифровом пине HIGH и начинает работу со своим датчиком, вторая дуина (без RTC, slave) в прерывании ловит сигнал мастера и тоже начинает работу со своим датчиком. Похоже нужна обратная связь по второму цифровому пину, когда слэйв поймал сигнал мастера надо отправить мастеру ответит и мастер должен снять свой сигнал (т.е. мастер тоже ловит сигнал в прерывании от слэйва). В общем логику общения по пинам надо проработать, сам не пойму пока как.

Так не получиться сделать?

Идея хорошая, тем более, что некоторые RTC работают по I2C и данные с них могут снимать хоть 100 дуинок сразу. Но если axill прав и она имеет дискретность в одну секунду - вряд ли это нам поможет. Разве что, сделать расписание замеров(каждая 15-я секунда, каждая 30-я и т.д.) 

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Ну я просто подумал, что датчики нужно опрашивать по реальному времени, а не по внутреннему. А если нужно просто синхронизовать действия 2 дуин, то тогда  RTC ваыкидываем, а связь по пинам оставляем.

axill
Offline
Зарегистрирован: 05.09.2011

Daemon2017 пишет:

А не подскажете, какой командой сбрасывать часы? Если честно, не очень понял, как мне поможет сброс времени. 

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

но мне видится вариант с пинами вам может даже больше подойти. Если просто нужно выполнить два действия одновременно сделайте как вам выше порекомендовали - одна ардуина на специальном пине делает импульс (например один за другим выставляем HIGH и тут же LOW), этот пин подключен у другой ардуины к пинам с прерываниями настроеными на raise.

Daemon2017
Offline
Зарегистрирован: 08.10.2013

axill пишет:

Daemon2017 пишет:

А не подскажете, какой командой сбрасывать часы? Если честно, не очень понял, как мне поможет сброс времени. 

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

но мне видится вариант с пинами вам может даже больше подойти. Если просто нужно выполнить два действия одновременно сделайте как вам выше порекомендовали - одна ардуина на специальном пине делает импульс (например один за другим выставляем HIGH и тут же LOW), этот пин подключен у другой ардуины к пинам с прерываниями настроеными на raise.

Спасибо за советы! Боюсь, что через прерывания и придется делать.

Вот что нашел в гитхабе - 

TimeServer:

#include <Wire.h>

String timeOffsetStr; // Variable to hold time
char dataFromClient[10];  // Variable to hold data from client

volatile unsigned long clientTime;  // Variable to hold timestamp of client

void setup()
{
  timeOffsetStr.reserve(10); // Reserve 10 bytes for unsigned long time
  Wire.begin(0x50); // Setup I2C slave address as 0x50     
  Wire.onRequest(requestEvent); // Register event for requested data
  Wire.onReceive(receiveEvent); // Register event for incomming data
}

void loop() {
 // Do nothing
}

void receiveEvent(int bytes){
  int index = 0;  // index of char array
  while(Wire.available())   // while more bytes avail
  { 
    char c = Wire.read();    // receive a byte as character
    dataFromClient[index++] = c;  // store byte in char array
    
  }
  clientTime = atol(dataFromClient);  // convert char array to unsigned long
}

void requestEvent()
{
  timeOffsetStr = String(micros() - clientTime);  // convert timestamp to string
  volatile int timeLength = timeOffsetStr.length();  // find out how many digits are in the current time
  // If not 10 digits (max digit length for unsigned long), add leading zeros
  // This is to keep the time of I2C transfers consistent between subsequent read/writes
  if(timeLength == 1){
    timeOffsetStr = "000000000" + timeOffsetStr;
  }
  else if(timeLength == 2){
    timeOffsetStr = "00000000" + timeOffsetStr;
  }
  else if(timeLength == 3){
    timeOffsetStr = "0000000" + timeOffsetStr;
  }
  else if(timeLength == 4){
    timeOffsetStr = "000000" + timeOffsetStr;
  }
  else if(timeLength == 5){
    timeOffsetStr = "00000" + timeOffsetStr;
  }
  else if(timeLength == 6){
    timeOffsetStr = "0000" + timeOffsetStr;
  }
  else if(timeLength == 7){
    timeOffsetStr = "000" + timeOffsetStr;
  }
  else if(timeLength == 8){
    timeOffsetStr = "00" + timeOffsetStr;
  }
  else if(timeLength == 9){
    timeOffsetStr = "0" + timeOffsetStr;
  }
  Wire.print(timeOffsetStr); // respond with padded timestamp
  
}

Timer:

#include <Wire.h>

char dataFromTimeServer[10]; // char array to hold I2C data from TimeServer
unsigned long timeOffset = 0;  // unsigned long to hold time from TimeServer

unsigned long currentSyncedTime = 0;  // Last button press's time (synced)
unsigned long timeOfLastSync = 0;   // Timestap of last sync

const int buttonPin = 10; // Pin that button is on
const int syncStatusOutPin = 11; // Pin that sync status is output on
const int syncStatusInPin = 22; // Pin that sync status is collected on

void setup() {
  
  Serial.begin(9600);
  
  digitalWrite(buttonPin,HIGH);  // enable pull up resitor
  attachInterrupt(buttonPin, buttonPress, FALLING);  // register interrup on FALLING state
  
  
  pinMode(syncStatusOutPin, OUTPUT);  // set it up as an output
  digitalWrite(syncStatusOutPin,LOW); // default state
  
  digitalWrite(syncStatusInPin, LOW);  // setup pull-down
  pinMode(syncStatusInPin, INPUT);  // set it up as an input
  
  syncTime(); // Sync time with TimeServer
}


void loop() {
 if(currentSyncedTime!=0){
   Serial.println(currentSyncedTime);
   currentSyncedTime=0;
 }
 // Sync every 5 seconds if Time server is avail
 if((millis() - timeOfLastSync) > 5000 && digitalRead(syncStatusInPin) == LOW){ 
   syncTime();
 }

}
// Run when falling
void buttonPress(){
  // Record timestamp in reference frame of TimeServer
  currentSyncedTime = timeOffset + micros();
}

// Method to read timestamp from central TimeServer over I2C
void syncTime(){
  
  while(digitalRead(syncStatusInPin) != LOW) { } // wait for server to become free
  
  digitalWrite(syncStatusOutPin,HIGH);   // let others know server is busy
    //Serial.print("SYNCING...");
  Wire.begin();  // Initialize I2c  
  
  Wire.beginTransmission(0x50);  // begin communication with TimeServer
  String timeStr = String(micros());  // convert timestamp to string
  
  volatile int timeLength = timeStr.length();  // find out how many digits are in the current time
  // If not 10 digits (max digit length for unsigned long), add leading zeros
  // This is to keep the time of I2C transfers consistent between subsequent read/writes
  if(timeLength == 1){
    timeStr = "000000000" + timeStr;
  }
  else if(timeLength == 2){
    timeStr = "00000000" + timeStr;
  }
  else if(timeLength == 3){
    timeStr = "0000000" + timeStr;
  }
  else if(timeLength == 4){
    timeStr = "000000" + timeStr;
  }
  else if(timeLength == 5){
    timeStr = "00000" + timeStr;
  }
  else if(timeLength == 6){
    timeStr = "0000" + timeStr;
  }
  else if(timeLength == 7){
    timeStr = "000" + timeStr;
  }
  else if(timeLength == 8){
    timeStr = "00" + timeStr;
  }
  else if(timeLength == 9){
    timeStr = "0" + timeStr;
  }
  
  Wire.print(timeStr); // Send padded timestamp to TimeServer
  Wire.endTransmission(); // End comm.

  Wire.requestFrom(0x50, 10);  // Request 10 bytes (timestamp) from TimeServer
  int index = 0;  // index of char array
  while(Wire.available())   // while more bytes avail
  { 
    char c = Wire.read();    // receive a byte as character
    dataFromTimeServer[index++] = c;  // store byte in char array
    
  }
  digitalWrite(syncStatusOutPin,LOW); // let others know server is free
  timeOffset = atol(dataFromTimeServer);  // convert char array to unsigned long
  timeOfLastSync = millis();  // update time of last sync
  
}  

Попробую и так, для очистки совести, вдруг оно будет работать так, как мне нужно.