Чтение состояния порта в переменную
- Войдите на сайт для отправки комментариев
Втр, 02/07/2019 - 22:29
byte P0 = (1 << 0);
byte P1 = (1 << 1);
byte P2 = (1 << 2);
byte P3 = (1 << 3);
byte P4 = (1 << 4);
byte P5 = (1 << 5);
byte P6 = (1 << 6);
byte P7 = (1 << 7);
#define D2_INPUT (DDRD &= ~P2)
#define D3_INPUT (DDRD &= ~P3)
#define D4_INPUT (DDRD &= ~P4)
#define D5_INPUT (DDRD &= ~P5)
#define D6_INPUT (DDRD &= ~P6)
#define D7_INPUT (DDRD &= ~P7)
#define D8_INPUT (DDRB &= ~P0)
#define D9_INPUT (DDRB &= ~P1)
#define READ (PIND & P2)+(PIND & P3)+(PIND & P4)+(PIND & P5)+(PIND & P6)+(PIND & P7)+(PINB & P0)+(PINB & P1)
void setup() {
D2_INPUT;
D3_INPUT;
D4_INPUT;
D5_INPUT;
D6_INPUT;
D7_INPUT;
D8_INPUT;
D9_INPUT;
Serial.begin(19200);
Serial.println("Start");
}
void loop() {
Serial.println(READ);
delay(1000);
}
Мне нужно отправить с одной ардуины на другую состояние, к примеру, 8 пинов ардуино.
Если 2 и 9 пины HIGH,остальные LOW, то получаю число 6
Как на второй ардуине мне расшифровать число 6 в формат 100000001
Т.е. упаковывали Вы по портам, а распаковать надо в номера ардуиновсикх пинов, так что ли? Или я недопонял?
Ну да, хотелось бы отправлять не
Serial.println("100000001");а что-то короче,в hEX это 101
Не знаю, Вы как-то сложно придумали. У Вас что за контроллер? 328? Там же можно спокойно прочитать в 4 байта все пины, сколько есть и отправить эти 4 байта. Или другой контроллер?
328, как это сделать?
Только мне не все пины нужны, а определенные.
Сейчас мне убегать надо, если у Вас не получится, скажите, я завтра могу расписать на пример. Но идея такая. Определяете структуру s из 4 байтов. В эти байты пихаете PINA, PINB, PINC и PIND. ОТправляете эти 4 байта (Serial.write(&s, sizeof(s));
на стороне приёмника принимаете эти 4 байта и имеете все пины.
И пофиг, что не все нужны - передавайте все. Оно ж всего 4 байта, делов-то. Зато никаких распаковок - что передали, то и приняли.
Попробуйте, если будет затык, скажите и заходите завтра к обеду, я постараюсь пример сделать.
Irinka, теоретически Вам нужны битовые операции и сдвиги.
Но в данном конкретном случае даже сдвиги, похоже, не нужны:
Если вперемешку пины будут в байте, то через 8-битный union с битовыми полями под состояние пина попонятнее, по-моему.
Спасибо. Попробую разобраться.
А если мне в порт нужно отправить запись:
Serial.println("1234,100100011");
Так же будет неправильно*?
Можно же зашифровать' все что после запятой, а на другой ардуинке расшифровать.
На второй ардуинке нужен формат 1001...
Что такое 1234?
До запятой будет четырехзначное число, адрес устройства которому передаю, после запятой текст сообщения в формате 1000100...
В Ващем примере, Вы передаёте 11 байтов. Я же Вам предлагаю, передавать пять (ну, если с контрольной суммой, то шесть) и никакого разбора на стороне приёмника. Сделайте, Вам понравится. Ну, или скажите, я пример напишу.
Если не сложно, попрошу у Вас пример
CRC можно посчитать XOR-ом всех байтов или посложнее чего найти типа MAXIM Dallas CRC8 (из библиотеки Dallas Temperature). ptrPacket чисто для удобства объявлен.
typedef struct { uint8_t deviceAddr; uint8_t stateDoor01 : 1; uint8_t stateDoor02 : 1; uint8_t stateDoor03 : 1; uint8_t stateDoor04 : 1; uint8_t stateIgnition : 1; uint8_t stateLight : 1; uint8_t stateSound : 1; uint8_t stateGPS : 1; uint8_t CRC; } packet_t; void setup() { packet_t packet; uint8_t* ptrPacket = (uint8_t*) &packet; packet.deviceAddr = 0xF1; packet.stateDoor01 = digitalRead(A1); packet.stateDoor02 = digitalRead(A2); ... packet.stateIgnition = digitalRead(2); packet.stateGPS = digitalRead(8); // packet.CRC = calculateCRC(ptrPacket, sizeof(packet) - 1); Serial.write(ptrPacket, sizeof(packet)); ... }XOR наверное пошустрее будет
когда надо быстро и не заморачиваться, я делаю XOR со сдвигом. Но, заслушаем ЕвгенияП, нам есть чему у него поучица. :)
Это всё красиво, конечно, но слишком сложно для ТС, КМК. ЕвгенийП предлагает значительно проще.) Типа,
А на приёмной стороне уже выделяем нужные биты через маски.
А когда задышит, тогда уже можно будет добавлять - КС, маркер начала и т.п.
Так, ну поехали.
Ваш адрес (четырёхзначное число) помещается в двухбайтовый unsigned. А портов у 328-ой всего три (B, C и D). Поэтому мы будем передавать РОВНО ПЯТЬ байтов. В них поместится адрес и все порты.
Сначала напишем структуру для передачи. Она будет жить в файле «Packet.h». Это файл нужно будет включать И в передатчик, И в приёмник. Лучше включать один и тот же файл (сделать его «библиотекой»), чтобы избегнуть недоразумений, когда в одном месте поменяла, а в другом забыла.
Вот он файл «Packet.h»
#ifndef PACKET_H #define PACKET_H struct AllThePorts : public Printable{ uint16_t address; uint8_t pinB, pinC, pinD; // Читает порты и заполняет структуру void fillUp(void) { pinB = PINB; pinC = PINC; pinD = PIND; } // Печатает структуру для отладки size_t printTo(Print& p) const { return p.print("address=") + p.print(address) + p.print("; pinB=") + p.print(pinB, HEX) + p.print("; pinC=") + p.print(pinC, HEX) + p.print("; pinD=") + p.print(pinD, HEX); } } __attribute__ ((packed)); #endif // PACKET_HЗдесь Вам может оказаться незнакомым приём с использованием Printable (строка №4) и функция printTo (строки №№ 15-22). Не пугайтесь. Это для того, чтобы готовую структуру можно было просто пихать в print / println и система знала как её печатать. Подробно я описывал это технику в отдельной теме.
Функция fillUp нужна, чтобы собрать значения реальных портов в нашу структуру. Это нужно делать перед отправкой структуры другой ардуине.
А так всё понятно? Размер структуры – 5 байтов, можете проверить это, напечатав её sizeof.
Теперь скетч мастера - передатчика. Он просто раз в полсекунды заполняет структуру текущими значениями портов, печатает её в сериал для контроля и отправляет её приёмнику через SoftSerial. Заодно, он инвертирует 13-ый пин, чтобы хоть что-то менялось. На приёмнике мы будем значение этого пина печатать.
Вот текст мастера.
#include <SoftwareSerial.h> #include "Packet.h" const int8_t softRX = 6; const int8_t softTX = 7; SoftwareSerial sSerial(softRX, softTX); void setup(void) { Serial.begin(57600); sSerial.begin(9600); pinMode(LED_BUILTIN, OUTPUT); Serial.println("MASTER console"); delay(100); } void loop(void) { AllThePorts ports; ports.address = 1234; // адрес приёмника ports.fillUp(); Serial.println(ports); sSerial.write((char *) & ports, sizeof(ports)); digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); delay(500); }Приёмник просто ждёт пока структура придёт полностью, а когда придёт – печатает её и заодно печатает значение 13-го пина (как пример для Вас как к пинам добираться). Вот он.
#include <SoftwareSerial.h> #include "Packet.h" const int8_t softRX = 6; const int8_t softTX = 7; SoftwareSerial sSerial(softRX, softTX); void setup(void) { Serial.begin(57600); sSerial.begin(9600); Serial.println("SLAVE console"); } void loop(void) { if (sSerial.available() >= (int) sizeof(AllThePorts)) { AllThePorts ports; sSerial.readBytes((char *) & ports, sizeof(ports)); Serial.println(ports); // // Пример доступа к пинам. Пин 13 - PB5 bool _pin13 = ports.pinB & bit(5); Serial.print("pin13="); Serial.println(_pin13); } }Теперь Вы можете всё это запустить, наблюдать за печатью и попробовать менять на мастере состяния пинов (замыкайте их то на землю, то на питание) - увидите, как их состояния будут правильно передаваться.
------------------------------------------------------
------------------------------------------------------
Для этого в структуру добавим функцию getCRC, которая считает контрольную сумму. Также добавим поле crc и функция setCRC. Последнюю нужно вызывать непосредственно перед отправкой структуры. Она посчитает контрольную сумму и запишет её в поле crc. На приёмнике же нужно посчитать контрольную сумму полученного пакета (функцией getCRC) и сравнить её с полученным полем crc. Если сравнилось нормально, значит с высокой вероятностью ошибок не было.
Вот тексты.
«Packet.h»
#ifndef PACKET_H #define PACKET_H struct AllThePorts : public Printable{ uint16_t address; uint8_t pinB, pinC, pinD, crc; // Читает порты и заполняет структуру void fillUp(void) { pinB = PINB; pinC = PINC; pinD = PIND; } // Нужно вызвать перед отправкой void setCRC(void) { crc = getCRC(); } uint8_t getCRC(void) const { const uint8_t * p = reinterpret_cast<const uint8_t *>(this); uint8_t crc = 0xFF; for (uint8_t n = 0; n < sizeof(*this) - sizeof(crc); n++) { crc ^= *p++; for (uint8_t i = 0; i < 8; i++) crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1; } return crc; } // Печатает структуру для отладки size_t printTo(Print& p) const { return p.print("address=") + p.print(address) + p.print("; pinB=") + p.print(pinB, HEX) + p.print("; pinC=") + p.print(pinC, HEX) + p.print("; pinD=") + p.print(pinD, HEX) + p.print("; crc=") + p.print(crc, HEX); } } __attribute__ ((packed)); #endif // PACKET_HПередатчик
#include <SoftwareSerial.h> #include "Packet.h" const int8_t softRX = 6; const int8_t softTX = 7; SoftwareSerial sSerial(softRX, softTX); void setup(void) { Serial.begin(57600); sSerial.begin(9600); Serial.println("MASTER console"); delay(100); } void loop(void) { static long counter = 0; AllThePorts ports; ports.address = 1234; // адрес приёмника ports.fillUp(); ports.setCRC(); Serial.println(ports); // Кажду десятую посылку портим, чтобы проверить реакцию на ошибку if (++counter % 10 == 0) ports.pinB++; sSerial.write((char *) & ports, sizeof(ports)); delay(500); }Приёмник
#include <SoftwareSerial.h> #include "Packet.h" const int8_t softRX = 6; const int8_t softTX = 7; SoftwareSerial sSerial(softRX, softTX); void setup(void) { Serial.begin(57600); sSerial.begin(9600); Serial.println("SLAVE console"); } void loop(void) { if (sSerial.available() >= (int) sizeof(AllThePorts)) { AllThePorts ports; sSerial.readBytes((char *) & ports, sizeof(ports)); if (ports.getCRC() == ports.crc) Serial.println(ports); else { Serial.print("Transmission error: "); Serial.println(ports); } } }В передатчике я добавил порчу пакета (уже после расчёта crc) на каждой десятой посылке, чтобы убедиться, что приёмник адекватно реагирует на ошибку. Можете запустить и убедиться, что всё работает нормально.
Ну, вроде так, если я нигде ничего не ляпнул. Если что, спрашивайте.
Green, это у меня-то сложный пример? ))
Евгений Петрович, а восстановление по CRC? Для полного Иринкиного щастья )))
Green, это у меня-то сложный пример? ))
Нет, теперь точно не сложный.))
а восстановление по CRC? Для полного Иринкиного щастья )))
Без поллитры? Нее ...
Будет чем вечером заняться. Буду изучать. Большое огромное спасибо за оказываемую помощь.
Green, это у меня-то сложный пример? ))
А у меня-то чего сложного? принтабле что-ли? Так это не про решаемую задачу а про удобство отладочной печати. А по задаче - структура и write c readBytes, где там сложности-то?
Работает. Теперь буду разбираться в коде
Если что, спрашивайте.
Начала разбираться:
#ifndef PACKET_H #define PACKET_H struct AllThePorts : public Printable{ uint16_t address;//переменная адреса 2 Байта uint8_t pinB, pinC, pinD, crc;//переменные 1 Байт //Считываю порты void fillUp(void) { pinB = PINB; pinC = PINC; pinD = PIND; } //Тёмный лес void setCRC(void) { crc = getCRC(); } uint8_t getCRC(void) const { const uint8_t * p = reinterpret_cast<const uint8_t *>(this); uint8_t crc = 0xFF; for (uint8_t n = 0; n < sizeof(*this) - sizeof(crc); n++) { crc ^= *p++; for (uint8_t i = 0; i < 8; i++) crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1; } return crc; } //Конец тёмного леса // Печатает структуру для отладки size_t printTo(Print& p) const { return // почему return здесь, а не перед закрывающей скобкой? p.print("address=") + p.print(address) + p.print("; pinB=") + p.print(pinB, HEX) + p.print("; pinC=") + p.print(pinC, HEX) + p.print("; pinD=") + p.print(pinD, HEX) + p.print("; crc=") + p.print(crc, HEX); } //End Печатает структуру для отладки } __attribute__ ((packed));//Что значит эта строчка? #endif //почему rerurn здесь, а не перед закрывающей скобкой?
Потому, что он относится ко всему, что ниже. Строки №№32-37 это по сути одна строка (return .....;). Просто разнёс на разные строки, чтобы не писать такую длинную. Посмотрите где точка с запятой - одна в самом конце.
Тёмный лес - это стандартная процедура подсчёта 8-битной CRC. Я её не сам придумал, она есть в любом учебнике и, кажется, даже в википедии (сейчас проверю) ....
А мне жутко интересно узнать, но стыдно спросить.... Но спрошу (если что - к нужным букварям отправьте или скажите - тебе ещё рано :-) )
вот эта сокращённая запись:
crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1;
можно ее расшифровать, так сказать?
Потому, что он относится ко всему, что ниже. Строки №№32-37 это по сути одна строка (return .....;). Просто разнёс на разные строки, чтобы не писать такую длинную. Посмотрите где точка с запятой - одна в самом конце.
Поняла.
void loop(void) { AllThePorts ports; ports.address = 1234; // Задала адрес ports.fillUp();//Получила состояние портов ports.setCRC();//Посчитала контрольную сумму строки, состоящей из адреса и трех портов Serial.println(ports); sSerial.write((char *) & ports, sizeof(ports)); }пишу...сейчас кое-что првоерю и отпишусь дальше. Почему-то в протеусе не работает проект
crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1;
можно ее расшифровать, так сказать?
Тернарная условная операция
Почему-то в протеусе не работает проект
Это известный косяк модели в протеусе. Сделайте там не ардуины, а просто ATmega382P - заработает.
В википедии на нашёл, но не суть - это одна из стандартных процедур (есть и другие), я её просто использую готовую.
Вот момент который не понимаю:
В слейве получаю:
получаю в мастере строку 1234108338, но в этой строке не 38 байт же
Я совсем не поняла, с мастера я отправляю всю эту строку
address=1234; pinB=1; pinC=0; pinD=83; crc=38
или только значение переменных 1234; 1 0 83 38
как слейв понимает что 1234 это адрес, 1 это pinB...как просиходит этот "парсинг"?
address=1234; pinB=1; pinC=0; pinD=83; crc=38
address=1234; pinB=1; pinC=0; pinD=83; crc=38
Понятно, что раз 38 значит 38 и должно быть)
Понятно, что раз 38 значит 38 и должно быть)
Правильный подход к пониманию.
Если мастер послал CRC пакета = 38, то и слейв при проверочном расчёте (тем же способом) CRC на основании данных в пакете должен получить 38. Если вышло что-то другое - пакет битый.
Я совсем не поняла, с мастера я отправляю всю эту строку
address=1234; pinB=1; pinC=0; pinD=83; crc=38
или только значение переменных 1234; 1 0 83 38
А Вы ещё мой пример гляньте - он про то же самое, но несколько проще (хотя ЕвгенийП так не считает, конечно).
Если вышло что-то другое - пакет битый.
и если это настоящий crc то запускается процедура воостановления битой информации, а если не удалось, то пакет отбрасывается и отправляется запрос на переотправку битого пакета...
Объясниете, не понимаю:
//Добавила переменную uint16_t text; size_t printTo(Print& p) const { return p.print("address=") + p.print(address) + p.print("; pinB=") + p.print(pinB, HEX) + p.print("; pinC=") + p.print(pinC, HEX) + p.print("; pinD=") + p.print(pinD, HEX) + p.print("; text=") + p.print(text) + p.print("; crc=") + p.print(crc, HEX); } } __attribute__ ((packed)); Отправляю на слейв.....На слейве:
void loop(void) { if (sSerial.available() >= (int) sizeof(AllThePorts)) { AllThePorts ports; sSerial.readBytes((char *) & ports, sizeof(ports)); if (ports.getCRC() == ports.crc){ Serial.println(ports.text); В каком месте в переменную ports.text произошло присвоение отправленного текста? }В каком месте в переменную ports.text произошло присвоение отправленного текста?
В sSerial.readBytes().
В struct у нас как данные хранятся? Последовательно сложенными в линеечку, одна за другой: address | pinB | pinC | pinD | ...
Т.е. фактически - массив байтов, из которых две ячейки отданы address, по одной pinX и т.д.
write() этот массив выплёскивает в сторону слейва, слейв принимает и складывает в такой же массив на своей стороне, на который можно смотреть через шаблон структуры. Как в советском "Шерлоке Холмсе" показано - накладывают на страницу книги черный лист с дырками и читают тайное слово.
Ну, как в каком, Эта переменная - часть структуры, Вы ей на мастере что-то присвоили? А структура передаётеся полностью! Вот она и передалась, как часть структуры.
Только, если Вы хотите добавлять поля в структуру, дробавляйте их ДО crc, а не после. crc должна быть последним полем в структуре. После неё никаких полей, иначе может сломаться подсчёт crc.
А структура передаётеся полностью!
Так понятнее. Спасибо.
__attribute__ ((packed));
Что значит эта строчка?
#ifndef PACKET_H #define PACKET_H struct AllThePorts{ uint16_t address,text; uint8_t pinC, pinB, pinD, crc; void fillUp(void) { pinB = 2; pinC = 3; pinD = 4; } void setCRC(void) { crc = getCRC(); } uint8_t getCRC(void) const { const uint8_t * p = reinterpret_cast<const uint8_t *>(this); uint8_t crc = 0xFF; for (uint8_t n = 0; n < sizeof(*this) - sizeof(crc); n++) { crc ^= *p++; for (uint8_t i = 0; i < 8; i++) crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1; } return crc; } /*size_t printTo(Print& p) const { return p.print("address=") + p.print(address) + p.print("; pinB=") + p.print(pinB, HEX) + p.print("; pinC=") + p.print(pinC, HEX) + p.print("; pinD=") + p.print(pinD, HEX) + p.print("; text=") + p.print(text) + p.print("; crc=") + p.print(crc, HEX); }*/ } __attribute__ ((packed)); #endif // PACKET_HХочу убрать отладочную печать. Где я накосячила?
__attribute__ ((packed));
Что значит эта строчка?
При опциях IDE "из коробки" - ничего, но она страхует от неприятностей, если опции компилятора поменяются. Она требует, чтобы каждый элемент структуры занимал минимально-достаточное место в памяти. А бывает, что каждый элемент занимает целое количество int'ов (или даже long'ов) - это от опций зависит. Но прямое указание в программе имеет более высокий приорите, чем опции. Потому, лучше указать и не думать какие там опции выставлены.
Хочу убрать отладочную печать. Где я накосячила?
Вроде, нигде. Только в print пихать уже нильзя. А что, ругается? Что за ругань?
Точно, на это и ругался //Serial.println(ports);
Спасибо
Irinka, а вот залетел к вам (в вашу сеть) один лишний байт из вне и что? Всё, накрылась ваша богодельня? Где начало, где конец, где данные, где КС...?