PDA

Просмотр полной версии : Вопросы начинающего Ардуинщика...



SoftFelix
10.02.2020, 14:44
Очень хочется разобраться, но туплю по-страшному. Именно в программировании на Си (Ардуино IDE). Ещё два месяца тому назад я понятия не имел с данным синтаксисом. В наличии китайская Ардуино UNO (на CH340G). Кое-что ужЕ понимаю. На Спекке очень давно писал на Басике и Ассме, но без фанатизма. Сейчас есть цель написать поддержку автономного программатора для отечественного микроконтроллера 1986ВЕ91Т на базе Ардуино. Связь через UART (Rx-Tx с преобразователем UART TTL <-> RS-422), чтение с SD. Железо всё ужЕ разработал и собрал - всё работает. Фото будет чуть позже. Нужен человек (люди), способный спокойно подсказать по алгоритму и синтаксису.

Сейчас дошло время до процедуры начальной синхронизации с МК. Там по даташиту и исходникам для ПЦ (тоже на Си++ ) всё довольно "протсто":

1. Цикле заслали в порт 512 байт с кодом 0х00 (512 нулей). На ПЦ это делается дважды.
2. МК должен ответить тремя (3) байтами - 0d, 0a, 3e - это успешная синхронизация. На ПЦ просто проверяют, что пришло 3 байта и НЕ парсят их.
3. Если ответил иное - ошибка.

На ПЦ это выглядит так:




txdbuf[0] = 0x0;
for(i=0;i<512;i++)
com.WriteBlock(txdbuf,1);
if (!com.ReadBlock(rxdbuf,3))
{
for(i=0;i<512;i++)
com.WriteBlock(txdbuf,1);
if (!com.ReadBlock(rxdbuf,3))
{
str = "ошибка синхронизации!";
InsertStrToList();
com.Close();
return;
}
}

m_list.DeleteString(m_list.GetCount()-1);
str = "Синхронизация...ОК!";



Я родил вот это, но оно всегда пишет (у меня вывод на LCD 16X2) "Succes!".




void SYNC_1986(){ //функция синхронизации с 1986ВЕ91

lcd.clear();
lcd.setCursor(1,0);
lcd.print("Try to Sync...");
lcd.setCursor(5,1); //подготовим позицию для печати результата ("Failed!" / "Succes!")

while(Serial.available()) Serial.read(); //вот это работает - очищает буфер UART'перед работой!!!

for (int i=0; i < 512; i++){
Serial.write(0); //выдали в порт 512 байт 0х00
}
if (!Serial.available() == 3){ //приняли 3 байта от контроллера?
lcd.print("Failed!"); }
else {
lcd.print("Succes!");
}
}



Добавил фото программатора (внутри Ардуино УНО с обвесом).

https://b.radikal.ru/b22/2002/5e/54fa91729da2t.jpg (http://radikal.ru/fp/de86tgtjb5t7x)

https://b.radikal.ru/b26/2002/4e/35c0bf81cf1at.jpg (http://radikal.ru/fp/br5p4qxa6tzu8)

https://d.radikal.ru/d25/2002/d2/724c8398b019t.jpg (http://radikal.ru/fp/fd955vqm35ilc)

https://d.radikal.ru/d06/2002/d1/87d8d5392b62t.jpg (http://radikal.ru/fp/ugj2ff87h3nl9)

https://c.radikal.ru/c18/2002/f1/09adcbfbc3dat.jpg (http://radikal.ru/fp/s753qvwbra2kw)

https://d.radikal.ru/d12/2002/00/c48d6509deabt.jpg (http://radikal.ru/fp/jm75ww818wfys)

https://a.radikal.ru/a26/2002/a7/f3f3bdf40e80t.jpg (http://radikal.ru/fp/2y8xuo7xsnqh5)

https://a.radikal.ru/a10/2002/78/3d261c415e6at.jpg (http://radikal.ru/fp/b7ypyei2lgp29)

andrews
10.02.2020, 15:10
+

2. МК должен ответить тремя (3) байтами - 0d, 0a, 3e - это успешная синхронизация. На ПЦ просто проверяют, что пришло 3 байта и НЕ парсят их. у Arduino есть окно терминала под выбранный порт. Посмотрите, что там MCU выводит. Только придется выход с lcd на Arduino переключить. По программам ничего не скажу, нужно смотреть описание

Serial.available() Вообще лучше отлаживать "step by step" и железо тоже.

Tronix
10.02.2020, 15:18
if (!Serial.available() == 3){ //приняли 3 байта от контроллера?
lcd.print("Failed!"); }
else {
lcd.print("Succes!");
}

Я ни разу не гуру Си, потомушта сам Паскалист, но вот тут в условие какая-то не однозначность есть точно. Нужно написать либо

if (Serial.available() != 3), либо

if !(Serial.available() == 3)

Имхо.

SoftFelix
10.02.2020, 15:24
Tronix, нет, ! именно в скобках. Иначе ругается компилятор. И так же в ПЦшном исходнике.

SoftWareGuy
10.02.2020, 15:29
Как правильно заметил Tronix, там ошибка.

(!Serial.available() == 3)

Сначала выполняется оператор "!" и он инвертирует результат available, далее bool сравнивается с int, и на выходе будет конечно же false, т.к. 1 или 0 сравнивается с 3.

Если нужно выдавать Failed если в данный момент доступно отличное от 3 количество байт, то условие будет следующим:
(Serial.available() != 3)

Tronix
10.02.2020, 15:35
. Иначе ругается компилятор.

Да, прально ругается, надо так: if (!(Serial.available() == 3)) . Но как я и SoftWareGuy уже сказали, чтобы не косить под индусов, правильно написать так:
if (Serial.available() != 3)

SoftFelix
10.02.2020, 15:38
Tronix,
SoftWareGuy, точно! Переписал на:


if (Serial.available() != 3){ //приняли 3 байта от контроллера?
lcd.print("Failed!"); }
else {
lcd.print("Succes!");
}


И вроде заработало. Т.е. вот это (if (Serial.available() != 3)) читается "если в буфере Rx не 3 байта, то далее делаем"?

Tronix
10.02.2020, 15:41
Т.е. вот это (if (Serial.available() != 3)) читается "если в буфере Rx не 3 байта, то далее делаем"?

Это, а именно !=, читается как "не равно". Если Serial.available() не равно 3, то Failed, иначе - Success!

SoftFelix
12.02.2020, 08:43
Как выяснилось, вот эта конструкция не работает: (if (Serial.available() != 3)) или (if (Serial.available() == 3))

Компилятор на неё не ругается, но условие просто не отрабатывается. Проверялось в отладке. А вот классическое условие проверки буфера на возможность считывания в таком виде (if (Serial.available() > 0)) работает всегда.

Опытным путём выяснено, что проверку на количество байт можно сделать только через дополнительную переменную:


int n = Serial.available();
if (n == 3) {

//тут исполняем, если совпало

}

//тут если не совпало

Может всё-таки есть возможность сделать изящно, как тут?: (if (Serial.available() == 3))

SoftWareGuy
12.02.2020, 09:17
Проверялось в отладке
Есть подозрение, что условие работает иногда, время от времени. Возможно потому, что там race condition - иногда в буфер успевает попасть нужное количество байт, а иногда просто нет.

Через доп. переменную делать всегда лучше по нескольким причинам: можно в дебаге увидеть какое там значение, плюс читабельность лучше, что немаловажно.

Я бы делал ожидание в спинлоке, примерно так:


bool success = false;
for (int istep = 0; istep < 100; ++istep) // крутимся в цикле некоторое количество раз, это есть спинлок
{
int n = Serial.available();
// здесь проверка что получили то что нужно, выставляем флажок успеха и выходим из цикла
if (n == 3)
{
success = true;
break;
}
// здесь можно делать дополнительный delay
}

SoftFelix
12.02.2020, 09:33
Уже нашёл. Для проверки на возможность считывания из буфера, достаточно конструкции if (Serial.available(), т.е. без > 0. Данная функция после выполнения возвращает false, если в буфере 0 (буфер пуст); и true, если в буфере есть любое количество байт (в пределах размера буфера) для чтения.

- - - Добавлено - - -

SoftWareGuy, спасибо. Я там ниже ответил, что функция в if возвращает только логическое значение, а через переменную - числовое.

rw6hrm
12.02.2020, 09:53
...я влезу на секундочку: как в Ардуино ИДЕ писать код не на Си, а на ассмблере? А с учётом нескольких входных файлов на ассемблере? Просто есть проект (многофайловый) на асме, его нужно немного подкорректировать, а вот коррекцию можно и на Си сделать.

andrews
12.02.2020, 17:55
Данная функция после выполнения возвращает false, если в буфере 0 (буфер пуст); и true, если в буфере есть любое количество байт (в пределах размера буфера) для чтенияо чем я Вам и писал ))
нужно смотреть описание
Цитата Сообщение от SoftFelix Посмотреть сообщение
Serial.available() а почитать содержимое буфера и посчитать сколько в нем символов можно чем-то другим, очевидно

SoftFelix
12.02.2020, 18:55
о чем я Вам и писал ))
Там об это ни слова. Если честно.

Цитата Сообщение от SoftFelix Посмотреть сообщение
+

2. МК должен ответить тремя (3) байтами - 0d, 0a, 3e - это успешная синхронизация. На ПЦ просто проверяют, что пришло 3 байта и НЕ парсят их.
у Arduino есть окно терминала под выбранный порт. Посмотрите, что там MCU выводит. Только придется выход с lcd на Arduino переключить. По программам ничего не скажу, нужно смотреть описание
Цитата Сообщение от SoftFelix Посмотреть сообщение
Serial.available()
Вообще лучше отлаживать "step by step" и железо тоже.

И в описании к функции (http://arduino.ru/Reference/Serial/Available) этого нет.


а почитать содержимое буфера и посчитать сколько в нем символов можно чем-то другим, очевидно
Чем другим, если данная функция этим и занимается.

p.s. Не хотелось бы флеймить по данному вопросу. И очень бы хотелось конкретно "вот тут неправильно, нужно так..."

andrews
12.02.2020, 19:10
Если конкретно, то для начала, какой у Вас Arduino board?

SoftFelix
12.02.2020, 19:12
Если конкретно, то для начала, какой у Вас Arduino board?
Это не повлияет на результат вопроса в первой мессаге. :) И там же в первой мессаге указана модель.

andrews
12.02.2020, 19:46
Это не повлияет на результат вопроса в первой мессаге. :) И там же в первой мессаге указана модель. UNO такая у меня тоже есть. На вопрос не повлияет, а на натурный эксперимент вполне повлиять может )) я не доверяю чужим кодам, и тем более чужим описаниям )) Попробую на своей(их) железке(ах) и MEGA тоже

- - - Добавлено - - -


как в Ардуино ИДЕ писать код не на Си, а на ассмблере? А с учётом нескольких входных файлов на ассемблере? Просто есть проект (многофайловый) на асме, его нужно немного подкорректировать, а вот коррекцию можно и на Си сделать.
можете поэкспериментировать на Processing language
https://www.processing.org/
если Arduino железа под рукой никакого нет, так как скетчи вроде как почти идентичные и на одних и тех же Java-классах(?)

SoftFelix
12.02.2020, 20:58
Если подытожить начало темы, то ниже рабочая функция. Но там реализован только один цикл синхронизации, отличии от оригинала. Надо добавить второй, но я запутался во вложенных условиях и фигурных скобках. :( Видимо, из-за этого программа иногда пишет Failed!.



void SYNC_1986() { //функция синхронизации с 1986ВЕ91

Serial.end(); //для смены скорости сперва нужно закрыть!
Serial.begin(9600); //настроили скорость UART на 9600

lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Try to Sync...");
lcd.setCursor(5, 1); //подготовим позицию для печати результата ("Failed!" / "Succes!")

while (Serial.available()) Serial.read(); //вот это работает - очищает буфер UART'перед работой!!!

for (int i = 0; i < 512; i++) {
Serial.write(0); //выдали в порт 512 байт 0х00
}
Serial.flush(); //ждём окончания передачи из буфера

int n = Serial.available();
if (n == 3) {
lcd.print("Succes!");
} else {
lcd.print("Failed!");
delay(1000);
menu = 3; //опять вернулись к файлу прошивки
root = SD.open(name_folder + "/");
SD_card(root);
root.close();
}
}


- - - Добавлено - - -

p.s. Обновил первую мессагу, для какого девайса это пишется.

SoftWareGuy
12.02.2020, 21:33
Видимо, из-за этого программа иногда пишет Failed!

Попробуйте этот код:


void SYNC_1986()
{
Serial.end();
Serial.begin(9600);

lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Try to Sync...");
lcd.setCursor(5, 1);

while (Serial.available()) Serial.read();

for (int i = 0; i < 512; i++) {
Serial.write(0);
}
Serial.flush();

const int NumBytesToReceive = 3; // количество байт которые ждем в ответ

bool isSuccess = false; // флаг успешности операции

int numBytesReceived = 0; // здесь храним количество принятых байт
for(int istep = 0; istep < 5; ++istep) // делаем несколько попыток считать ответ
{
while (Serial.available() && numBytesReceived < NumBytesToReceive) // читаем все что уже есть во входном буфере
{
char receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
++numBytesReceived; // храним количество принятых байт
}

if (numBytesReceived == NumBytesToReceive) // получили все что нужно
{
isSuccess = true;
break;
}
delay(10);
}


if (isSuccess) {
lcd.print("Succes!");
} else {
lcd.print("Failed!");
delay(1000);
menu = 3; //опять вернулись к файлу прошивки
root = SD.open(name_folder + "/");
SD_card(root);
root.close();
}
}

SoftFelix
12.02.2020, 22:08
const int NumBytesToReceive = 3
Почему именно const int?


bool isSuccess = false; // флаг успешности операции
bool ???



for(int istep = 0; istep < 5; ++istep)
Вот это вообще не понял. Тут цикл на 5? У нас же 3 байта...



char receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
А почему char ?

Я пока такое сложное не понимаю. :( Можно словами описать алгоритм этого кода или более подробно в комментариях?

SoftWareGuy
12.02.2020, 22:25
Почему именно const int?
Считается хорошим тоном в программировании на Си++ делать константным всё, что не должно меняться по ходу выполнения программы. В общем то это не обязательно, просто в силу привычки так написал. Можно вынести отдельным дефайном, если хочется.


bool ???
Определяется переменная isSuccess логического типа - в ней будем хранить результат всей этой операции - успешный ли был прием или нет.


Вот это вообще не понял. Тут цикл на 5? У нас же 3 байта
Смысл в том, что эти 3 байта могут быть еще не доступны в момент опроса Serial.available(). Если мк на том конце тупит и отправляет с задержками, то Там может быть 1 байт или 2, а может быть и 3 - если повезет. Чтобы исключить фактор "везения-невезения" (то есть рандом) нужно сделать несколько попыток считать данные.
Мы все равно не считаем больше заданного предельного количества байт - там есть проверка на это. То есть случилось чудо и при первой же попытке вычитали все три байта, то выходим из цикла (строка с if (numBytesReceived == NumBytesToReceive) ).


А почему char ?
Считывается один байт. Char как раз имеет разрядность 8 бит, можно конечно сохранять и в int, но это не имеет смысла - на восьмибитных контроллерах int равен 2 байтам, это лишнее место на стеке.


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

Terabiterr
12.02.2020, 22:42
А почему char ?
По ардуино есть документация хорошая ну к ней приступать нужно только после прочтения основ языка С что бы вы начали ее более менее понимать. Нужно изучить не большой ряд основ языка С и пройти не много практики на простых задачах. Инфы полно в гугл. Запросы Уроки по С, как программировать на С и т.п...
А дальше на справочник ардуино.
https://doc.arduino.ua/ru/prog/

SoftFelix
12.02.2020, 22:55
SoftWareGuy, спасибо! Мне пока вот так нужно объяснять. :) И это я понимаю. Кстати, твой код нормально откомпилился в Ардуино IDE. Завтра на работе его попробую. А можно туда сразу добавить второй цикл синхронизации, как ПЦшном исходнике (https://zx-pk.ru/threads/31356-voprosy-nachinayushchego-arduinshchika.html?p=1045373&viewfull=1#post1045373)? В идеале на основе моего последнего кода (https://zx-pk.ru/threads/31356-voprosy-nachinayushchego-arduinshchika.html?p=1045759&viewfull=1#post1045759).

- - - Добавлено - - -


Считывается один байт. Char как раз имеет разрядность 8 бит, можно конечно сохранять и в int, но это не имеет смысла - на восьмибитных контроллерах int равен 2 байтам, это лишнее место на стеке.
Тогда тут можно и byte использовать?

SoftWareGuy
12.02.2020, 23:07
А можно туда сразу добавить второй цикл синхронизации, как ПЦшном исходнике? В идеале на основе моего последнего кода.

Правильно ли я понял, что нужно
1) отправить 512 пустых байт
2) прочитать 3 байта
3) повторно отправить 512 пустых байт
4) еще раз считать 3 байта

?
Если так, что можно выделить часть кода в отдельную функцию и вызывать ее дважды:


bool SYNC_1986_handshake()
{
while (Serial.available()) Serial.read();

for (int i = 0; i < 512; i++) {
Serial.write(0);
}
Serial.flush();

const int NumBytesToReceive = 3; // количество байт которые ждем в ответ

int numBytesReceived = 0; // здесь храним количество принятых байт
for(int istep = 0; istep < 5; ++istep) // делаем несколько попыток считать ответ
{
while (Serial.available() && numBytesReceived < NumBytesToReceive) // читаем все что уже есть во входном буфере
{
char receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
++numBytesReceived; // храним количество принятых байт
}

if (numBytesReceived == NumBytesToReceive) // получили все что нужно
{
return true; // возвращаем успех
}
delay(10);
}

return false; // возвращаем неудачу
}

void SYNC_1986()
{
Serial.end();
Serial.begin(9600);

lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Try to Sync...");
lcd.setCursor(5, 1);

bool isSuccess = SYNC_1986_handshake();
if (isSuccess) // выполняем второй шаг только если предыдущий был успешен
{
isSuccess = SYNC_1986_handshake();
}

if (isSuccess) {
lcd.print("Succes!");
} else {
lcd.print("Failed!");
delay(1000);
menu = 3; //опять вернулись к файлу прошивки
root = SD.open(name_folder + "/");
SD_card(root);
root.close();
}
}



Тогда тут можно и byte использовать?
Вообще в Си нет такого типа. На ардуино это тайпдеф (то есть синоним) char.

SoftFelix
12.02.2020, 23:14
Правильно ли я понял, что нужно
1) отправить 512 пустых байт
2) прочитать 3 байта и обработать ошибку
3) повторно отправить 512 пустых байт, если предыдущий окончился ошибкой.
4) еще раз считать 3 байта
5) и тут обработать возможную ошибку.

- - - Добавлено - - -


Вообще в Си нет такого типа. На ардуино это тайпдеф (то есть синоним) char.
А это (https://doc.arduino.ua/ru/prog/Byte) что?

SoftWareGuy
12.02.2020, 23:28
А это что?
Есть такая фича в Си языке - объявление синонимов для типов, typedef. Где-то в заголовочных файлах ардуино определен тайпдеф byte, который на самом деле unsigned char. Можно вот это почитать если интересно https://oscarliang.com/arduino-difference-byte-uint8-t-unsigned-cha/

- - - Добавлено - - -

Забавно что в доках ардуины написано:

For consistency of Arduino programming style, the byte data type is to be preferred

SoftFelix
13.02.2020, 09:35
Если так, что можно выделить часть кода в отдельную функцию и вызывать ее дважды:
К сожалению, это не работает - всегда пишет "Failed!". Компилятор ошибок не выдал.

SoftWareGuy
13.02.2020, 11:12
всегда пишет "Failed!"
Нужно понять в какой момент случается fail - после первого вызова SYNC_1986_handshake или после второго. Можно временно убрать второй вызов SYNC_1986_handshake просто закомментировать строки


if (isSuccess)
{
isSuccess = SYNC_1986_handshake();
}
и посмотреть что будет.

SoftFelix
13.02.2020, 22:16
Нужно понять в какой момент случается fail - после первого вызова SYNC_1986_handshake или после второго.
Привет. Только вот дома...

Я сейчас смотрю UART терминалкой, которая в BIN или в DEC может выводить. Так вот твой код выполняет только один (первый) хендшейк (видно что отправили 512 нулей), потом сразу Failed!, хотя должен был отправить второй хендшейк, т.к. первый закончился неудачей. Что-то с алгоритмом не то. Ведь этот код...



if (isSuccess)
{
isSuccess = SYNC_1986_handshake();
}


...читается как "если первый хендшейк был удачным, то делаем ещё один". Так? Только второй должен запускаться при неудаче в первом. Напомню, что работающий исходник ПЦшной программы находится в первой мессаге топика (https://zx-pk.ru/threads/31356-voprosy-nachinayushchego-arduinshchika.html?p=1045373&viewfull=1#post1045373).

Ещё я убрал условной компиляцией всю отладку, которая тоже шла в uart. Возможно, ещё из-за этого была проблема. Проверю только завтра на работе.

p.s. Вот бы треминалку со скриптами, которая принимала определённую последовательность байт и выдавала бы сразу тоже что-то определённое. Т.е. эмулировать это контроллер.

SoftWareGuy
13.02.2020, 22:40
Только второй должен запускаться при неудаче в первом
Мой косяк, действительно невнимательно прочитал.
Исправленный код:


bool SYNC_1986_handshake()
{
while (Serial.available()) Serial.read();

for (int i = 0; i < 512; i++) {
Serial.write(0);
}
Serial.flush();

const int NumBytesToReceive = 3; // количество байт которые ждем в ответ

int numBytesReceived = 0; // здесь храним количество принятых байт
for(int istep = 0; istep < 5; ++istep) // делаем несколько попыток считать ответ
{
while (Serial.available() && numBytesReceived < NumBytesToReceive) // читаем все что уже есть во входном буфере
{
char receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
++numBytesReceived; // храним количество принятых байт
}

if (numBytesReceived == NumBytesToReceive) // получили все что нужно
{
return true; // возвращаем успех
}
delay(10);
}

return false; // возвращаем неудачу
}

void SYNC_1986()
{
Serial.end();
Serial.begin(9600);

lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Try to Sync...");
lcd.setCursor(5, 1);

bool isSuccess = SYNC_1986_handshake();
if (!isSuccess) // делаем еще одну попытку если в первый раз не получилось наладить связь
{
isSuccess = SYNC_1986_handshake();
}

if (isSuccess) {
lcd.print("Succes!");
} else {
lcd.print("Failed!");
delay(1000);
menu = 3; //опять вернулись к файлу прошивки
root = SD.open(name_folder + "/");
SD_card(root);
root.close();
}
}

SoftFelix
14.02.2020, 11:20
Исправленный код:
Проверил на реальном железе - код полностью рабочий! А вот почему у меня не работало. Выявлено отладкой.

Функция Serial.available() даже при вычитывании в переменную ( int n = Serial.available(); ) выдаёт всякую фигню о количестве байт в буфере. Очень редко там появляются правильные значения. Т.е. её можно использовать только как логический флаг true-false, сообщающий, что буфер не пуст (или пуст). И вот если буфер не пуст, то его надо вычитывать, что бы определить, сколько же там байт было. Твой алгоритм именно это и делает.

Хотя описание функции такое:



Serial.available()

Функция получает количество байт(символов) доступных для чтения из последовательного интерфейса связи. Это те байты которые уже поступили и записаны в буфер последовательного порта. Буфер может хранить до 64 байт.

SoftWareGuy
14.02.2020, 11:51
Функция Serial.available() даже при вычитывании в переменную ( int n = Serial.available(); ) выдаёт всякую фигню о количестве байт в буфере
Это очень странно.
В самом начале перед отправкой есть очистка входного буфера:

while (Serial.available()) Serial.read();
Если в момент чтения available выдает всякую фигню, значит кто-то засирает входной буфер.

andrews
14.02.2020, 20:25
Сразу предлагал автору отображать в окне консоли Arduino, получаемые от его mcu символы. А чтобы проконтролировать как действует функция Serial.available(), достаточно сделать "заворотку"(проще чем нуль-модем) на UNO board по последовательному порту и написать простенький тест. В том числе в этом тесте можно послать 64 и более символа и посмотреть, как она себя ведет. Могли ведь и китайцы в софте к плате UNO накосячить?

SoftFelix
14.02.2020, 21:53
Если в момент чтения available выдает всякую фигню, значит кто-то засирает входной буфер.
И только не МК на той стороне. С той стороны МК всегда отдаёт 3 байта (0d,0a,3e). Я это проверил в терминалке на ПЦ (подключил МК к ПЦ). Задал отправку 512 нулей в цикле на скорости 9600 и в другом окне (показывает, что принято от МК) через какое-то время (даже при незаконченном цикле 512-ти нулей) появляются три байта от МК - удачная синхронизация. И только ТРИ байта.

Потом я вставил отладку в твой код - вывод принятых байтов на дисплей.



int receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
lcd.print(receivedByte);


Первые три принятых байта были правильными и в нужной последовательности - 0d,0a,3e.

Потом я увеличил количество ожидаемых байтов до 7-ми (ограничение дисплея и ленью возиться с переносом строки - всё делалось за 10 мин до конца рабочего дня).



NumBytesToReceive = 7; // количество байт которые ждем в ответ


И вот тут получилось занятно - после первых трёх правильных байтов, в буфере есть ещё данные, которые доступны для чтения! Там вместилось (до конца строки на дисплее) ещё две повторяющиеся последовательности байтов. К сожалению, я забыл их переписать, теперь только в понедельник. Но МК их 200% не передаёт! Это либо железо Ардуины (очень вряд ли, т.к. я писал тест loop-back'а и там всё принимается нормально.



void ECHO_RUN() { //тест UART'a на loop-back (коротим Rx-Tx)

Serial.end(); //для смены скорости сперва нужно закрыть!
Serial.begin(9600); //настроили скорость UART на 115200

lcd.clear();
while (Serial.available()) Serial.read(); //вот это работает - очищает буфер UART'перед работой!!!

lcd.setCursor(0, 0);
lcd.print("Transmit=");
lcd.setCursor(0, 1);
lcd.print("Receive =");

for (int i = 0; i <= 255; i++) {
Serial.write(i);
lcd.setCursor(9, 0);
lcd.print(i);

if (Serial.available()) { // Если в буфере есть данные
incomingByte = Serial.read();
lcd.setCursor(9, 1);
lcd.print(incomingByte);
}
delay(200);
}

ECHO_TEST();
}


Ещё в своей версии синхронизации, я тоже вставил отладку - печать переменной n, возвращаемой функцией int n = Serial.available();...




void SYNC_1986() { //функция синхронизации с 1986ВЕ91

Serial.end(); //для смены скорости сперва нужно закрыть!
Serial.begin(9600); //настроили скорость UART на 9600

lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Try to Sync...");
lcd.setCursor(5, 1); //подготовим позицию для печати результата ("Failed!" / "Succes!")

while (Serial.available()) Serial.read(); //вот это работает - очищает буфер UART'перед работой!!!

for (int i = 0; i < 512; i++) {
Serial.write(0); //выдали в порт 512 байт 0х00
}
Serial.flush(); //ждём окончания передачи из буфера

int n = Serial.available();

lcd.setCursor(0, 1);
lcd.print(n); //вывод количества байт в буфере.

if (n == 3) {
lcd.setCursor(5, 1);
lcd.print("Succes!");
} else {
lcd.setCursor(5, 1);
lcd.print("Failed!");
delay(1000);
menu = 3; //опять вернулись к файлу прошивки
root = SD.open(name_folder + "/");
SD_card(root);
root.close();
}
}


Так вот в этом случае у меня почти рандомно выводились значения 3,5,7,9,11 (dec). Эти наиболее часто. Очень редко были 17 и ещё выше.

Т.е. всегда нечётное количество байт. Повторюсь: МК на той стороне всегда гарантированно отдаёт только 3 байта.

SoftFelix
16.02.2020, 11:24
Добавил проверку на правильность соответствия приёма трёх байт. Вот так заработает?



bool SYNC_1986_handshake()
{
while (Serial.available()) Serial.read();

for (int i = 0; i < 512; i++) {
Serial.write(0);
}
Serial.flush();

const int NumBytesToReceive = 3; // количество байт которые ждем в ответ
int syncSuccess1986 [3] = {0x0D, 0x0A, 0x3E}; //для проверки ответа от 1986ВЕ91
int numBytesReceived = 0; // здесь храним количество принятых байт
for (int istep = 0; istep < 5; ++istep) // делаем несколько попыток считать ответ
{
while (Serial.available() && numBytesReceived < NumBytesToReceive) // читаем все что уже есть во входном буфере
{
byte receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
if (receivedByte != syncSuccess1986[numBytesReceived]); //парсим принятый байт на правильное совпадение с ответом 1986
{
return false; //байт не совпал - выходим с ошибкой
}
++numBytesReceived; // храним количество принятых байт
}

if (numBytesReceived == NumBytesToReceive) // получили все что нужно
{
return true; // возвращаем успех
}
delay(10);
}

return false; // возвращаем неудачу
}

SoftWareGuy
16.02.2020, 11:45
Вот так заработает?

Есть одна небольшая ошибочка - лишняя точка с запятой в условии if (receivedByte != syncSuccess1986[numBytesReceived]). Это приведет к тому что в случае несовпадения байта выполнится пустая инструкция, а затем будет безусловный return false. Плюс кое-какие стилистические правки я добавил чтобы по возможности избавляться от "магических чисел" и дать возможность компилятору самому вычислять количество байт ответа основываясь на содержимом массива syncSuccess1986 - он обязательно должен быть типа byte иначе оператор sizeof вернет неправильное количество байт.


bool SYNC_1986_handshake()
{
while (Serial.available()) Serial.read();

for (int i = 0; i < 512; i++) {
Serial.write(0);
}
Serial.flush();

byte syncSuccess1986 [] = {0x0D, 0x0A, 0x3E}; //для проверки ответа от 1986ВЕ91
const int NumBytesToReceive = sizeof(syncSuccess1986); // количество байт которые ждем в ответ

int numBytesReceived = 0; // здесь храним количество принятых байт
for (int istep = 0; istep < 5; ++istep) // делаем несколько попыток считать ответ
{
while (Serial.available() && numBytesReceived < NumBytesToReceive) // читаем все что уже есть во входном буфере
{
byte receivedByte = Serial.read(); // можно сохранить это где нибудь чтобы проверить значение
if (receivedByte != syncSuccess1986[numBytesReceived]) //парсим принятый байт на правильное совпадение с ответом 1986
{
return false; //байт не совпал - выходим с ошибкой
}
++numBytesReceived; // храним количество принятых байт
}

if (numBytesReceived == NumBytesToReceive) // получили все что нужно
{
return true; // возвращаем успех
}
delay(10);
}

return false; // возвращаем неудачу
}

SoftFelix
16.02.2020, 11:56
лишняя точка с запятой в условии
Да, я с этим пока часто путаюсь... Получается, в конце if вообще не надо ставить точку с запятой?


он обязательно должен быть типа byte иначе оператор sizeof вернет неправильное количество байт.
Точно - int же у нас 2х-байтовый? Из-за этого?

Спасибо.

SoftWareGuy
16.02.2020, 12:01
в конце if вообще не надо ставить точку с запятой?
Да, верно, после if оно не нужно. За if следует оператор либо блок операторов.


Точно - int же у нас 2х-байтовый? Из-за этого?
Да, sizeof для массива дает не количество элементов в массиве, а весь размер - если тип будет int, то sizeof даст результат 3 (кол. элементов) * 2 (размер int) = 6 байт.

SoftFelix
16.02.2020, 12:05
byte syncSuccess1986 [] = {0x0D, 0x0A, 0x3E}; //для проверки ответа от 1986ВЕ91
const int NumBytesToReceive = sizeof(syncSuccess1986); // количество байт которые ждем в ответ
Для меня пока вот это сложней читается, чем более очевидное (конкретное указание размера массива). Словарный запас Си пока меленький.

- - - Добавлено - - -


За if следует оператор либо блок операторов.
А блок операторов (боле одного действия-команды?) мы выделяем фигурными скобками?

SoftWareGuy
16.02.2020, 12:07
Для меня пока вот это сложней читается, чем более очевидное (конкретное указание размера массива).
Можно делать так:



const int NumBytesToReceive = 3;
byte syncSuccess1986 [NumBytesToReceive] = {0x0D, 0x0A, 0x3E};


Если программист допишет в массив syncSuccess1986 еще пару элементов, то компилятор выдаст ошибку - т.к. размер задан NumBytesToReceive. Но проблема в том, что если программист уберет одно или более элементов из массива, например вот так:



const int NumBytesToReceive = 3;
byte syncSuccess1986 [NumBytesToReceive] = {0x0D};


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

- - - Добавлено - - -


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

SoftFelix
16.02.2020, 12:17
Если программист допишет в массив syncSuccess1986 еще пару элементов, то компилятор выдаст ошибку - т.к. размер задан NumBytesToReceive. Но проблема в том, что если программист уберет одно или более элементов из массива, например вот так:
Вот сейчас это стало очевидным. А если ставить конкретную задачу - "жду только три байта и только их буду проверять" - такой универсальности же не требуется?

SoftWareGuy
16.02.2020, 12:47
А если ставить конкретную задачу - "жду только три байта и только их буду проверять"

Оба варианта будут работать, если второй вариант кажется проще - берите его. Для такой небольшой задачи это не принципиально.