RS485 Modbus RTU <-> DIN66019II

Grenader
Offline
Зарегистрирован: 07.03.2015

Здравствуйте. В данный момент занимаюсь созданием преобразователя протоколов 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 вот такие: 

Grenader
Offline
Зарегистрирован: 07.03.2015

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();
}

Кто что посоветует, может я его не правильно готовлю?