Современные технологии автоматизации» («СТА») —  журнал для квалифицированных специалистов по промышленной автоматизации Форум СТА — современные технологии автоматизации Домашняя страница
Домашняя страница форума CTA Домашняя страница форума CTA > II. АСУТП и SCADA > ПЛК и микроконтроллеры
  Активные темы Активные темы
  FAQ FAQ  Искать в форуме   Зарегистрироваться Зарегистрироваться  Вход в систему Вход в систему

Modbus на Си

 Ответить Ответить
Автор
Сообщение
Рома81 Смотреть выпадающим
Новичок
Новичок


Присоединился: 07 Август 2006
Online Status: Offline
Публикации: 17
Свойства публикации Свойства публикации   Ответить, цитируя автора - Рома81 Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Тема сообщения: Modbus на Си
    Опубликовано: 11 Сентябрь 2006 10:21

Кто нибудь писал сам протокол ModBus на Си, поделитесь опытом:

Slave контроллер отвечает на команду чтения регистров, но что он отвечает понять не могу, т.е. считываю отклик комады чтения функцией

unsigned char = ReadCom(ModPort) ;

как и должно быть получаю пакет байтов, но определить формат отклика не получается, (т.е. не могу поймать адрес слейва, и.т.д.) идет какой то произвольный набор байтов, что там где непоймешь

может кто подскажет как нормально считать пакет байтов с Сом порта, и главное определить что пришло с откликом.

Наверх
nick_user Смотреть выпадающим
Участник
Участник


Присоединился: 18 Ноябрь 2005
Категория: Russian Federation
Online Status: Offline
Публикации: 54
Свойства публикации Свойства публикации   Ответить, цитируя автора - nick_user Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 13 Сентябрь 2006 18:23
Писал (только на C++).
Киньте на пейджер свою почту, вышлю исходники.
Наверх
basal Смотреть выпадающим
Новичок
Новичок


Присоединился: 20 Сентябрь 2006
Online Status: Offline
Публикации: 11
Свойства публикации Свойства публикации   Ответить, цитируя автора - basal Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 20 Сентябрь 2006 12:59

Рекомендую скачать протокол modbus и программные средства от modicon http://sysavt.h11.ru/docs/inter/modbus.html

Имитатор мастера - modscan32 позволяет просматривать запросы от себя и ответы слэйва - это поможет в реализации протокола

 

Наверх
Рома81 Смотреть выпадающим
Новичок
Новичок


Присоединился: 07 Август 2006
Online Status: Offline
Публикации: 17
Свойства публикации Свойства публикации   Ответить, цитируя автора - Рома81 Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 20 Сентябрь 2006 15:07

Спасибо,за помощь но уже стало немного получаться)))))))

Получилось прочитать отклик по формату.

Но буду рад любой информации.

Мой мейл Rolando81@mail.ru

 

Наверх
casey Смотреть выпадающим
Новичок
Новичок
Аватар

Присоединился: 19 Июль 2005
Категория: Ukraine
Online Status: Offline
Публикации: 39
Свойства публикации Свойства публикации   Ответить, цитируя автора - casey Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 25 Сентябрь 2006 10:21

Я в свое время делал так. Конечно это не идеальный код, но работает.

Главный плуг в этом коде - Sleep между запросом и ответом. В идеале интервалы должны устанавливаться структурой comtimeouts, но я почему-то так и не подобрал их. Еще одно: функции IEEE754.... нужны для приведения порядка байт в нужный (порядок байт в контроллере и компьютере могут отличатся). Если увидишь ситуацию, при которой ответ есть, но числа бредовые - скорее всего у тебя другой порядок байт. 

//описание структур запроса и ответов 4,6 и 8 байт

union crc_union{
unsigned short integ;
BYTE bytes[2];
};

struct {
BYTE _1addr;
BYTE _2cmd;
BYTE _3data_start_addr_hi;
BYTE _4data_start_addr_lo;
BYTE _5data_length_hi;
BYTE _6data_length_lo;
BYTE _7crc1;
BYTE _8crc2;
} zapros;

struct otvet4{
BYTE _1from_addr;
BYTE _2cmd;
BYTE _3byte_count;
BYTE _4data[4];
BYTE _8crc1;
BYTE _9crc2;
} ;

struct otvet6{
BYTE _1from_addr;
BYTE _2cmd;
BYTE _3byte_count;
BYTE _4data[6];
BYTE _Acrc1;
BYTE _Bcrc2;
} ;

struct otvet8{
BYTE _1from_addr;
BYTE _2cmd;
BYTE _3byte_count;
BYTE _4data[8];
BYTE _Ccrc1;
BYTE _Dcrc2;
} ;

//псевдонимы функций
unsigned short __fastcall CRC16(unsigned char *Buffer, int Buffer_Length);
BOOL OpenCommPort(int Baudrate, int ByteSize, int Parity, int StopBits);
BOOL __fastcall floatNotNumber(BYTE src[4]);
float __fastcall IEEE754float(BYTE src[4]);
long __fastcall IEEE754long(BYTE src[4]);
double __fastcall IEEE754double(BYTE src[8]);
double ReadDouble(BYTE mbadr,BYTE addr_hi,BYTE addr_lo);
float ReadFloat(BYTE mbadr,BYTE addr_hi,BYTE addr_lo);
long ReadLong(BYTE mbadr,BYTE addr_hi,BYTE addr_lo);

//описание переменных
HANDLE Comport=NULL;
DCB dcb1;
COMMTIMEOUTS comtimeouts;
COMSTAT statistic;


DWORD nNumberOfBytesToWrite; // number of bytes to write
DWORD lpNumberOfBytesWritten; // pointer to number of bytes written

int IntEditTimeout=500;
BOOL BoolInvertedBytes=0;
AnsiString ComPort;
int IntBaudRate,IntParity,IntStopBits,IntByteSize;

BYTE MBadr;
//---------------------------------------------------------------------------

 

//---------------------------------------------------------------------------
// вспомагательная функция вычисления контрольной суммы CRC16, используемой в ModbusRTU
unsigned short __fastcall CRC16(unsigned char *Buffer, int Buffer_Length)
{
//
// Algorithm - CRC16 Standard, Polynom - 8005 (X16 + X15 + X2 + 1)
// Init const = ffff
// Check: CRC16("123456789",9) = BB3D
//

const unsigned short CRC16_tbl[ 256 ] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };

unsigned short CRC16 = 0xffff;

for (int i=0; i < Buffer_Length; i++)
{
CRC16 = ( CRC16 >> 8 ) ^ CRC16_tbl[ ( CRC16 & 0xFF ) ^ Buffer ];
}
return CRC16;
}
//-------------------------------------------------------------------------
//Функция открытия СОМ-порта
BOOL OpenCommPort(int baudrate, int bytesize, int parity, int stopbits)
{
Comport = CreateFile("COM1",
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL );

if (Comport!=INVALID_HANDLE_VALUE)
{

GetCommState(Comport, &dcb1);
dcb1.BaudRate=baudrate;
dcb1.fBinary=1;
dcb1.fParity=1;
dcb1.fDtrControl=0;
dcb1.Parity=parity;
dcb1.ByteSize=bytesize;
dcb1.StopBits = stopbits;
dcb1.fOutxCtsFlow=0;
dcb1.fOutxDsrFlow=0;
dcb1.fDsrSensitivity=0;
dcb1.fOutX=0;
dcb1.fInX=0;


SetCommState(Comport, &dcb1);

comtimeouts.ReadIntervalTimeout=0;
comtimeouts.ReadTotalTimeoutMultiplier=0;
comtimeouts.WriteTotalTimeoutMultiplier=0;
comtimeouts.ReadTotalTimeoutConstant=500;
comtimeouts.WriteTotalTimeoutConstant=500;
SetCommTimeouts(&Comport,&comtimeouts);

return true;
}
else return false;
}


//--------------------------------------------------------------------------
//прочитать значение типа float из modbus-slave устройства с адресом mbadr, адрес регистров - addr_hi, addr_lo

float ReadFloat(BYTE mbadr,BYTE addr_hi,BYTE addr_lo)
{
crc_union crc_float, crc_otv_flt;
otvet4 otvet4f;
DWORD p;

crc_float.integ=0;

zapros._1addr=mbadr;
zapros._2cmd=0x03;
zapros._3data_start_addr_hi=addr_hi;
zapros._4data_start_addr_lo=addr_lo;
zapros._5data_length_hi=0x00;
zapros._6data_length_lo=0x02;

crc_float.integ=CRC16(&zapros._1addr,6);

zapros._7crc1=crc_float.bytes[0];
zapros._8crc2=crc_float.bytes[1];

for(p=0;p<3;p++)
{
ZeroMemory(&otvet4f,sizeof(otvet4f));

PurgeComm(Comport,PURGE_TXCLEAR);
PurgeComm(Comport,PURGE_RXCLEAR);

if (WriteFile(
Comport,
&zapros,
sizeof(zapros),
&lpNumberOfBytesWritten,
NULL
))
{
Sleep(IntEditTimeout);

ClearCommError(Comport,NULL,&statistic);
if (statistic.cbInQue>=sizeof(otvet4f))
ReadFile( Comport, &otvet4f, sizeof(otvet4f),
&lpNumberOfBytesWritten, NULL );


crc_otv_flt.integ=CRC16(&otvet4f._1from_addr,7);
if ((crc_otv_flt.bytes[0]==otvet4f._8crc1)&& (crc_otv_flt.bytes[1]==otvet4f._9crc2))
{
p=4;
}
else
{
ZeroMemory(&otvet4f,sizeof(otvet4f));
}
}
}
if (floatNotNumber(otvet4f._4data))
return 0;
else
return IEEE754float(otvet4f._4data);
}

//-------------------------------------------------------------------------
//вспомогательная функция - преобразование 4 байт в число float
float __fastcall IEEE754float(BYTE src[4])
{
BYTE aaa;
float ffloat;
if (BoolInvertedBytes)
{
aaa=src[0];
src[0]=src[3];
src[3]=aaa;
aaa=src[1];
src[1]=src[2];
src[2]=aaa;
}
_wmemcpy(&ffloat,src,4);
return ffloat;
}
//--------------------------------------------------------------------------
//вспомогательная функция - проверка согласно IEEE754
BOOL __fastcall floatNotNumber(BYTE src[4])
{
return (((src[0]==0x7F) || (src[0]==0xFF)) && (src[1]>0x7F)
& ((src[1]>0x80) || (src[2]>0x00) || (src[3]>0x00)));
}

//---------------------------------------------------------------------------
//прочитать значение типа long из modbus-slave устройства с адресом mbadr, адрес регистров - addr_hi, addr_lo

long ReadLong(BYTE mbadr,BYTE addr_hi,BYTE addr_lo)
{
crc_union crc_long, crc_otv_lng;
otvet4 otvet4l;
DWORD p;

crc_long.integ=0;

zapros._1addr=mbadr;
zapros._2cmd=0x03;
zapros._3data_start_addr_hi=addr_hi;
zapros._4data_start_addr_lo=addr_lo;
zapros._5data_length_hi=0x00;
zapros._6data_length_lo=0x02;

crc_long.integ=CRC16(&zapros._1addr,6);

zapros._7crc1=crc_long.bytes[0];
zapros._8crc2=crc_long.bytes[1];

for(p=0;p<3;p++)
{

ZeroMemory(&otvet4l,sizeof(otvet4l));

PurgeComm(Comport,PURGE_TXCLEAR);
PurgeComm(Comport,PURGE_RXCLEAR);

if (WriteFile( Comport, &zapros, sizeof(zapros),
&lpNumberOfBytesWritten, NULL ))
{
Sleep(IntEditTimeout);

ClearCommError(Comport,NULL,&statistic);
if (statistic.cbInQue>=sizeof(otvet4l))
ReadFile( Comport,&otvet4l,sizeof(otvet4l),
&lpNumberOfBytesWritten, NULL );

crc_otv_lng.integ=CRC16(&otvet4l._1from_addr,7);
if ((crc_otv_lng.bytes[0]==otvet4l._8crc1) && (crc_otv_lng.bytes[1]==otvet4l._9crc2))
{
p=4;
}
}}
return IEEE754long(otvet4l._4data);
}


//-------------------------------------------------------------------------
//вспомогательная функция - преобразование 4 байт в число long (собственно к стандарту IEEE754 отношения не имеет, просто для не знаю чего)

long __fastcall IEEE754long(BYTE src[4])
{
BYTE aaa;
long ilong;
if (BoolInvertedBytes)
{
aaa=src[0];
src[0]=src[3];
src[3]=aaa;
aaa=src[1];
src[1]=src[2];
src[2]=aaa;
}
_wmemcpy(&ilong,src,4);
return ilong;
}
//-------------------------------------------------------------------------
//вспомогательная функция - преобразование 8 байт в число double
double __fastcall IEEE754double(BYTE src[8])
{
BYTE aaa;
double ddouble;

if (BoolInvertedBytes)
{
aaa=src[0];
src[0]=src[7];
src[7]=aaa;

aaa=src[1];
src[1]=src[6];
src[6]=aaa;

aaa=src[2];
src[2]=src[5];
src[5]=aaa;

aaa=src[3];
src[3]=src[4];
src[4]=aaa;
}

_wmemcpy(&ddouble,src,8);
return ddouble;
}
//-------------------------------------------------------------------------
//прочитать значение типа double из modbus-slave устройства с адресом mbadr, адрес регистров - addr_hi, addr_lo
double ReadDouble(BYTE mbadr,BYTE addr_hi,BYTE addr_lo)
{
crc_union crc_double, crc_otv_dbl;
otvet8 otvet8d;
DWORD p;

crc_double.integ=0;

zapros._1addr=mbadr;
zapros._2cmd=0x03;
zapros._3data_start_addr_hi=addr_hi;
zapros._4data_start_addr_lo=addr_lo;
zapros._5data_length_hi=0x00;
zapros._6data_length_lo=0x04;

crc_double.integ=CRC16(&zapros._1addr,6);

zapros._7crc1=crc_double.bytes[0];
zapros._8crc2=crc_double.bytes[1];

for(p=0;p<3;p++)
{
ZeroMemory(&otvet8d,sizeof(otvet8d));

PurgeComm(Comport,PURGE_TXCLEAR);
PurgeComm(Comport,PURGE_RXCLEAR);

if (WriteFile (Comport, &zapros,sizeof(zapros),
&lpNumberOfBytesWritten, NULL ))
{
Sleep(IntEditTimeout);

ClearCommError(Comport,NULL,&statistic);
if (statistic.cbInQue>=sizeof(otvet8d))
ReadFile( Comport,&otvet8d, sizeof(otvet8d),
&lpNumberOfBytesWritten, NULL );

crc_otv_dbl.integ=CRC16(&otvet8d._1from_addr,11);
if ((crc_otv_dbl.bytes[0]==otvet8d._Ccrc1) && (crc_otv_dbl.bytes[1]==otvet8d._Dcrc2))
{
p=4;
}
}
}
return IEEE754double(otvet8d._4data);
}
//---------------------------------------------------------------------------

Наверх
casey Смотреть выпадающим
Новичок
Новичок
Аватар

Присоединился: 19 Июль 2005
Категория: Ukraine
Online Status: Offline
Публикации: 39
Свойства публикации Свойства публикации   Ответить, цитируя автора - casey Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 25 Сентябрь 2006 10:23

После этих объявлений читать из устройства следует так:

int main()

{
float Rashod;
OpenCommPort(9600, 8,0,1);
Rashod=ReadFloat(0x01,0x0a,0xf2); //- 0х01 - адрес Модбас-слэйва в 16-ричной с-ме
//0х0а, 0хf2 - адреса ячейки модбас, hi и lo

}

Наверх
Рома81 Смотреть выпадающим
Новичок
Новичок


Присоединился: 07 Август 2006
Online Status: Offline
Публикации: 17
Свойства публикации Свойства публикации   Ответить, цитируя автора - Рома81 Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 25 Сентябрь 2006 13:15

casey,

вот спасибо за вспомогательные функции преобразования чисел, очень помогло - взял некоторые части

Наверх
casey Смотреть выпадающим
Новичок
Новичок
Аватар

Присоединился: 19 Июль 2005
Категория: Ukraine
Online Status: Offline
Публикации: 39
Свойства публикации Свойства публикации   Ответить, цитируя автора - casey Ответить, цитируя автора -  ОтветитьОтвет Прямая ссылка на эту публикацию Опубликовано: 25 Сентябрь 2006 15:48
Рад, что помог
Наверх
 Ответить Ответить

Переход на форум Права доступа на форуме Смотреть выпадающим

Bulletin Board Software by Web Wiz Forums® version 9.64
Powered by Web Wiz Forums Free Express Edition
Copyright ©2001-2009 Web Wiz