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() в цикле, то теряется некоторая часть посылок.
#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(); }А если переключение в режим ожидания данных следующего программного порта делать только после приёма посылки из первого, то всё работает, но только последовательно.
#include <SoftwareSerial.h> SoftwareSerial portOne(10, 11); SoftwareSerial portTwo(8, 9); void setup() { Serial.begin(9600); portOne.begin(9600); portTwo.begin(9600); } void loop() { Serial.println("Data from port one:"); while (portOne.available() > 0) { char inByte = portOne.read(); Serial.write(inByte); portTwo.listen(); } Serial.println(); Serial.println("Data from port two:"); while (portTwo.available() > 0) { char inByte = portTwo.read(); Serial.write(inByte); portOne.listen(); } Serial.println(); }Кто что посоветует, может я его не правильно готовлю?