Считывание последовательных данных
- Войдите на сайт для отправки комментариев
Хочу считать девять байт от стороннего устройства. Частота передачи 25 КГц (такт 40 мкС). Устройство генерит по нажатию на кнопку последовательную передачу 9 байт. Прога опрашивает порт и после приема стартового бита, после задержки заполняет буфер полученными данными. Байт должен быть заполнен числом 01010101 (55H). Получается совсем другая картинка.
int TX=17; //передача
int RX=14; //прием
int i; //переменная цикла
int j; //переменная цикла
unsigned long time0;
unsigned long time1;
unsigned long time2;
unsigned long time3;
unsigned long time4;
byte B[9]={0,0,0,0,0,0,0,0,0}; //
//
void setup(void) {
pinMode(TX, OUTPUT);
pinMode(RX, INPUT);
Serial.begin(9600);
}
void loop() {
Serial.println("Wait press ");
Serial.println()
while (digitalRead(RX)!= HIGH) {};//ожидание стартового бита
time0 = micros();
delayMicroseconds(8);//задержка от фронта стартового бита
time1 = micros();
delayMicroseconds(40);//задержка для попадания в следующий слот
time2 = micros();
for (i=0;i<9;i++){ //заполнение буфера
for (j=0;j<7;j++)
{if (digitalRead(RX)== HIGH){
bitWrite(B[i], j,1);
}
else {bitWrite(B[i], j,0);
}
delayMicroseconds(40);
}
}
Serial.print("time1="); Serial.println(time1-time0,DEC); // вывод показаний таймера для проверки времени выполнения команды
Serial.print("time2="); Serial.println(time2-time1,DEC);
for (i=0;i<9;i++){
Serial.print("B");Serial.print(i,DEC);Serial.print("=");Serial.print(B[i],BIN);Serial.println();// вывод значений полученных байт
}
delay(1000);
Serial.println("Was signale");
Serial.println();
//
}
Получается совсем другая картинка.
Wait press
time1=12
time2=40
B0=10011
B1=1001001
B2=100100
B3=110010
B4=1011011
B5=1101101
B6=10110
B7=0
B8=0
Was signale
Wait press
time1=8
time2=48
B0=10010
B1=1001001
B2=100100
B3=110110
B4=1011011
B5=1101101
B6=10110
B7=0
B8=0
Was signale
Wait press
time1=12
time2=40
B0=1011011
B1=1101101
B2=110110
B3=10010
B4=1001001
B5=100101
B6=10110
B7=0
B8=0
Was signale
Wait press
Что делать? Писать на АСМе обработчик?
Спасибо за отклик!
Но не помогло. в распечатке содержимого буфера, в первом байте В1=11, в остальных - нули. Попробывал учитывать задержку в самом цикле. Буфер стал наполняться содержимым, но не тем. Уменьшил количество считываемых байт,
История - ледянящая кровь!!! ;)
---
По делу: Я прошу обратить внимание на то, как устроена передача в аналогичных ситуациях профессионально, Например УАРТ. там есть стартовый бит, стоповый и длина посылки не более 8 бит. Именно для того, чтобы не потерять синхронизацию.
Вы собираетесь принять 9*8=72 бита без контроля синхронизации, полагаясь лишь на веру в то, что 40 мкс на процессре - такие же, как и на приборе.
Удивительная доверчивость! Вас в юности цыгане на "погадаю" не разводили? -Шутка... если что.
Без синхроимпульсов есть один проверенный десятилетиями метод - избыточность частоты.
Делается так: Вы пишите в буфер с частотой в 3-4 раза выше необходимой. По окончания приема разбирате буфер приема. 3-4 единицы подряд - "1", больше - уже вторая единица пошла и так далее. Много 1 или много 0 подряд все равно могут вызвать ошибку "бит-фрейма" Но уже сложнее.
Подробнее разжевывать не стану. Вроде все понятно. Чем выше частота, тем легче точнее можно определить бит-фрейм. В идеале писать по прерыванию таймера. Тогда Вы будете точно уверены в частоте семплов. Делаете таймер1 на 5, например, мкс. Это 80 тактов. Каждый раз по достижении 80 у вас вызывется прерывание, в нем Вы читаете бит порта и пишите его в бит буфера. Таймер сам продолжит отсчет. Важно, чтобы за 80 тактов вы успели записать бит в буфер. На это время стоит остановить таймер0, который миллис считает. Не хватает времени? - сделайте на 10 мкс.
Попробуйте написать это самостоятельно, ничего, кроме основ програмирования и знания аглицкого, для чтения даташита, Вам не понадобится.
-----
если можете влезть в устройство - то идеально было бы добавить синхру или разбить передачу по байтно. Если нет, то только так.
Устройство я делаю сам, так что то, что оно будет передавать и как будет передавать - это зависит от моей хотелки. Можно подумать по УСАРТ...
Тогда передавайте, как в SPI, с синхрой. Для этого есть примитивы в Ардуино: shiftIn() и shiftOut(). И никаких проблемм не будет.
----------------
добавлю: если сами делаете, но хотите провода экономить - смотрите на 1-wire. Есть в сети много вариантов софтовой реализации. Там смысл в том, что приемник сам синхронизирует передачу: я опускаю линию на короткое время и поднимаю снова и жду, что сделает передатчик - опустит линию или нет. Очень легко реализовать.
Передача данных по 485. Конечно можно извратится и по принципу одноволновых, но уж это совсем...
Если написать обработчик на АСМе? Как передать в основную прогу то, что получит обработчик?
Для связки Ардуино и 485 все уже 100500 раз написано. Зачем Вы велосипедостроением стали заниматься? То есть, если стали, то Вам, вероятно, нужно. Просто Вы не пояснили - зачем?
А про асм - читайте про ассемблерный код внутри gcc. Это горазно правильнее, чем отдельно писать, тогда компилятор свободно интегрирует С-шный код и ассемблерный, с точки зрения распределения регистров и много чего еще. Прямо в поиске вбиваете "avr-gcc asm" - и читаете все подряд, пока не постигните дзен.
Поясняю. :)
Связать 485 и любой МК- нет проблем. Электрических, так сказать. Как обрабатывать данные , которые приходят на порт МК? Приходят побитно от любого девайса. К примеру - от моего. Сейчас я выставил на передачу для тестировки процесса передачи следующую последовательность: стартовый бит, далее сколько то байт (это уже не так важно) младшим битом вперед. По идее, заполнив побитно буфер приемника, будет отображаться содержимое принятого байта- 55Н. А этого не происходит. Моя железяка генерирует железно - смена бит происходит через 40 мкс. Не такая уж и большая скорость передачи. Значит, неграматно пишу обработчик. Или принципиально невозможно решить эту задачу данным способом. Сейчас обдумываю применение UART. Можно ли его использовать на других портах, к примеру. и как им пользоваться. Как всегда на первой стадии - почти ничего не понятно.
Спасибо за наколку avr-gcc asm - стало понятно, как сформулировать критерии поиска.
Визуально, у Вас скорость передачи в два раза выше. То есть вы передете бит не за 40мкс, а за 20.
Раз у Вас стартовый бит высокого уровня то можно первый байт использовать для синхронизации. Он должен быть всегда 01010101b. Примерно так:
На скорую руку, конечно, можно по УАРТу передать/принять, пока не напишу свой обработчик.
Я чисто для понимания процесса. Зачем свой обработчик? Если для собственного развития то совет - посмотрите как реализован software serial. Уже реализовано несколькими способами то что вы хотите. И работает. Если для запутывания иностранных шпионов .... Люди уже много раз наступили на грабли передачи информации между устройствами. Придумали много разных шин и способов обеспечить помехозащищённость. Надо ли изобретать велосипед?
Ну , мало ли у кого какие тараканы! Мне вообще то нужен результат. Сейчас как раз юзаю эту библу-. К сожалению (скоро перезжаю) приходится делать некоторые операции (буквально) на коленке. Типа, пихать в программатор чип. Прохождение некоторых операций приходится отслеживать косвенно, типа, запрограммировал девайс, а что там с него идет... Только косвенно. Ну да ладно, разберемся.
Да! Что бы небыло лишних поводов тыкнуть палкой! Сейчас выходной сигнал стандартизировал под передачу по нормальному - 1 высокий, 0 - низкий.
Перепрограммировал девайс. По умолчанию UART работает в режиме 8N1. А как это проверить? Ардуина девайс видит, но данные идут с искажениями - видимо частоты плавают. Чуть попозже выложим результаты. А как режим 8N1 поменять - нигде указаний не нашел. В описании библиотек сказано, что режим этот задается по умолчанию. Полазил по форумам - тот же результат.
USART Control and Status Register n C – UCSRnC
Bits 5:4 – UPMn1:0: Parity Mode
Bit 3 – USBSn: Stop Bit Select
Bit 2:1 – UCSZn1:0: Character Size
В любом даташите AVR! Только для Вас! Любая мода на выбор.
Ни разу не наблюдал ошибок нормально настроенного UART. Если плывёт частота, просто надо снизить скорость. Всегда можно подобрать режим где ошибок нет.
Ни разу не наблюдал ошибок нормально настроенного UART. Если плывёт частота, просто надо снизить скорость.
А вот я недавно наблюдал. По стандарту скорость должна быть 31250. И снижать ее, естественно, нельзя. Да и невозможно. Удалось избавиться от ошибок при установки частоты в 31500.
Да, кстати, частота там вместо расчетной 440 Гц выдавалась 437 Гц. Видать такой кварц (или керамика).
Всегда можно подобрать режим где ошибок нет.
Вот с этим соглашусь. Правда, я нескольколько вечеров бился, пока догадался, что нужно поварьировать скорость.
Конечно согласен! Тоже прикидываю, насколько можно упасть в скорости обмена, что бы не совсем заморачиваться с абсолютной синхронизацией (время сдачи поджимает). Спасибо за строчку "USART Control and Status Register n C – UCSRnC". Я то спрашивал, есть ли настройки типа mySerial.begin(19200, XXX), где ХХХ - установка режима обмена. Во всяком случае, в описании библиотеки, приводится именно такая конструкция. Сегодня прикину, насколько выгоднее понизить скорость что бы избежать большого рассогласования. В том смысле, что учту "гуляние" частот девайсов. В даташитах есть указание - рассогласование в битрейтах(аж поморщился) - не более 1,5%. Понижение битрейта равносильно повышению частоты ЗГ девайсов, так что процент рассогласования будет неизменно падать.
" В любом наборе исходных данных самая надежная величина, не требующая никакой проверки, является ошибочной." . Сменил кварц, поставил в два раза больший по частоте. Все работает, скорость обмена установил 38400. Как говорится, спасибо, за поддержку! Бум теперь организовывать обмен по УАРТу.
Я же приводил выше самосинхронизирующийся код
Я недавно строил программый сериал на tiny25. Для подбора констант задержки запускал в цикле изменение и вывод величины текущей задержки. Прикольно было наблюдать как постепенно исчезают ошибки передачи а потом опять появляются до полной потери информации. Без ошибок принималось +-10% на 19200.
Вообще то я железячник, поэтому и смотрю на некоторые тексты программ с некоторым недоверием. Для меня важен железный результат, чтобы девайсы работали без излишних костылей, программных или железячных. Ну невозможно объять сразу все и все помнить! Особенно когда бегаешь по форумам и натыкаешься на одни и те же ссылки, на одни и теже неясночти. Вот к примеру, Stream.readBytesUntil(character, buffer, length) Все вроде бы ясно. Но не все length - типа int. Значит ли это, что можно принимать одним пакетом 65536 байта? Я так понимаю, что пересылка идет сплошным потоком, без разделительных старт/стопных бит?
Во-первых, если int, то все же 32767 максимум. Во-вторых, если размер буфера (объем оперативой памяти) позволяют, то можно и гигабайты одним вызовом функции в буфер прочитать.
Любая передача данных - это всегда стек протоколов. Начиная с протоколов физического уровня (токовая петля, фазовая модуляция, частотная модуляция, отсутствие модуляции и т.п.) и бесконечное количество протоколов более высокого уровня. Причем последние могут еще и инкапсулироваться друг в друга. Например, если физический уровень позволяет нам передавать и принимать байты, то ничто, кроме недостатка ресурсов МК, не мешает нам развернуть на этот уровень PPP/IP/TCP/HTTP/SSL/IP/TCP/HTTP/SSL и так до бесконечности.
Сильно ли Вы задумываетесь, когда читаете эту страницу, о том, какие протоколы физического и нижнего логического уровня были использованы для доставки содержимого этой страницы к Вам на компьютер и для отображения ее на мониторе?
Аналогично и для класса Stream. Метод readBytesUntil может быть перегружен и унаследован множество раз. И с какого уровня стека протоколов он берет данные, зависит от конкретного объекта, который Вы создали и его свойств. Причем сам объект, в случе инкапсуляции протоколов, может даже не знать ничего о том, на каком уровне в стеке протоколов оказался тот протокол, которым он воспользовался.
У меня железка - работает, как оконечное устройтсво, типа кнопочки и лампочки. В этом девайсе, нет ни ОС, нет вычурных построений. Девайс работает железно- по железной логике, которую прошиваю на программаторе. Стыкуется с Ардуиной, на котором собран другой девайс. Физически состыковать два девайса - не сложная задача. Ардуина должна принимать пакет данных в 10 байт. Стартовый бит, потом 10 байт инфы и стоповый бит. Ардуина должна посылать следуюшую последовательность сигналов - стартовый бит, затем 6 байт инфы, стоповый бит. ВОзможно ли это сделать с помощью Stream.readBytes(buffer, length)? В описании методов, потоков практически на всех форумах одни и теже слова. Ситуация, которую описал Лем с Йоном Тихим. когда он попытался узнать, что такое сепуление.
Или процесс заполнения буфера в УАРТ происходит СТРОГО по 8бит? С обрамлением старт/стоповых бит?
А вы уже обратили внимание, что в коде для перехода к следующему биту используется delay? Не удивляйтесь, что даже при малых объемах у вас будет вся линейка съезжать.
40 мкс вы подождали, дальше какая-то инструкция. Она тоже кушает время. В итоге вы подождали по факту больше, чем надо. Как решить? Ну вы можете читать таймер, а не пропускать время. В цикле читаете таймер и сравниваете его с расчетным, по которому вы ожидаете бит.
Вы и сами можете прикинуть: если ваш МК допутим 16 мегагерц, то он выполнит до 16 инструкций за 1 мкс. Учитывая то, что МК у вас скорее всего восьмибитный, то это еще больше усугубляет положение по точности ожиданий.
Кроме того, вы не учли, что испульсы могут быть не совсем прямоугольными. Читать их нужно не в начале, а ближе к середине импульса, чтобы уж точно взять корректное значение, а не случайно получившееся на краю. Код я вам писать не буду, но на фундаментальные ошибки указал. Справитесь.
ВОзможно ли это сделать с помощью Stream.readBytes(buffer, length)?
Все возможно, перегрузив имеющийся класс своей реализацией нужных методов. Но новичкам я это не советую, так как перегрузка классов требует хорошего понимания не только того, что Вы делаете, а и того, что в преспективе Вам потребуется получить. Поэтому рекомедую обойтись вообще без объектно-ориентированного программирования. Для начала просто реализовав нужный Вам функционал в рамках C. А когда и если будет необходимость - оформите уже в виде класса на C++.
40 мкс вы подождали, дальше какая-то инструкция. Она тоже кушает время.
Я уже написал ему выше код, в котором данная проблема решена.
40 мкс вы подождали, дальше какая-то инструкция. Она тоже кушает время.
Я уже написал ему выше код, в котором данная проблема решена.
Ничего страшного, я лишь явно указал на узкие места. Это поможет человеку избежать подобных просчетов впредь.
Кроме того, вы не учли, что испульсы могут быть не совсем прямоугольными. Читать их нужно не в начале, а ближе к середине импульса, чтобы уж точно взять корректное значение, а не случайно получившееся на краю. Код я вам писать не буду, но на фундаментальные ошибки указал. Справитесь.
Именно учел. Я - железячник. Фронты импульсов не завалены ибо формируются неким amd485, с обоих концов физической передачи. Ошибки передачи контролируются CRC8 (да, девайс восьмибитный, как было написано выше).
Собственно связал два девайса: Ардуино Уно Р3 и свое устройство, выровнил все задержки.
Скетч посылает на девайс байт, девайс полсекунды его держит и отправляет на Ардуино. В принципе, это и требовалось на этом этапе проектирования/изготовления - обеспечить устойчивую связь.
На монитор отправляется для сравнения то, что я посылаю в девайс и то, что девайс отправляет обратно. Вопрос следующий. При первом включении или при сбросе на Ардуине прием первого полученног байта отображается неправильно.
При включении монитора:
byte
incomingByte = i;
// while
(mySerial.available()){
// mySerial.flush();
И? инициализируем
byte
incomingByte = i; , убираем проверку заполнености буфера?
При первом обращении к УАРТу
mySerial.read(); считывает содержимое буфера. Вместо ожидаемого содержимого H=1, там находится H=2F. С чем это связано? Настройки УАРТа все по умолчанию, в последующих циклах передачи/ прием все отображается ожидаемо.И? инициализируем
byte
incomingByte = i; , убираем проверку заполнености буфера?
При первом обращении к УАРТу
mySerial.read(); считывает содержимое буфера. Вместо ожидаемого содержимого H=1, там находится H=2F. С чем это связано? Настройки УАРТа все по умолчанию, в последующих циклах передачи/ прием все отображается ожидаемо.byte
incomingByte = i; // это должно быть очевидно
убираем проверку заполнености буфера?// зачем тебе проверять наличие в приёмном буфере чего-то, если ты перед этим отправил что-то в сериал - очевидно же, что в буфере что-то будет всегда.
Буфера на передачу и прием - разные. Это отдельные регистры- устройства. Причем, независимо от реализации микро или процессорной платформы. Именно поэтому:
"byte
incomingByte = i; // это должно быть очевидно" - очевидно только сокращение исходного кода, что в данном конкретном случае не так важно.
При первом обращении к УАРТу
mySerial.read(); считывает содержимое буфера входных данных(если уж становиться на позицию буквоедства). Вместо ожидаемого содержимого H=1, там находится H=2F. - так почему этакое происходит?Буфера на передачу и прием - разные. Это отдельные регистры- устройства. Причем, независимо от реализации микро или процессорной платформы. Именно поэтому...
ты сделал
byte
incomingByte = i;
прежде чем здесь играть в Капитан Очевидность? - мне лично пофиг, зачем ты тупишь, но если ты не станешь сотрудничать, останешься наедине со своими глюками.И получаем... кто возьмет билетов пачку....
Ещё какие тыкалки будут?
Ещё какие тыкалки будут?
добавь в сетап:
не при вызове не чистится, а при инициализации там уже что-то есть.
Спасибо, учтем. Эффект интересный, но пока неопасный.
Уха-ха! Это Ардуино в первый раз посылает в девайс что то, что лежит в буфере при включении или при ресете! Девайс, как железяка, отсылает то, что получил. Как почистить выходной буфер, после первой инициализации?
Уха-ха! Это Ардуино в первый раз посылает в девайс что то, что лежит в буфере при включении или при ресете! Девайс, как железяка, отсылает то, что получил. Как почистить выходной буфер, после первой инициализации?
прекращай тупить - проверяй этим:
Программа работает следующим образом. На девайс посылаются 1 .. 6. Получаем 11 байт от девайса. В девайсе первоначально байты заполнены от 11 до ВВ. Девайс принимает байты , записывает их себе в буфер и посылает в Ардуину.
Получаем на мониторе...
А это без словечка их четырех букавок (void)
SoftwareSerial mySerial(10, 11); // RX, TX
https://www.arduino.cc/en/Tutorial/SoftwareSerialExample
Ещё есть какие нибудь мнения ?
SoftwareSerial mySerial(10, 11); // RX, TX
https://www.arduino.cc/en/Tutorial/SoftwareSerialExample
Ещё есть какие нибудь мнения ?
есть мнение, что ты кретин
15
pinMode(TX, OUTPUT);
16
pinMode(RX, INPUT);
и, это ты не делаешь - нужно ждать, пока не расдуплится сериал
Может быть, остряк. ТХ - передающий пин. Тип - ВЫХОД, RX- приемный пин. Тип - ВХОД. Хоть убейся ап стену. Короче - не знаешь, не пищи и не пиши. При твоем раскладе - обмен вааще работать не будет.