Связь двух arduino Mega

egorka11
Offline
Зарегистрирован: 23.07.2012

 Пытаюсь скрестить две платы arduino Mega и передать массив данных с одной на другую. Вот чето не выходит пока никак. Опишу немного упрощенно проблему:

Код отправителя:

byte Str[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
void setup() { 
Serial1.begin(115200); 
}

void loop() { 
for (int i = 0; i < 20; i++) 
Serial1.write(Str[i]);        // отсылает  1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
}

Код получателя:

byte Str[20];
void setup() {
Serial.begin(115200);
Serial1.begin(115200);
}

void loop(){

if (Serial1.available() > 0) { 
for (int i=0;i<20;i++)
Str[i] = Serial1.read();
}


for (int i=0;i<20;i++)
Serial.println(Str[i],DEC);   // печатает, например  15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14

delay(5000);
}

 

 

Проблема в том что получатель принимает данные в разнобой. т.е. не последовательность данных как в переменной Str отправителя, а начинает принимать с разгого элемента массива.. то с 5го, то с 8го. Что нужно подправить чтобы у меня в переменной приемника всегда был массив отправителя?

Заранее большое спасибо.
 

Borland
Offline
Зарегистрирован: 17.05.2012

Полагаю, какойнибудь синхронизационный символ, или строка символов спасут

верхний код поменять на чтонибудь вроде

#define ACK 55

byte Syn[2]={AA,55};

 byte Str[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
 void setup() {
 Serial1.begin(115200);
 }
 
 void loop() {

while  (!Serial.Avaluable()) {

   for (int i = 0; i < 2; i++)
     Serial1.write(SYN[i]); 

      delay(x);

}

 

//

if (serial.read)==ACK {

 

   for (int i = 0; i < 20; i++)
    Serial1.write(Str[i]); // отсылает 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
   }

}

Нижний соответственно настроен сперва на прием SYN , как только принял, отсылает ACk и уже смело настраивается на прием 20 байтов

 

не проверял не компилил, возможны ошибки )) , пытался передать смысл

maksim
Offline
Зарегистрирован: 12.02.2012
byte Str[20] = {
  1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
void setup() { 
  Serial1.begin(115200); 
}

void loop() { 
  for(int i = 0; i < 20; i++){ 
    Serial1.write(Str[i]);        // отсылает  1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
  }
  delay(1000);
}

 

 

byte Str[20];
void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}

void loop(){
  if (Serial1.available() > 0){ 
    for (int i=0;i<20;i++){
      delay(1);
      Str[i] = Serial1.read();
    }
    for (int i=0;i<20;i++){
      Serial.print(Str[i],DEC);   // печатает, например  15,16,17,18,19,20,1,2,3,4,5,6,7,8,9,10,11,12,13,14
    }
    Serial.print('\n');
  }
}

 

egorka11
Offline
Зарегистрирован: 23.07.2012

 maksim, спасибо большое - теперь работает как надо. С синхронизацией тоже щас попробую.

egorka11
Offline
Зарегистрирован: 23.07.2012

 Наткнулся на еще одну проблемку. Данных на самом деле должно передаваться очень много (порядка 100 байт). И все должно приходить одним куском.

Код остается таким же как написал maksim, но массив в обоих случаях Str[100] и в коде передатчика в setup для примера заполняем его так: 

for(int i = 0; i < 100; i++) 
Str[i]=i;

Дак вот при приеме данных получатель отрисовывает эти 100 байт не полностью.. Выглядит это так:

 

57
58
59
60
61
62
255
255
255

и так до конца.. потом опять с 0 до 62 и потом 255. Это чего такое может быть?

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

Это скорее всего связано с переполнением буфера, попробуй закомментировать 10 строку 

     // delay(1);

но тогда не исключено, что будет читаться пустой буфер, тогда нужно уменьшить эту задержку например в 2 раза

      delayMicroseconds(500);

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

egorka11
Offline
Зарегистрирован: 23.07.2012

Наигрался с задержками.. ставил и убирал разные задержки как в приемнике так и в передатчике - все равно после 62го байта  далее идут 255. Наверное, если бы задержки как то влияли, то это было бы видно.. а тут всегда такая картина.. Может еще в чем причина может быть?

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

Какие задержки ставили?

egorka11
Offline
Зарегистрирован: 23.07.2012

Код отправителя:

byte Str[100];
void setup() {
  Serial1.begin(115200);
   for(int i = 0; i < 100; i++)
   Str[i]=i;
}
 
void loop() {
  for(int i = 0; i < 100; i++){
    Serial1.write(Str[i]);      
  }
  delay(1000);                  //менял от 2000 до 200
}

 Код получателя:

byte Str[100];
void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}

void loop(){
     if (Serial1.available() > 0) {   
             delay(1);                    // менял от delay(10) до delayMicroseconds(100)
             for (int i=0;i<100;i++)
             Str[i] = Serial1.read();
    }
    for (int i=0;i<100;i++){
    Serial.println(Str[i],DEC);      
    }
}

 При любых манипуляциях с задержками вывод значений заканчивается ВСЕГДА на 62 байте.. дальше идут 255. Потом по новой. Может буфер чистить надо перед отправкой.. или после.. (((

step962
Offline
Зарегистрирован: 23.05.2011

maksim пишет:

Это скорее всего связано с переполнением буфера, попробуй закомментировать 10 строку 

// delay(1);

Это связано с попыткой чтения еще не успевшего поступить байта (чтение из пустого буфера):

"Возвращаемое значение
Следующий доступный байт или -1 если его нет (int)
"

-1 (signed char) это как раз 255 (unsigned char).

Так что задержку необходимо не уменьшать, а увеличивать.

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

Правильный подход - обрамить посылку префиксом и суффиксом. Например, использовать для этой цели символы STX (0x02) и ETX (0x03).

После получения префикса переходить из состояния "ожидание" в состояние "прием" и считывать все доступные байты в свой буфер, пока не будет считан суффикс. Это событие будет сигналом перехода в состояние "обработка" (ну а после обработки - возврат в "ожидание").

Короче - реализовать простейший вариант конечного автомата (или на пальцах).

 

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

egorka11 пишет:

Код отправителя:

byte Str[100];
void setup() {
  Serial1.begin(115200);
   for(int i = 0; i < 100; i++)
   Str[i]=i;
}
 
void loop() {
  for(int i = 0; i < 100; i++){
    Serial1.write(Str[i]);      
  }
  delay(1000);                  //менял от 2000 до 200
}

 Код получателя:

byte Str[100];
void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}

void loop(){
     if (Serial1.available() > 0) {   
             delay(1);                    // менял от delay(10) до delayMicroseconds(100)
             for (int i=0;i<100;i++)
             Str[i] = Serial1.read();
    }
    for (int i=0;i<100;i++){
    Serial.println(Str[i],DEC);      
    }
}

 При любых манипуляциях с задержками вывод значений заканчивается ВСЕГДА на 62 байте.. дальше идут 255. Потом по новой. Может буфер чистить надо перед отправкой.. или после.. (((

Так естественно, у вас читается пустой буфер. Зачем вы обманываете когда пишите такое:

egorka11 пишет:

Код остается таким же как написал maksim, но массив в обоих случаях Str[100] и в коде передатчика в setup для примера заполняем его так:

for(int i = 0; i < 100; i++)
Str[i]=i;

Вы вынесли задержку из цикла чтения и пытаетесь что-то добиться изменяя ее... задержка должна находиться в теле цикла. Именно так как я вам выше написал в коде.

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

Разницу видите?

byte Str[100];
void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}

void loop(){
  if (Serial1.available() > 0) {   
    for (int i=0;i<100;i++){
      delayMicroseconds(200);
      Str[i] = Serial1.read();
    }
  }
  for (int i=0;i<100;i++){
    Serial.println(Str[i],DEC);      
  }
}

А вообще наверное лучше вам привыкнуть выделять тело цикла (условия) фигурными скобками {....}

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

step962 пишет:

maksim пишет:

Это скорее всего связано с переполнением буфера, попробуй закомментировать 10 строку 

// delay(1);

Это связано с попыткой чтения еще не успевшего поступить байта (чтение из пустого буфера):

"Возвращаемое значение
Следующий доступный байт или -1 если его нет (int)
"

-1 (signed char) это как раз 255 (unsigned char).

Так что задержку необходимо не уменьшать, а увеличивать.

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

Правильный подход - обрамить посылку префиксом и суффиксом. Например, использовать для этой цели символы STX (0x02) и ETX (0x03).

После получения префикса переходить из состояния "ожидание" в состояние "прием" и считывать все доступные байты в свой буфер, пока не будет считан суффикс. Это событие будет сигналом перехода в состояние "обработка" (ну а после обработки - возврат в "ожидание").

Короче - реализовать простейший вариант конечного автомата (или на пальцах).

 


Задержки в 1 милисекунду для скорости 115200 более чем достаточно. При переполнения буфера симптомы будут примерно такими же - передающая строна начинает передавать байты, а принмающая начинает читать, если скорость чтения из буфера меньше скорости передачи, то буфер начинает заполняться. И когда бефер переполняется данные начинают теряться, то есть, например, из 100 байт 30 потерялось, а так как у нас в коде чтение происходит строго 100 раз, оставшиеся 30 раз будет прочитан пустой буфер.

step962
Offline
Зарегистрирован: 23.05.2011

 Как я понимаю, в задаче используются две платы Arduino Mega2560. В крайнем случае - просто Arduino Mega. И там и там камни имеют RAM размером 8 кБ. Стало быть, приемный буфер объекта Serial имеет длину 128 байт:

// Define constants and variables for buffering incoming serial data.  We're
// using a ring buffer (I think), in which rx_buffer_head is the index of the
// location to which to write the next incoming character and rx_buffer_tail
// is the index of the location from which to read.
#if (RAMEND < 1000)
  #define RX_BUFFER_SIZE 32
#else
  #define RX_BUFFER_SIZE 128
#endif

Как можно переполнить буфер размером 128 байт при приеме посылки длиной в 100 байт, представляю себе с великим трудом. Единственное, что приходит на ум - плата-получатель еще не успела обработать предыдущую посылку, а нее уже запихивается следующая. То есть, плата-приемник должна принимать 100 байтов в секунду и даже меньше (передатчик делает секундный перерыв между очередными посылками). Где коды, которые так грузят процессор, что он не способен справиться даже с передачей данных на скорости 9600 бит/с (ок. 1000 байт/с)? Мне кажется, что таких кодов здесь нет...

А теперь другой вопрос: предположим, что длина буфера у нас не 128, а 32 байта. Что произойдет при переполнении буфера? А ничего страшного не произойдет - при попытке записать очередной принятый байт он просто не запишется в буфер:

inline void store_char(unsigned char c, ring_buffer *rx_buffer)
{
  int i = (unsigned int)(rx_buffer->head + 1) % RX_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != rx_buffer->tail) {
    rx_buffer->buffer[rx_buffer->head] = c;
    rx_buffer->head = i;
  }
}

 

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

В нашем - чисто теоретическом - случае (буфер 32 байта, срыв после 62 байта) можно предположить, что данные обрабатываются в 2 раза медленнее, чем передаются. Значит, место под прием байта будет высвобождаться после 1-2 безуспешных попыток записи в него. И выводимая приемником последовательность будет выглядеть примерно так:

57
58
59
60
61
62
65   <---
68   <---
[...]
255
255

а не

57
58
59
60
61
62
255
255
255

 

И еще пара вопросов: насколько синхронно запускаются обе платы? что произойдет, если приемник запустится на несколько мкс позже передатчика и его UART-модуль будет в состоянии принимать поток данных, начиная лишь с n-го байта? Как вообще приемник сможет определить, где начало у посылки, а где конец? И на чем базируется ваша уверенность, что передавая 100 байтов, вы примете также 100 байтов, а не 99 или 98 (особенно на достаточно высоких скоростях)?

Передача по UART по определению - асинхронный процесс.  Синхронизация в нем присутствует только на уровне приема байта и реализуется аппаратно (если не пытаться оседлать процесс с использованием SoftwareSerial). На уровне потока байтов необходимо управлять обработкой с использованием методов асинхронной обработки данных. Иначе любой ваш код будет сбоить при малейшем изменении условий его применения.

Borland
Offline
Зарегистрирован: 17.05.2012

Вы меня извините конечно, но почемубы не вставить в тело приемного цикла проверку на Serial.avaluable

ведь вы везде делаете ее 1 раз, а потом неистово читаете из буфера.

for (i=0;i<100;i++) {

  while (!Serial.avaluable()); //вместо всяких delay

  serial.read(....

}

а вообще то максим прав, нужно реализовать простейший протокол 

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

step962
Offline
Зарегистрирован: 23.05.2011

Borland пишет:

почемубы не вставить в тело приемного цикла проверку на Serial.avaluable

for (i=0;i<100;i++) {

  while (!Serial.avaluable()); //вместо всяких delay

  serial.read(....

}

Так тоже не пойдет - любая потеря принимаемого байта и код остановится в ожидании данных из уже завершившейся посылки. Работа будет продолжена только с поступлением новой посылки. Причем первый байт новой посылки будет интерпретироваться как последний байт предыдущей. Дальше, с накоплением этих самых потерь - N первых байтов новой посылки будут записываться как N последних данных предыдущей. И пошло-поехало...

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

Borland
Offline
Зарегистрирован: 17.05.2012

Я согласен, но это лучше манипуляций с delay()

 

Итого

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

 

 

 

carduino.ru
Offline
Зарегистрирован: 06.12.2011

egorka11 пишет:

 Пытаюсь скрестить две платы arduino Mega и передать массив данных с одной на другую. Вот чето не выходит пока никак.  

Почему выбор пал на Serial, а не на SPI?

По SPI скорость обмены в разы быстрее

Borland
Offline
Зарегистрирован: 17.05.2012

вот полагаю реализованый вариант обмена.

https://github.com/madsci1016/Arduino-EasyTransfer

step962
Offline
Зарегистрирован: 23.05.2011

 Максимальная теоретическая скорость SPI - Fosc/2 или 8 Мбит/с (при стандартном кварце 16 МГц).  Принятые байты необходимо обрабатывать каждые 16 тактов или будет происходить их потеря. Чтение из регистра SPDR в РОН, сохранение из РОН в память, изменение индексов, вход/выход из подпрограммы приема байта - на это уходит 6-8 тактов. Половина ресурсов ЦПУ. Поэтому обычно замахиваются на Fosc/4 (4 Мбит/с). При этом никакой аппаратной защиты от помех.

Максимальная скорость передачи по UART при кварце 16 МГц - 2 Мбит/с (datasheet на ATMegaXX8, стр. 199). При этом UART Имеет встроенные механизмы защиты от помех (стр. 192-193 из того же документа).

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

Микропроцессоры STM32F10x работают на частоте 72 МГц. 32-битные. Производительность на порядок (десятичный!) выше. Arduino-подобная IDE и платы в формате Arduino-UNO имеются. Почему при этом участники этого форума выбирают Arduino, а не Maple?...