RS485 Modbus RTU <-> DIN66019II
- Войдите на сайт для отправки комментариев
Здравствуйте. В данный момент занимаюсь созданием преобразователя протоколов Modbus RTU и DIN66019II на arduino pro mini. Возникло несколько нюансов требующих совета.
В идеале хотелось бы привязать два преобразователя интерфейсов RS485 к двум SoftSerial'ам, а с аппаратного Serial'а просматривать все данные приходящие и уходящие от них. Тут возникла первая проблема, так как в один момент времени на приём может работать только один SoftSerial, перед проверкой наличия данных в буфере нужно переключать их на приём функцией SoftSerial.listen(). Вообщем сделал всё почти как в примере к библиотеке:
#include <SoftwareSerial.h> SoftwareSerial portOne(10, 11); SoftwareSerial portTwo(8, 9); void setup() { Serial.begin(9600); portOne.begin(9600); portTwo.begin(9600); } void loop() { portOne.listen(); Serial.println("Data from port one:"); while (portOne.available() > 0) { char inByte = portOne.read(); Serial.write(inByte); } Serial.println(); portTwo.listen(); Serial.println("Data from port two:"); while (portTwo.available() > 0) { char inByte = portTwo.read(); Serial.write(inByte); } Serial.println(); }
Но при тестировании выяснилось, что при посылке данных в один SoftSerial с периодом в пол секунды из 10 доходило 3-4. Потом добавил задержку перед переводом второго порта в режим ожидания данных, из 10 посылок стало доходить 5-6. Вопрос к знатокам, что не так?
Пока что отказался от использования двух SoftSerial'ов, но из-за этого потерял средство диагностики, т.к. прицепил оба преобразователя RS485 к аппаратному и одному программному порту.
Ещё возник вопрос по моему куску кода, который находится ниже. Из-за наличия в командах, используемых в протоколе, символа конца строки 0x00 в середине этой команды (пример: 0x05, 0x10, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0x55, 0x50) пришлось использовать посимвольное сравнение и их посимвольный вывод, что не есть хорошо. Может всё же есть вариант работы со строками?
#include <avr/pgmspace.h> const PROGMEM char forward[] = {0x05, 0x10, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0x55, 0x50}; const PROGMEM char backward[] = {0x05, 0x10, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x15, 0x51}; const PROGMEM char freq10[] = {0x05, 0x10, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0xE8, 0x95, 0xFF}; const PROGMEM char freq15[] = {0x05, 0x10, 0x00, 0x01, 0x00, 0x01, 0x02, 0x05, 0xDC, 0x97, 0x88}; const PROGMEM char stay[] = {0x05, 0x10, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x94, 0x90}; #include <SoftwareSerial.h> SoftwareSerial Serial485(10, 11); int mEn = 9; int sEn = 12; int led = 13; int k; char b; char output; String input; void setup() { pinMode(mEn, OUTPUT); pinMode(sEn, OUTPUT); pinMode(led, OUTPUT); digitalWrite(mEn, LOW); digitalWrite(sEn, LOW); digitalWrite(led, LOW); Serial.begin(9600); Serial485.begin(9600); } void loop() { while (Serial.available()) { b = Serial.read(); input += b; switch (input.charAt(0)) { case 0x05: if (input.length() == 2) { switch (input.charAt(1)) { case 0x00: sendSlave(stay, 11); break; case 0x01: sendSlave(forward, 11); break; case 0x02: sendSlave(backward, 11); break; default: input = ""; } } break; default: input = ""; } } while (Serial485.available()) { b = Serial485.read(); input += b; switch (input.charAt(0)) { case 0x05: if (input.length() == 2) { switch (input.charAt(1)) { case 0x00: sendMaster(freq10, 11); break; case 0x01: sendMaster(freq15, 11); break; default: input = ""; } } break; default: input = ""; } } } void sendSlave (const char* h, int n){ input = ""; digitalWrite(sEn, HIGH); digitalWrite(led, HIGH); for (k = 0; k < n; k++) { output = pgm_read_word_near(h + k); Serial485.print(output); } //delayMicroseconds (660); while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer UCSR0A |= 1 << TXC0; // mark transmission not complete while (!(UCSR0A & (1 << TXC0))); // Wait for the transmission to complete digitalWrite(sEn, LOW); digitalWrite(led, LOW); } void sendMaster (const char* h, int n){ input = ""; digitalWrite(mEn, HIGH); digitalWrite(led, HIGH); for (k = 0; k < n; k++) { output = pgm_read_word_near(h + k); Serial.print(output); } //delayMicroseconds (660); while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer UCSR0A |= 1 << TXC0; // mark transmission not complete while (!(UCSR0A & (1 << TXC0))); // Wait for the transmission to complete digitalWrite(mEn, LOW); digitalWrite(led, LOW); }
И на последок, при тестировании в условиях приближенных к боевым, а именно при подключении преобразователя RS485 (который привязан к SoftSerial'у) к преобразователю частоты, ардуина удачно посылает одну команду, а затем зависает. При отключении "на горячую" от преобразователя частоты она начинала снова работать (видно по миганию led). При подключении же преобразователя RS485 (который подключен к аппаратному Serial'у) всё работает. Это напрочь убивает использование SoftSerial'а для решения данной задачи...
Модули RS485 вот такие:
Up
Вопрос на засыпку, кто-нибудь использовал несколько SoftSerial'ов?
Не встречался такой косяк, что если переводить программные порты в режим ожидания данных до проверки наличия данных в буфере функцией SoftSerial.listen() в цикле, то теряется некоторая часть посылок.
А если переключение в режим ожидания данных следующего программного порта делать только после приёма посылки из первого, то всё работает, но только последовательно.
Кто что посоветует, может я его не правильно готовлю?