Моя реализации Modbus Slave RTU/ASCII

kandiral
Offline
Зарегистрирован: 15.04.2017

Когда мы имеем дело с микроконтроллерами нужно экономно подходить к использованию ресурсов, как программных так и данных. По этому я постарался сделать максимально простой обработчик Modbus RTU/ASCII Slave для микроконтроллеров Arduino с минимальным использованием ресурсов.

Обрабатываются функции Modbus:

  • Read Holding Registers 03h
  • Read Input Registers 04h
  • Write Holding Register 06h
  • Write Holding Registers 10h

Простой пример:

#include <EEPROM.h>
#include "KRRegisters.h"
#include "KRModbusRTUSlave.h"

class Data : public KRRegisters{
  public:
    uint32_t dw;
    long lng;
    float flt;
    
    virtual uint16_t read(uint16_t index){
      if(index<10){
        int n=index*2;
        uint16_t res=EEPROM.read(n++);
        res<<=8;
        return res|EEPROM.read(n);
      }else{
        switch(index){
          case 10:return dw;
          case 11:return dw >> 16;
          case 12:return *(reinterpret_cast<uint32_t*>(&lng));
          case 13:return *(reinterpret_cast<uint32_t*>(&lng)) >> 16;
          case 14:return *(reinterpret_cast<uint32_t*>(&flt));
          case 15:return *(reinterpret_cast<uint32_t*>(&flt)) >> 16;
        }
      }
    }
    virtual void write(uint16_t index, uint16_t value){
      uint32_t dw0;
      if(index<10){
        int n=index*2;
        EEPROM.write(n++,value >> 8);
        EEPROM.write(n,value);
      }else{
        switch(index){
          case 10:dw=(dw & 0xffff0000) | value; break;
          case 11:dw=(dw & 0x0000ffff) | ((uint32_t)value << 16); break;
          case 12:
            dw0=(*(reinterpret_cast<uint32_t*>(&lng)) & 0xffff0000) | value;
            lng=*(reinterpret_cast<long*>(&dw0)) ;
            break;
          case 13:
            dw0=(*(reinterpret_cast<uint32_t*>(&lng)) & 0x0000ffff) | ((uint32_t)value << 16);
            lng=*(reinterpret_cast<long*>(&dw0)) ;
            break;
          case 14:
            dw0=(*(reinterpret_cast<uint32_t*>(&flt)) & 0xffff0000) | value;
            flt=*(reinterpret_cast<float*>(&dw0)) ;
            break;
          case 15:
            dw0=(*(reinterpret_cast<uint32_t*>(&flt)) & 0x0000ffff) | ((uint32_t)value << 16);
            flt=*(reinterpret_cast<float*>(&dw0)) ;
            break;
        }
      }
    }
};

Data data;
KRModbusRTUSlave mb(Serial,data,1);

void setup() {
  Serial.begin(9600);
  data.dw=1234;
  data.lng=-4567;
  data.flt=12.345;
}

void loop() {
  mb._DO();
  data.dw+=1;
  data.lng-=1;
  data.flt+=0.012;  
}

В примере первые 10 регистров это чтение/запись напрямую из EEPROM. А далее идут три четырех байтовые переменные разных типов.

Более сложный пример с видео обзором можно посмотреть тут:

http://kandiral.ru/arduino/modbus_slave_na_arduino.html

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Вот мне интересно, зачем мастеру писать в EEprom slave. может все же лучше банально передать пакет. Пусть Slave сам разберется что и куда сувать.

//Cl_MasterInSlave
#include "Cl_Wire.h"
#include "Cl_MasterInSlave.h"
Cl_Wire Wire(pin1, pin2);// создать шину
void Do_Master(); //
void Do_Master1();//
/* Wire шина к которой мастер
    0x01,0x02 адрес под которым представляется данной устройство
    Do_Master функция обработки данного пакета полученого от мастера
*/
Cl_MasterInSlave Master(&Wire , 0x01, &Do_Master);//
Cl_MasterInSlave Master1(&Wire, 0x02, &Do_Master1);
void Do_Master() {
  Master.buffer;
};
void Do_Master1() {
  Master1.buffer;
};
void setup() {
  Wire.setup();
  Master.setup();
  Master1.setup();

}

void loop() {
  Wire.loop();
  Master.loop();
  Master1.loop();

}

 

kandiral
Offline
Зарегистрирован: 15.04.2017

Я решил делать свою реализацию Modbus Slave, после того, как используя стороннюю библиотеку я уперся в ограничение на количество памяти для данных. В той библиотеке приходилос дублировать переменные и данные из EEPROM в массиве регтстров. 

В приведенном примере я показал как можно предоставить доступ к данным EEPROM по Modbus вообще не используя промежуточные переменные.