Связь двух arduino Mega
- Войдите на сайт для отправки комментариев
Пнд, 23/07/2012 - 17:14
Пытаюсь скрестить две платы 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го. Что нужно подправить чтобы у меня в переменной приемника всегда был массив отправителя?
Заранее большое спасибо.
Полагаю, какойнибудь синхронизационный символ, или строка символов спасут
верхний код поменять на чтонибудь вроде
#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, спасибо большое - теперь работает как надо. С синхронизацией тоже щас попробую.
Наткнулся на еще одну проблемку. Данных на самом деле должно передаваться очень много (порядка 100 байт). И все должно приходить одним куском.
Код остается таким же как написал maksim, но массив в обоих случаях Str[100] и в коде передатчика в setup для примера заполняем его так:
Дак вот при приеме данных получатель отрисовывает эти 100 байт не полностью.. Выглядит это так:
57
58
59
60
61
62
255
255
255
и так до конца.. потом опять с 0 до 62 и потом 255. Это чего такое может быть?
Это скорее всего связано с переполнением буфера, попробуй закомментировать 10 строку
но тогда не исключено, что будет читаться пустой буфер, тогда нужно уменьшить эту задержку например в 2 раза
В общем нужно подобрать задержку таким образом, что бы и буфер не успевал переполняться и что бы не было таких моментов, когда байт еще не передался, а его уже читают из буфера.
Наигрался с задержками.. ставил и убирал разные задержки как в приемнике так и в передатчике - все равно после 62го байта далее идут 255. Наверное, если бы задержки как то влияли, то это было бы видно.. а тут всегда такая картина.. Может еще в чем причина может быть?
Какие задержки ставили?
Код отправителя:
Код получателя:
При любых манипуляциях с задержками вывод значений заканчивается ВСЕГДА на 62 байте.. дальше идут 255. Потом по новой. Может буфер чистить надо перед отправкой.. или после.. (((
Это скорее всего связано с переполнением буфера, попробуй закомментировать 10 строку
Это связано с попыткой чтения еще не успевшего поступить байта (чтение из пустого буфера):
"Возвращаемое значение
Следующий доступный байт или -1 если его нет (int)"
-1 (signed char) это как раз 255 (unsigned char).
Так что задержку необходимо не уменьшать, а увеличивать.
Но в любом случае подобный метод будет очень неустойчивым к любым внешним воздействиям (прерывание, изменение температуры, ... да хоть погода на Марсе).
Правильный подход - обрамить посылку префиксом и суффиксом. Например, использовать для этой цели символы STX (0x02) и ETX (0x03).
После получения префикса переходить из состояния "ожидание" в состояние "прием" и считывать все доступные байты в свой буфер, пока не будет считан суффикс. Это событие будет сигналом перехода в состояние "обработка" (ну а после обработки - возврат в "ожидание").
Короче - реализовать простейший вариант конечного автомата (или на пальцах).
Код отправителя:
Код получателя:
При любых манипуляциях с задержками вывод значений заканчивается ВСЕГДА на 62 байте.. дальше идут 255. Потом по новой. Может буфер чистить надо перед отправкой.. или после.. (((
Так естественно, у вас читается пустой буфер. Зачем вы обманываете когда пишите такое:
Код остается таким же как написал maksim, но массив в обоих случаях Str[100] и в коде передатчика в setup для примера заполняем его так:
for(int i = 0; i < 100; i++)
Str[i]=i;
Вы вынесли задержку из цикла чтения и пытаетесь что-то добиться изменяя ее... задержка должна находиться в теле цикла. Именно так как я вам выше написал в коде.
Разницу видите?
А вообще наверное лучше вам привыкнуть выделять тело цикла (условия) фигурными скобками {....}
Это скорее всего связано с переполнением буфера, попробуй закомментировать 10 строку
Это связано с попыткой чтения еще не успевшего поступить байта (чтение из пустого буфера):
"Возвращаемое значение
Следующий доступный байт или -1 если его нет (int)"
-1 (signed char) это как раз 255 (unsigned char).
Так что задержку необходимо не уменьшать, а увеличивать.
Но в любом случае подобный метод будет очень неустойчивым к любым внешним воздействиям (прерывание, изменение температуры, ... да хоть погода на Марсе).
Правильный подход - обрамить посылку префиксом и суффиксом. Например, использовать для этой цели символы STX (0x02) и ETX (0x03).
После получения префикса переходить из состояния "ожидание" в состояние "прием" и считывать все доступные байты в свой буфер, пока не будет считан суффикс. Это событие будет сигналом перехода в состояние "обработка" (ну а после обработки - возврат в "ожидание").
Короче - реализовать простейший вариант конечного автомата (или на пальцах).
Задержки в 1 милисекунду для скорости 115200 более чем достаточно. При переполнения буфера симптомы будут примерно такими же - передающая строна начинает передавать байты, а принмающая начинает читать, если скорость чтения из буфера меньше скорости передачи, то буфер начинает заполняться. И когда бефер переполняется данные начинают теряться, то есть, например, из 100 байт 30 потерялось, а так как у нас в коде чтение происходит строго 100 раз, оставшиеся 30 раз будет прочитан пустой буфер.
Как я понимаю, в задаче используются две платы Arduino Mega2560. В крайнем случае - просто Arduino Mega. И там и там камни имеют RAM размером 8 кБ. Стало быть, приемный буфер объекта Serial имеет длину 128 байт:
Как можно переполнить буфер размером 128 байт при приеме посылки длиной в 100 байт, представляю себе с великим трудом. Единственное, что приходит на ум - плата-получатель еще не успела обработать предыдущую посылку, а нее уже запихивается следующая. То есть, плата-приемник должна принимать 100 байтов в секунду и даже меньше (передатчик делает секундный перерыв между очередными посылками). Где коды, которые так грузят процессор, что он не способен справиться даже с передачей данных на скорости 9600 бит/с (ок. 1000 байт/с)? Мне кажется, что таких кодов здесь нет...
А теперь другой вопрос: предположим, что длина буфера у нас не 128, а 32 байта. Что произойдет при переполнении буфера? А ничего страшного не произойдет - при попытке записать очередной принятый байт он просто не запишется в буфер:
Но ведь чтение будет продолжаться - пусть через один или два принятых байта, но место в приемном буфере будет освобождаться. Т.е. после переполнения хоть один символ, да прорвется.
В нашем - чисто теоретическом - случае (буфер 32 байта, срыв после 62 байта) можно предположить, что данные обрабатываются в 2 раза медленнее, чем передаются. Значит, место под прием байта будет высвобождаться после 1-2 безуспешных попыток записи в него. И выводимая приемником последовательность будет выглядеть примерно так:
а не
И еще пара вопросов: насколько синхронно запускаются обе платы? что произойдет, если приемник запустится на несколько мкс позже передатчика и его UART-модуль будет в состоянии принимать поток данных, начиная лишь с n-го байта? Как вообще приемник сможет определить, где начало у посылки, а где конец? И на чем базируется ваша уверенность, что передавая 100 байтов, вы примете также 100 байтов, а не 99 или 98 (особенно на достаточно высоких скоростях)?
Передача по UART по определению - асинхронный процесс. Синхронизация в нем присутствует только на уровне приема байта и реализуется аппаратно (если не пытаться оседлать процесс с использованием SoftwareSerial). На уровне потока байтов необходимо управлять обработкой с использованием методов асинхронной обработки данных. Иначе любой ваш код будет сбоить при малейшем изменении условий его применения.
Вы меня извините конечно, но почемубы не вставить в тело приемного цикла проверку на Serial.avaluable
ведь вы везде делаете ее 1 раз, а потом неистово читаете из буфера.
for (i=0;i<100;i++) {
while (!Serial.avaluable()); //вместо всяких delay
serial.read(....
}
а вообще то максим прав, нужно реализовать простейший протокол
синхронизация , прием. , собстно об этом я вначале и написал. тогда принимающая дуина будет четко знать какой байт она принимает
почемубы не вставить в тело приемного цикла проверку на Serial.avaluable
for (i=0;i<100;i++) {
while (!Serial.avaluable()); //вместо всяких delay
serial.read(....
}
Так тоже не пойдет - любая потеря принимаемого байта и код остановится в ожидании данных из уже завершившейся посылки. Работа будет продолжена только с поступлением новой посылки. Причем первый байт новой посылки будет интерпретироваться как последний байт предыдущей. Дальше, с накоплением этих самых потерь - N первых байтов новой посылки будут записываться как N последних данных предыдущей. И пошло-поехало...
В канале передачи данных, в котором возможны помехи/ошибки - а на скорости 115200 бит/с это можно и нужно ожидать уже при длине линии в несколько десятков сантиметров - необходимо уметь обнаруживать и корректировать ошибочные ситуации, иначе программа будет работать нестабильно.
Я согласен, но это лучше манипуляций с delay()
Итого
Нужен простейший протокол , с проверкой контрольной суммы, и реализацией выхода из состояний приема передачи с применением таймерного прерывания, тоесть протокол надежной передачи данных, возможно он уже есть, надо посмотреть
Пытаюсь скрестить две платы arduino Mega и передать массив данных с одной на другую. Вот чето не выходит пока никак.
Почему выбор пал на Serial, а не на SPI?
По SPI скорость обмены в разы быстрее
вот полагаю реализованый вариант обмена.
https://github.com/madsci1016/Arduino-EasyTransfer
Максимальная теоретическая скорость 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?...