Моя реализации Modbus Slave RTU/ASCII
- Войдите на сайт для отправки комментариев
Сб, 15/04/2017 - 15:09
Когда мы имеем дело с микроконтроллерами нужно экономно подходить к использованию ресурсов, как программных так и данных. По этому я постарался сделать максимально простой обработчик 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. А далее идут три четырех байтовые переменные разных типов.
Более сложный пример с видео обзором можно посмотреть тут:
Вот мне интересно, зачем мастеру писать в 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(); }Я решил делать свою реализацию Modbus Slave, после того, как используя стороннюю библиотеку я уперся в ограничение на количество памяти для данных. В той библиотеке приходилос дублировать переменные и данные из EEPROM в массиве регтстров.
В приведенном примере я показал как можно предоставить доступ к данным EEPROM по Modbus вообще не используя промежуточные переменные.