Связь nano и pro mini по RX-TX - проблема
- Войдите на сайт для отправки комментариев
Не смог найти ответа на свою проблему в недрах Интернета.
Проблема в следующем - для проекта нужна связь между двумя клонами Ардуино - nano 328, 16 Mhz и pro mini 328, 16 Mhz.
Скетч уже более 2000 строк, но суть передать с nano на pro mini до 5 байт информации с разной переодичностью.
В начала проекта, особо не обращал внимания на правильность и аккуратность и в итоге, после небольшой серии экспериментов зараьботало так:
- на передающей ардуине стоит скорость 9600, байты передаются из программы каждый с помощью Serial.write (byte), то 1, то 2-3, то все 5 с периодичностью от 100 мс до нескольких секунд.
- На принимающей стоит скорость 19200 (!!!) и байты принимаются 5 раз Serial.read(), как только становятся доступными if (Serial.available() > 0), байты неполученные получались как 255
Все работало и я как то не озадачивался почему это работает в таком виде.
Настал момент причесывания и все пошло наперекосяк. На скорости на обоих ардуинах 9600 или 19200 - не работает в принципе - вместо посланных байтов доходят не те. Вернул разные скорости, написал функцию, где послыка идет строго 5 байт и прием идет строго по количеству байт в буфере - работает нестабильно, постоянно проскакивают неконятные байты. Добавил Serial.flush () после пяти посылов байт Serial.write() - так же нестабильно. Или вообще мусор или ноль проскакивает или 255.
Никто не сталкивался? Или кинте ссылкой где почитать о похожей проблеме.
есть одно НО - на nano выгорел диод B2 от неправильного включения и она питается только от VCC. Не думаю, что это как то влияет, но все же.
Буду очень признателен за помощь. Ниже - куски кода
Передающая:
.......
sendToLED (boardNum, 0, 0, 0, 0);
......................
sendToLED (0, 0, 0, 'T', '*');
.........
во первых скорость должна быть одинаковая, во вторых возьми готовую библиотеку какую...
какую...
какую?
да хоть вот эту :)
#ifndef BUS_RS485_H #define BUS_RS485_H #include <Arduino.h> #include <inttypes.h> #include <Print.h> #include <wiring_private.h> // буфер Serial может хранить 64 байта или 16, заголовок пакета 8 байт, по этому размер буфера дополнительных данных // 56 или 8 байт, такой малый размер буфера надо учитывать !!! // Рекомендуемый размер дополнительных данных не более 24 байта (2 полных кадра в буфере) // данная библиотека не будет работать с малым размером буфера для шины RS485, пин управления будет отключен !!! #if (RAMEND < 1000) #define MAX_BUFFER 8 #else #define MAX_BUFFER 56 #endif // описание пакета, данные передаются и принимаются именно в такой последовательности, // длина заголовка пакета всегда 8 байт // длина дополнительных данных от 0 до 24 байт typedef struct { uint8_t Mark; // маркер начала кадра uint8_t Id_Destination; // ID получателя пакета uint8_t Id_Source; // ID отправителя пакета uint16_t Control_1; // Два байта контроля пакета (сделано побайтно для удобства применения битовых операций), состав: uint16_t Control_2; // 1...4 - 4 бита, номер команды // 5...8 - 4 бита, всего кадров (пакетов) в команде // 1...4 - 4 бита, номер текущего кадра (пакета) в команде // 5 - 1 бит, ошибка // 6 - 1 бит, ожидание // 7 - 1 бит, повтор // 8 - 1 бит, подтверждение uint8_t SizeDate; // Размер дополнительных данных пакета uint8_t CRC_Data; // CRC дополнительных данных пакета uint8_t CRC_Head; // CRC заголовка, служит для определения корректности заголовка и выделения кадра из потока данных uint8_t Data[MAX_BUFFER]; // дополнительные данные, рекомендуемый размер не более 24 байт, максимальный размер 56 байт } BusRS485_Pack; // описание параметров ожидаемого пакета, используется для фильтрации пакетов typedef struct { boolean OnReply; // ожидаем, или нет, ответный пакет int TimeOut; // время которое будем ожидать ответный пакет unsigned long StartTime; // время начала ожидания ответного пакета uint8_t Id_Destination; // ID получателя пакета uint8_t Id_Source; // ID отправителя пакета uint8_t Control_1; // Номер команды и количество кадров в команде uint8_t Control_2; // Номер пакета (младшие 4 бита) voidFuncPtr Reply_OnStep; // Пользовательское событие возникающее при получении ответного пакета } Reply_Pack; class BusRS485 { private: // -------------------------------------------------------------- // данные класса HardwareSerial *port; // указатель на Serial class uint8_t Id_Unit; // 0=master, 1..240=slave, 247=резерв uint8_t MaskRW; // маска потра для получения и отправки данных unsigned int PauseWrite; // синхро пауза после перехода в режим write и до начала передачи первого байта BusRS485_Pack InPack; // входящий пакет Reply_Pack Reply; // ожидаемые данные и правила их обработки voidFuncPtr OnSlave; // Пользовательское событие возникающее при получении не ожидаемых данных слейвом от мастера uint8_t Echo; // Вариант эхо входящих данных // 0 - нет эхо // 1 - эхо всех считаных байт // 2 - эхо всех распознаных пакетов // 3 - эхо только пакетов предназначеных для устройства uint8_t FInPack; // флаг входящего пакета // 0-ждем заголовок // 1-ждем дополнительные данные // 2-пакет загружен валиден // 3-пакет загружен, но не валиден uint8_t FStatus; // битовый флаг статуса контроллера RS485 // 0 - не верный ID // 1 - слишком маленький буфер // 2 - конфликт ID в сети // 3 - выключен RS-485 // 4 - <свободно> // 5 - <свободно> // 6 - прочая не критическая ошибка // 7 - прочая критическая ошибка // -------------------------------------------------------------- // локальные процедуры uint8_t CRC(uint8_t, uint8_t); // возвращает true если данные отправлены, и false если ошибка boolean send(uint8_t, // ID получателя uint8_t, // номер функции uint8_t, // номер кадра в команде uint8_t, // всего кадров в команде boolean, // флаг ошибки boolean, // флаг ожидания boolean, // флаг повтора boolean, // флаг подтверждения uint8_t, // размер дополнительных данных uint8_t *); // указатель на буфер дополнительных данных public: // -------------------------------------------------------------- // конструктор и процедуры инициализации класса BusRS485(uint8_t, // номер порта Uart, =0, для Меги доступны =0, =1, =2, =3 uint8_t, // пин контрольного тригера получения данных интерфейса RS485 uint8_t); // пин контрольного тригера отправки данных интерфейса RS485 void InitEnd(void); void InitMaster(long Speed); void InitSlave(long, // скорость порта uint8_t, // Id слейва void (*)()); // пользовательская функция для обработки получения неожидаемого пакета от мастера void SetEcho(uint8_t); // устанавливает режим эхо // -------------------------------------------------------------- // публикуемые "свойства" класса uint8_t Id (void); uint8_t Status (void); // -------------------------------------------------------------- // публикуемые "свойства" ожидаемого пакета boolean OnReply (void); boolean OnReply (boolean); uint8_t ReplyId (void); uint8_t ReplyNumFunc (void); int TimeOut (void); unsigned long StartTime (void); // -------------------------------------------------------------- // публикуемые "свойства" входящего пакета uint8_t Id_Destination (void); uint8_t Id_Source (void); uint8_t SizeDate (void); uint8_t *Date (void); uint8_t NumFunc (void); uint8_t NumCadr (void); uint8_t CountCadr (void); boolean FError (void); boolean FWait (void); boolean FRetry (void); boolean FValidate (void); // -------------------------------------------------------------- // доступные процедуры класса void poll(); // процедура запуска обработки получения данных void onRead(); // включение шины на прием void onWrite(); // включение шины на передачу void off(); // выключение шины uint8_t Read(); // чтение с UART контроллера 1 байта, с учетом включеного/выключеного эха // простая команда из одного пакета без ожидания ответа // вызывается только на мастере void Command(uint8_t, // Id получателя пакета uint8_t, // номер функции uint8_t, // размер дополнительных данных, не более 24 uint8_t *); // указатель на буфер дополнительных данных // начало команды из нескольких пакетов // вызывается только на мастере void Command(uint8_t, // Id получателя пакета uint8_t, // номер функции uint8_t, // всего кадров в команде uint8_t, // размер дополнительных данных первого пакета uint8_t *, // указатель на буфер дополнительных данных первого пакета int, // время в которое будем ожидать ответ на первый пакет unsigned long, // время начала ожидания ответного пакета void (*)()); // процедура вызываемая при получения ответа // пакеты из сложной команды // вызывается и на мастере и на слейве из обработчиков получения ответов void Command(uint8_t, // Id получателя пакета uint8_t, // номер функции uint8_t, // номер кадра в команде uint8_t, // всего кадров в команде boolean, // флаг ошибки boolean, // флаг ожидания boolean, // флаг повтора boolean, // флаг подтверждения uint8_t, // размер дополнительных данных первого пакета uint8_t *, // указатель на буфер дополнительных данных первого пакета int, // время в которое будем ожидать ответ на первый пакет unsigned long, // время начала ожидания ответного пакета void (*)()); // процедура вызываемая при получения ответа }; #endif#include "BusRS485.h" // размер буфера дополнительных данных // размер 24 выбран для размещения в стандартном буфере двух полных пакетов // что бы уменьшить потери при задержках в обработки поступившего пакета #define MIN_BUFFER 24 // маркер начала кадра (введен для стабильности распознования кадров), всегда $ED, B11101101, 237 #define MARK 237 //-------------------PUBLIC FUNCTIONS------------------------------------------ // конструктор для мастера BusRS485::BusRS485(uint8_t PortNum, uint8_t CPinRx, uint8_t CPinTx) { FStatus = 0; this->Echo = 0; if (MAX_BUFFER < MIN_BUFFER) { FStatus = FStatus | B00000010; } switch( PortNum ) { #if defined(UBRR1H) case 1: port = &Serial1; break; #endif #if defined(UBRR2H) case 2: port = &Serial2; break; #endif #if defined(UBRR3H) case 3: port = &Serial3; break; #endif case 0: default: port = &Serial; break; } MaskRW = 0; // MaskRWI = 0; if (CPinRx > 1 && CPinRx < 7) { pinMode(CPinRx, OUTPUT); bitSet(MaskRW, CPinRx); } if (CPinTx > 1 && CPinTx < 7) { pinMode(CPinTx, OUTPUT); bitSet(MaskRW, CPinTx); } if (CPinRx > 1 && CPinRx < 7 && CPinTx > 1 && CPinTx < 7 && CPinTx != CPinRx) {;} } void BusRS485::InitEnd(){ // закрытие соединения off(); port->flush(); port->end(); FInPack = 0; } void BusRS485::InitMaster(long Speed){ // скорость порта // это мастер !!!! PauseWrite = (10000.0 / 9600 * 1000) // это время на отправку 10 бит эха в порт USB со скоростью 9600, // нужна в случае включенного эха на устройствах для которых предназначена передача // это время через которое устройства шины перейдут из режима Write в режим Read + (10000.0 / Speed * 1000); // это время на отправку 10 бит в порт 485 с установленой скоростью, // такая пауза обеспечивает синхронизацию на физическом уровне (удаление ложного бита начала посылки) Id_Unit = 0; port->begin(Speed); port->flush(); onRead(); delay(100); FInPack = 0; } void BusRS485::InitSlave(long Speed, // скорость порта uint8_t Id, // Id слейва void (*_OnSlave)()){ // пользовательская функция для обработки получения неожидаемого пакета от мастера PauseWrite = (10000.0 / 9600 * 1000) // это время на отправку 10 бит эха в порт USB со скоростью 9600, // нужна в случае включенного эха на устройствах для которых предназначена передача // это время через которое устройства шины перейдут из режима Write в режим Read + (10000.0 / Speed * 1000); // это время на отправку 10 бит в порт 485 с установленой скоростью, // такая пауза обеспечивает синхронизацию на физическом уровне (удаление ложного бита начала посылки) if (Id <= 0 || Id > 240) { // ошибочный ID FStatus = FStatus | B00000001; this->Id_Unit = 241; } else { this->Id_Unit = Id; } OnSlave = _OnSlave; port->begin(Speed); port->flush(); onRead(); delay(100); FInPack = 0; } //------------------------------------------------------------------ // простая команда из одного пакета без ожидания ответа void BusRS485::Command(uint8_t Id, // Id получателя пакета uint8_t Func, // номер функции uint8_t SizeDate, // размер дополнительных данных, не более 24 байт uint8_t *Data){ // указатель на буфер дополнительных данных Reply.OnReply = false; if (this->send( Id, // ID получателя Func, // номер функции 1, // номер кадра в команде 1, // всего кадров в команде false, // флаг ошибки false, // флаг ожидания false, // флаг повтора false, // флаг подтверждения SizeDate, // размер дополнительных данных &(Data[0])) // указатель на буфер дополнительных данных == false) { // попытка отправить слишком много данных, это критическая ошибка и отправка была заблокирована FStatus = FStatus | B10000000; // установим восьмой бит ошибки } if (this->Id_Unit > 0) { // попытка отправить простую команду со слейва - это ошибка, но не критическая, // команда все равно будет отправлена, но она будет игнорироватся получателями // отправка таких команды оставлена для резерва на будущее расширения возможностей FStatus = FStatus | B01000000; // установим седьмой бит ошибки } } //------------------------------------------------------------------ // начало команды из нескольких пакетов void BusRS485::Command(uint8_t Id, // Id получателя пакета uint8_t Func, // номер функции uint8_t CountPack, // всего кадров в команде uint8_t SizeDate, // размер дополнительных данных uint8_t *Data, // указатель на буфер дополнительных данных int TimeOut, // время которое будем ожидать ответный пакет unsigned long StartTime, // время начала ожидания ответного пакета void (*_userFunc)()){ // процедура вызываемая при получении ответа Reply.OnReply = false; if (this->Id_Unit > 0) { // попытка отправить сложную команду со слейва - это критическая ошибка и отправка была заблокирована FStatus = FStatus | B10000000; // установим восьмой бит ошибки } else if (this->send( Id, // ID получателя Func, // номер функции 1, // номер кадра в команде CountPack, // всего кадров в команде false, // флаг ошибки false, // флаг ожидания false, // флаг повтора false, // флаг подтверждения SizeDate, // размер дополнительных данных &(Data[0])) // указатель на буфер дополнительных данных == false) { // попытка отправить слишком много данных - это критическая ошибка и отправка была заблокирована FStatus = FStatus | B10000000; // установим восьмой бит ошибки } else if (CountPack > 1) { // отправка прошла, устанавливаем параметры ожидания ответа Reply.OnReply = true; Reply.TimeOut = TimeOut; Reply.StartTime = StartTime; Reply.Id_Destination = this->Id_Unit; Reply.Id_Source = Id; Reply.Control_1 = (CountPack << 4) | Func; Reply.Control_2 = 2; Reply.Reply_OnStep = _userFunc;; } } //------------------------------------------------------------------ // не первый пакет из сложной команды (вызывается из обработчиков ответа) void BusRS485::Command(uint8_t Id, // Id получателя пакета uint8_t Func, // номер функции uint8_t Pack, // номер кадра в команде uint8_t CountPack, // всего кадров в команде boolean F1, // флаг ошибки boolean F2, // флаг ожидания boolean F3, // флаг повтора boolean F4, // флаг подтверждения uint8_t SizeDate, // размер дополнительных данных uint8_t *Data, // указатель на буфер дополнительных данных int TimeOut, // время которое будем ожидать ответный пакет unsigned long StartTime, // время начала ожидания ответного пакета void (*_userFunc)()){ // процедура вызываемая при получении ответа Reply.OnReply = false; if (Pack == 1) { // попытка отправить первый кадр команды - это критическая ошибка и отправка была заблокирована FStatus = FStatus | B10000000; // установим восьмой бит ошибки } else if (this->send( Id, // ID получателя Func, // номер функции Pack, // номер кадра в команде CountPack, // всего кадров в команде F1, // флаг ошибки F2, // флаг ожидания F3, // флаг повтора F4, // флаг подтверждения SizeDate, // размер дополнительных данных &(Data[0])) // указатель на буфер дополнительных данных == false) { // попытка отправить слишком много данных - это критическая ошибка и отправка была заблокирована FStatus = FStatus | B10000000; // установим восьмой бит ошибки } else if (CountPack > Pack) { // отправка прошла, устанавливаем параметры ожидания ответа Reply.OnReply = true; Reply.TimeOut = TimeOut; Reply.StartTime = StartTime; Reply.Id_Destination = this->Id_Unit; Reply.Id_Source = Id; Reply.Control_1 = (CountPack << 4) | Func; Reply.Control_2 = (Pack+1); Reply.Reply_OnStep = _userFunc;; } } //------------------------------------------------------------------ uint8_t BusRS485::Id (void) { return Id_Unit; } uint8_t BusRS485::Status (void) { return FStatus; } void BusRS485::SetEcho(uint8_t a) { this->Echo = a; } uint8_t BusRS485::Id_Destination () { return InPack.Id_Destination; } uint8_t BusRS485::Id_Source () { return InPack.Id_Source; } uint8_t BusRS485::SizeDate () { return InPack.SizeDate; } uint8_t * BusRS485::Date () { return &(InPack.Data[0]); } uint8_t BusRS485::NumFunc () { return (InPack.Control_1 & B00001111); } uint8_t BusRS485::CountCadr () { return (InPack.Control_1 >> 4); } uint8_t BusRS485::NumCadr () { return (InPack.Control_2 & B00001111); } boolean BusRS485::FError () { return ((InPack.Control_2 >> 4) & B00000001); } boolean BusRS485::FWait () { return ((InPack.Control_2 >> 5) & B00000001); } boolean BusRS485::FRetry () { return ((InPack.Control_2 >> 6) & B00000001); } boolean BusRS485::FValidate () { return (InPack.Control_2 >> 7); } boolean BusRS485::OnReply (void) { return Reply.OnReply; } boolean BusRS485::OnReply (boolean value) { Reply.OnReply = value; return Reply.OnReply; } uint8_t BusRS485::ReplyId (void) { return Reply.Id_Source; } uint8_t BusRS485::ReplyNumFunc () { return (Reply.Control_1 & B00001111); } int BusRS485::TimeOut (void) { return Reply.TimeOut; } unsigned long BusRS485::StartTime (void) { return Reply.StartTime; } void BusRS485::onRead() { port->flush(); // ждем пока завершится отправка PORTD = PORTD & (B11111111 ^ MaskRW); // оба пина выключены // if ((CPinRx > 1)&&(CPinTx > 1)) { // // FStatus = FStatus ^ B11110111; // port->flush(); // digitalWrite(CPinTx, LOW); // отправка выключена // digitalWrite(CPinRx, LOW); // получение включено (RE инверсный) // } } void BusRS485::onWrite() { port->flush(); // ждем пока завершится предыдущая отправка, возможно было эхо PORTD = PORTD | MaskRW; // оба пина включены // if ((CPinRx > 1)&&(CPinTx > 1)) { // // FStatus = FStatus ^ B11110111; // port->flush(); // digitalWrite(CPinRx, HIGH); // получение выключено (RE инверсный) // digitalWrite(CPinTx, HIGH); // отправка включена // } } void BusRS485::off() { // if ((CPinRx > 1)&&(CPinTx > 1)) { // // FStatus = FStatus ^ B11110111; // digitalWrite(CPinRx, HIGH); // получение выключено (RE инверсный) // digitalWrite(CPinTx, LOW); // отправка выключена // port->flush(); // } } uint8_t BusRS485::Read(){ uint8_t a; a = port->read(); if (this->Echo == 1) { port->write(a); } return a; } void BusRS485::poll() { uint8_t mCRC = 0; uint8_t i1 = 0; int ii; while (true) { // FInPack=0 // 0-ждем заголовок // 1-ждем дополнительные данные // 2-пакет загружен валиден // 3-пакет загружен, но не валиден if (FInPack > 3) {FInPack = 0;} // не понятный флаг, значит будем ждать заголовок else if (FInPack == 2) {break;} // пакет загружен и не обработан else if (FInPack == 3) {break;} // пакет загружен и не валиден ii = port->available(); if (ii == 0) { break; } // данных нет // вне зависимости от ожиданий пробуем выцепить заголовок if (port->peek() == MARK && ii >= 8){ mCRC = CRC(MARK, 0); mCRC = CRC(port->peek(1), mCRC); mCRC = CRC(port->peek(2), mCRC); mCRC = CRC(port->peek(3), mCRC); mCRC = CRC(port->peek(4), mCRC); mCRC = CRC(port->peek(5), mCRC); mCRC = CRC(port->peek(6), mCRC); if (mCRC == port->peek(7)) { // да это заголовок пакета, InPack.Mark = Read(); InPack.Id_Destination = Read(); InPack.Id_Source = Read(); InPack.Control_1 = Read(); InPack.Control_2 = Read(); InPack.SizeDate = Read(); InPack.CRC_Data = Read(); InPack.CRC_Head = Read(); if (InPack.SizeDate == 0) { // пакет не содержит дополнительных данных, только номер и команду FInPack = 2; } else if (InPack.SizeDate <= MAX_BUFFER){ // заголовок загружен, нужно пытатся загрузить данные FInPack = 1; continue; } else { // мы не можем обработать такой большой пакет FInPack = 3; } break; } else if (FInPack == 0) { // ждали заголовок а CRC не совпало Read(); continue; } } if (FInPack == 0) { // ждем заголовка, а маркера нет if (port->peek() != MARK) { Read(); continue; } } //-------------------------------------------------------------------------- if (FInPack == 1 && ii >= InPack.SizeDate) { // Загрузим доп данные вне зависимости от CRC for (int i1=0; i1 < InPack.SizeDate; i1++){ InPack.Data[i1] = Read(); } //теперь проверим валидность mCRC = 0; for (int i1=0; i1 < InPack.SizeDate; i1++){ mCRC = CRC(InPack.Data[i1], mCRC); } if (mCRC == InPack.CRC_Data) { FInPack = 2; } // это правильные дополнительные данные else { FInPack = 3; } // контроль доп данных не сошелся весь пакет выкенем } break; } // ---- while ------ if (FInPack == 2) { if (Id_Unit == InPack.Id_Source) { // мы видим входящий пакет от устройства с нашим ID, это ошибка, в сети 2 устройства с одним ID FStatus = FStatus | B00000101; Id_Unit = 241; Reply.OnReply = false; } else if (Id_Unit == 0) { // это мастер, на нем обрабатываем только пакеты которые мы ожидаем // неожиданно пришедшие пакеты пришедшие на мастер пропускаем if ( Reply.OnReply && Reply.Id_Destination == InPack.Id_Destination && Reply.Id_Source == InPack.Id_Source && Reply.Control_1 == InPack.Control_1 && Reply.Control_2 == (InPack.Control_2 & B00001111) ) { // запускаем подключеную обработку ответа Reply.OnReply = false; Reply.Reply_OnStep(); } } else { // это слейв, на нем обрабатываем // 1. ожидаемые ответы // 2. широковещательные пакеты // 3. новые пакеты от мастера if ( Reply.OnReply && Reply.Id_Destination == InPack.Id_Destination && Reply.Id_Source == InPack.Id_Source && Reply.Control_1 == InPack.Control_1 && Reply.Control_2 == (InPack.Control_2 & B00001111) ) { // это ожидаемый пакет // запускаем подключеную обработку Reply.OnReply = false; Reply.Reply_OnStep(); } // кроме ожидаемых ответов на слейве отрабатываем вариант прихода новой команы else if (InPack.Id_Destination == 255) { // это широковещательный пакет Reply.OnReply = false; OnSlave(); } else if (InPack.Id_Destination == Id_Unit && InPack.Id_Source == 0) { // это новый пакет от мастера Reply.OnReply = false; OnSlave(); } } FInPack = 0; } if (FInPack > 1) { FInPack = 0; } } // ------------------PRIVATE FUNCTIONS------------------------------ boolean BusRS485::send(uint8_t Id, // ID получателя uint8_t Func, // номер функции uint8_t Pack, // номер кадра в команде uint8_t CountPack, // всего кадров в команде boolean F1, // флаг ошибки boolean F2, // флаг ожидания boolean F3, // флаг повтора boolean F4, // флаг подтверждения uint8_t SizeDate, // размер дополнительных данных uint8_t *Data) { // указатель на буфер дополнительных данных if (MAX_BUFFER >= SizeDate) { // посылаем только если доп данные влезут в буфер uint8_t CRC_Data = 0; // CRC дополнительных данных пакета uint8_t CRC_Head = 0; // CRC заголовка, служит для определения корректности заголовка и выделения кадра из потока данных uint8_t a = (CountPack << 4) | Func; uint8_t b = (F1 << 7) | (F2 << 6) | (F3 << 5) | (F4 << 4) | Pack; for (int i=0; i < SizeDate; i++){ CRC_Data = CRC(Data[i], CRC_Data); } CRC_Head = CRC(CRC_Head, MARK); CRC_Head = CRC(CRC_Head, Id); CRC_Head = CRC(CRC_Head, Id_Unit); CRC_Head = CRC(CRC_Head, a); CRC_Head = CRC(CRC_Head, b); CRC_Head = CRC(CRC_Head, SizeDate); CRC_Head = CRC(CRC_Head, CRC_Data); onWrite(); // перед отправкой выдержем паузу для того, что-бы остальные успелись переключится в режим приема delayMicroseconds(PauseWrite); port->write(MARK); port->write(Id); port->write(Id_Unit); port->write(a); port->write(b); port->write(SizeDate); port->write(CRC_Data); port->write(CRC_Head); for (int i=0; i < SizeDate; i++){ port->write(Data[i]); } onRead(); return true; } else { return false; } } // --------------------------------------------- // функция делает XOR и после циклический сдвиг uint8_t BusRS485::CRC(uint8_t a, uint8_t b) { uint8_t c; c = a ^ b; return (c << 1) | (c >> 7); }и нужен небольшой патч IDE
"hardware\arduino\avr\cores\arduino\HardwareSerial.h"
"hardware\arduino\avr\cores\arduino\HardwareSerial.cpp"
/* HardwareSerial.cpp - Hardware serial library for Wiring Copyright (c) 2006 Nicholas Zambetti. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Modified 23 November 2006 by David A. Mellis Modified 28 September 2010 by Mark Sproul Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <inttypes.h> #include "Arduino.h" #include "HardwareSerial.h" #include "HardwareSerial_private.h" // this next line disables the entire HardwareSerial.cpp, // this is so I can support Attiny series and any other chip without a uart #if defined(HAVE_HWSERIAL0) || defined(HAVE_HWSERIAL1) || defined(HAVE_HWSERIAL2) || defined(HAVE_HWSERIAL3) // SerialEvent functions are weak, so when the user doesn't define them, // the linker just sets their address to 0 (which is checked below). // The Serialx_available is just a wrapper around Serialx.available(), // but we can refer to it weakly so we don't pull in the entire // HardwareSerial instance if the user doesn't also refer to it. #if defined(HAVE_HWSERIAL0) void serialEvent() __attribute__((weak)); bool Serial0_available() __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL1) void serialEvent1() __attribute__((weak)); bool Serial1_available() __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL2) void serialEvent2() __attribute__((weak)); bool Serial2_available() __attribute__((weak)); #endif #if defined(HAVE_HWSERIAL3) void serialEvent3() __attribute__((weak)); bool Serial3_available() __attribute__((weak)); #endif void serialEventRun(void) { #if defined(HAVE_HWSERIAL0) if (Serial0_available && serialEvent && Serial0_available()) serialEvent(); #endif #if defined(HAVE_HWSERIAL1) if (Serial1_available && serialEvent1 && Serial1_available()) serialEvent1(); #endif #if defined(HAVE_HWSERIAL2) if (Serial2_available && serialEvent2 && Serial2_available()) serialEvent2(); #endif #if defined(HAVE_HWSERIAL3) if (Serial3_available && serialEvent3 && Serial3_available()) serialEvent3(); #endif } // Actual interrupt handlers ////////////////////////////////////////////////////////////// void HardwareSerial::_tx_udr_empty_irq(void) { // If interrupts are enabled, there must be more data in the output // buffer. Send the next byte unsigned char c = _tx_buffer[_tx_buffer_tail]; _tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_TX_BUFFER_SIZE; *_udr = c; // clear the TXC bit -- "can be cleared by writing a one to its bit // location". This makes sure flush() won't return until the bytes // actually got written sbi(*_ucsra, TXC0); if (_tx_buffer_head == _tx_buffer_tail) { // Buffer empty, so disable interrupts cbi(*_ucsrb, UDRIE0); } } // Public Methods ////////////////////////////////////////////////////////////// void HardwareSerial::begin(unsigned long baud, byte config) { // Try u2x mode first uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2; *_ucsra = 1 << U2X0; // hardcoded exception for 57600 for compatibility with the bootloader // shipped with the Duemilanove and previous boards and the firmware // on the 8U2 on the Uno and Mega 2560. Also, The baud_setting cannot // be > 4095, so switch back to non-u2x mode if the baud rate is too // low. if (((F_CPU == 16000000UL) && (baud == 57600)) || (baud_setting >4095)) { *_ucsra = 0; baud_setting = (F_CPU / 8 / baud - 1) / 2; } // assign the baud_setting, a.k.a. ubrr (USART Baud Rate Register) *_ubrrh = baud_setting >> 8; *_ubrrl = baud_setting; _written = false; //set the data bits, parity, and stop bits #if defined(__AVR_ATmega8__) config |= 0x80; // select UCSRC register (shared with UBRRH) #endif *_ucsrc = config; sbi(*_ucsrb, RXEN0); sbi(*_ucsrb, TXEN0); sbi(*_ucsrb, RXCIE0); cbi(*_ucsrb, UDRIE0); } void HardwareSerial::end() { // wait for transmission of outgoing data flush(); cbi(*_ucsrb, RXEN0); cbi(*_ucsrb, TXEN0); cbi(*_ucsrb, RXCIE0); cbi(*_ucsrb, UDRIE0); // clear any received data _rx_buffer_head = _rx_buffer_tail; } int HardwareSerial::available(void) { return ((unsigned int)(SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail)) % SERIAL_RX_BUFFER_SIZE; } int HardwareSerial::peek(void) { if (_rx_buffer_head == _rx_buffer_tail) { return -1; } else { return _rx_buffer[_rx_buffer_tail]; } } // ------------------------------------------------------------ // расширение синтаксиса команды Serial.peek() // int HardwareSerial::peek(uint8_t c) // надстройка для BusRS485 { int i = this->available(); // проверяем, что индекс в рабочем диапазоне if (c < i) { return _rx_buffer[(SERIAL_RX_BUFFER_SIZE + c + _rx_buffer_tail) % SERIAL_RX_BUFFER_SIZE]; } else { return -1; } } // // автор vde69@mail.ru (с) // ------------------------------------------------------------ int HardwareSerial::read(void) { // if the head isn't ahead of the tail, we don't have any characters if (_rx_buffer_head == _rx_buffer_tail) { return -1; } else { unsigned char c = _rx_buffer[_rx_buffer_tail]; _rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE; return c; } } int HardwareSerial::availableForWrite(void) { #if (SERIAL_TX_BUFFER_SIZE>256) uint8_t oldSREG = SREG; cli(); #endif tx_buffer_index_t head = _tx_buffer_head; tx_buffer_index_t tail = _tx_buffer_tail; #if (SERIAL_TX_BUFFER_SIZE>256) SREG = oldSREG; #endif if (head >= tail) return SERIAL_TX_BUFFER_SIZE - 1 - head + tail; return tail - head - 1; } void HardwareSerial::flush() { // If we have never written a byte, no need to flush. This special // case is needed since there is no way to force the TXC (transmit // complete) bit to 1 during initialization if (!_written) return; while (bit_is_set(*_ucsrb, UDRIE0) || bit_is_clear(*_ucsra, TXC0)) { if (bit_is_clear(SREG, SREG_I) && bit_is_set(*_ucsrb, UDRIE0)) // Interrupts are globally disabled, but the DR empty // interrupt should be enabled, so poll the DR empty flag to // prevent deadlock if (bit_is_set(*_ucsra, UDRE0)) _tx_udr_empty_irq(); } // If we get here, nothing is queued anymore (DRIE is disabled) and // the hardware finished tranmission (TXC is set). } size_t HardwareSerial::write(uint8_t c) { _written = true; // If the buffer and the data register is empty, just write the byte // to the data register and be done. This shortcut helps // significantly improve the effective datarate at high (> // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) { *_udr = c; sbi(*_ucsra, TXC0); return 1; } tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE; // If the output buffer is full, there's nothing for it other than to // wait for the interrupt handler to empty it a bit while (i == _tx_buffer_tail) { if (bit_is_clear(SREG, SREG_I)) { // Interrupts are disabled, so we'll have to poll the data // register empty flag ourselves. If it is set, pretend an // interrupt has happened and call the handler to free up // space for us. if(bit_is_set(*_ucsra, UDRE0)) _tx_udr_empty_irq(); } else { // nop, the interrupt handler will free up space for us } } _tx_buffer[_tx_buffer_head] = c; _tx_buffer_head = i; sbi(*_ucsrb, UDRIE0); return 1; } #endif // whole fileи
кстати вот тут думаю, чего лучше патч IDE или сделать свой класс наследник?
класс плох тем, что там нужно использоать прямой доступ к полям и при изменении исходником может перестать работать, как оно было при переходе с 152 на последнюю.
но и другой вариант то-же плох
во первых скорость должна быть одинаковая, во вторых возьми готовую библиотеку какую...
Проблема в том, что РАБОТАЕТ на разных скоростях и НЕ РАБОТАЕТ на одинаковых
Проблема в том, что РАБОТАЕТ на разных скоростях и НЕ РАБОТАЕТ на одинаковых
Ну тогда либо кварцы разные и одна не на 16 мегагерц, а на 8, либо - что-то не так в консерватории. Чудес не бывает, надо смотреть весь код, как минимум. Это если принимать описанное вами поведение на веру.
Это первое, что пришло в голову. Посмотрел - по маркировке обе 16 Мгц. Я это написал в первом посте. Код большой, куски, которые отправляют и принимают я написал. Больше ничего не отправляется, кроме таких и подобных кусков.
Нано вот такая - https://arduinobazar.ru/home/152-dccduino-nano-arduino-nano-r3-sovmestimaya-kupit-arduino-moskva-peterburg-dyoshevo.html
Про мини - вот такая - https://arduinobazar.ru/platy-arduino/2-arduino-pro-mini-16mhz-5v-atmega328-kupit-arduino-moskva-peterburg-dyoshevo.html
Есть пример скетча, как мегагерцы просто померять?
терминал на компе пробовал подключать и туда и туда на одной скорости?
терминал на компе пробовал подключать и туда и туда на одной скорости?
да. Но само-собой подключал только к передающей - скорость совпадает.
Неужели, подсунули 8 Мгц вместо 16-ти? Просто у продавца есть 8 Мгц версия, но она 3,3В, а эта 5В
терминал на компе пробовал подключать и туда и туда на одной скорости?
да. Но само-собой подключал только к передающей - скорость совпадает.
Неужели, подсунули 8 Мгц вместо 16-ти? Просто у продавца есть 8 Мгц версия, но она 3,3В, а эта 5В
подключи одновременно 2 терминала на одной скорости и к мастеру и слейву, будет видно чего отправляешь и чего доходит
это как? Типа в одну ЮСБ от одной и в другую - от другой?
Неужели, подсунули 8 Мгц вместо 16-ти? Просто у продавца есть 8 Мгц версия, но она 3,3В, а эта 5В
На кварце что написано?
Залей в обе ардуины какой-нибудь одинаковый простенький скетч на передачу данных по UART. Сначала подключи одну ардуину к компу, проверь скорость. Потом подключи другую и тоже поверь скорость.
это как? Типа в одну ЮСБ от одной и в другую - от другой?
берешь ДВА USB кабеля и каждую подключаешь к компу, у тебя будет 2 разных COM порта, терминалы подключаешь к этим ком портам и слушаешь чего они между собой шпарят
все заработало, помогавшим - спасибо!
теперь другая проблема. передаю 5 байт на скорости 38400, принимаю 5 байт на той же скорости. иногда (не часто), что-то теряется.
вопрос 1 - почему?, вопрос 2 - как добиться "железной 100%" передачи?
Код передачи
void sendToLED (byte s1, byte s2, byte s3, char s4, char s5) { Serial.write (s1); Serial.write (s2); Serial.write (s3); Serial.write (s4); Serial.write (s5); delay (150); // с 100 работало более-менее, c 150 стало лучше, но все-равно не доходят байты иногда if (Serial.available() > 0) { byte bytesSentToLED = Serial.read (); lcd.print (bytesSentToLED); } else lcd.print ("-"); }Код приема
num1 = proSerial.available(); if (num1 > 0) { delay (100); switch (num1) { case 1: num = proSerial.read (); spd = 0; spdD = 0; symb1 = 0; symb2 = 0; // Serial.flush(); break; case 2: num = proSerial.read (); spd = proSerial.read (); spdD = 0; symb1 = 0; symb2 = 0; // Serial.flush(); break; case 3: num = proSerial.read (); spd = proSerial.read (); spdD = proSerial.read (); symb1 = 0; symb2 = 0; // Serial.flush(); break; case 4: num = proSerial.read (); spd = proSerial.read (); spdD = proSerial.read (); symb1 = proSerial.read (); symb1 = 0; // Serial.flush(); break; case 5: num = proSerial.read (); spd = proSerial.read (); spdD = proSerial.read (); symb1 = proSerial.read (); symb2 = proSerial.read (); // Serial.flush(); break; default: // Serial.flush(); break; } proSerial.write (num1); // отправка обратно, сколько байт приняли }