هناك العديد من المكتبات المتاحة للجمهور لتنفيذ أجهزة ModBus Slave ، ولكنها غالبًا ما تحتوي على وظائف زائدة ، ويصعب تعلمها وتحتوي على أخطاء جسيمة. يناقش هذا المقال مكتبة ، في رأي المؤلف المتواضع ، خالية من هذه العيوب.
يتم توفير برنامج المكتبة كرمز C مفتوح المصدر.
modbus.h
////////////////////////////////////////////////////////////////////
// ModBus v2 //
// - I //
///////////////////////////////////////////////////////////////////
#ifndef __MODBUS_H
#define __MODBUS_H
#include "main.h"
///////////////////////////////////////////////////////////////////////////////
//
//,
#define ModBusUseGlobal (0) // /, /
//
#define ModBusUseFunc1 (0) // 1 - Coils ( )
#define ModBusUseFunc2 (0) // 2 -
#define ModBusUseFunc3 (1) // 3 -
#define ModBusUseFunc4 (0) // 4 -
#define ModBusUseFunc5 (0) // 5 -
#define ModBusUseFunc6 (1) // 6 -
#define ModBusUseFunc15 (0) // 15 -
#define ModBusUseFunc16 (1) // 16 -
//
#define ModBusID (1) //
#define ModBusID_FF (255) // ,
//
#define ModBusMaxPause (5)// , [mS],
#define ModBusMaxPauseResp (2) // [mS]
//
#define ModBusMaxPaketRX (96)// <127
//
#define ModBusMaxInBit (0) //
#define ModBusMaxInBitTX (8) //
#define ModBusMaxInByte ((ModBusMaxInBit+7)/8) //
//
#define ModBusMaxOutBit (0) //
#define ModBusMaxOutByte ((ModBusMaxOutBit+7)/8) //
#define ModBusMaxOutBitTX (8) //
#define ModBusMaxOutBitRX (8) //
//
#define ModBusMaxInReg (0) // ( )
#define ModBusMaxInRegTX (24) //
// -
#define ModBusMaxOutReg (48) //
#define ModBusMaxOutRegTX (32)//
#define ModBusMaxOutRegRX (32)//
////////////////////////////////////////////////////////////////////////////////
// ,
// ,
#define ModBusSysTimer TimingDelay
// - void ModBusPUT(unsigned char A)
#define ModBusPUT(A) PutFifo0(A)
// , - unsigned short ModBusGET(void)
// 00000, 001
#define ModBusGET() Inkey16Fifo0()
////////////////////////////////////////////////////////////////////////////////
//
void ModBusIni(void);
// RTU
//
// ModbusPUT(A) ModbusGET()
void ModBusRTU(void);
// ASCII
//
// ModbusPUT(A) ModbusGET()
void ModBusASCII(void);
//
//
void Prg2ModBusOutBit(void);
void Prg2ModBusInBit(void);
void Prg2ModBusOutReg(void);
void Prg2ModBusInReg(void);
//
//
void ModBus2PrgOutBit(void);
void ModBus2PrgOutReg(void);
#pragma pack(push,1)
// /
typedef union
{
unsigned char byte;
struct
{
unsigned char bit0:1;
unsigned char bit1:1;
unsigned char bit2:1;
unsigned char bit3:1;
unsigned char bit4:1;
unsigned char bit5:1;
unsigned char bit6:1;
unsigned char bit7:1;
};
}
ModBusBit_t;
#pragma pack(pop)
#ifdef __MODBUS2PRG_C
#if ModBusMaxInBit!=0
ModBusBit_t ModBusInBit[ModBusMaxInByte]; //
#endif
#if ModBusMaxOutBit!=0
ModBusBit_t ModBusOutBit[ModBusMaxOutByte]; //
#endif
#if ModBusMaxInReg!=0
unsigned short ModBusInReg[ModBusMaxInReg]; //
#endif
#if ModBusMaxOutReg!=0
unsigned short ModBusOutReg[ModBusMaxOutReg]; //
#endif
#else
#if ModBusUseGlobal!=0 || defined(__MODBUS_C)
#if ModBusMaxInBit!=0
extern ModBusBit_t ModBusInBit[ModBusMaxInByte]; //
#endif
#if ModBusMaxOutBit!=0
extern ModBusBit_t ModBusOutBit[ModBusMaxOutByte]; //
#endif
#if ModBusMaxInReg!=0
extern unsigned short ModBusInReg[ModBusMaxInReg]; //
#endif
#if ModBusMaxOutReg!=0
extern unsigned short ModBusOutReg[ModBusMaxOutReg]; //
#endif
#endif//#if ModBusUseGlobal!=0
#endif//#ifdef __MODBUS2PRG_C
#endif//#ifndef __MODBUS_H
modbus.c
#define __MODBUS_C
#include "modbus.h"
static unsigned char PaketRX[ModBusMaxPaketRX];//
static unsigned char UkPaket;// ,
static unsigned long TimModbus; //
static unsigned short CRCmodbus;// CRC
static unsigned char Sost;// 0/1 /
//
void ModBusIni(void)
{
TimModbus=ModBusSysTimer;//
UkPaket=0;//
CRCmodbus=0xFFFF; // CRC
//
#if ModBusMaxOutBit!=0
Prg2ModBusOutBit();
#endif
#if ModBusMaxInBit!=0
Prg2ModBusInBit();
#endif
#if ModBusMaxOutReg!=0
Prg2ModBusOutReg();
#endif
#if ModBusMaxInReg!=0
Prg2ModBusInReg();
#endif
return;
}
// CRC
static inline unsigned short CRCfunc(unsigned short inCRC, unsigned char in)
{
inCRC=inCRC^in;
for(int j=0;j<8;j++){if(inCRC&1){inCRC=(inCRC>>1)^0xA001U;}else {inCRC=inCRC>>1;}}
return inCRC;
}
//
void ModBusRTU(void)
{
if(Sost==0)
{//
while(!0)
{//
unsigned short Tmp=ModBusGET(); //
if(Tmp==0) return; // -
//
Tmp=Tmp&0xFF;//
//
if((ModBusSysTimer-TimModbus)>ModBusMaxPause)
{// ,
PaketRX[0]=Tmp;//
UkPaket=1;//
TimModbus=ModBusSysTimer;//
// CRC
CRCmodbus=CRCfunc(0xFFFF,Tmp);
continue;//
}
else
{// ,
TimModbus=ModBusSysTimer;//
PaketRX[UkPaket]=Tmp;//
UkPaket++;//
if(UkPaket==ModBusMaxPaketRX)//
{//
UkPaket=0;//
CRCmodbus=0xFFFF; // CRC
return;//,
}
// CRC
CRCmodbus=CRCfunc(CRCmodbus,Tmp);
}
//
if(UkPaket<8) continue; //
//
if(CRCmodbus==0)
{//
if(PaketRX[1]==15 || PaketRX[1]==16)
{// (15,16) , " "
if((PaketRX[6]+9)!=UkPaket) continue;
}
break; //! !!!
}
}
//////////////////////////////////////////////////////////////////////////////
// ! !!!
/////////////////////////////////////////////////////////////////////////////
UkPaket=0;//
//
if((PaketRX[0]!=ModBusID)&&(PaketRX[0]!=ModBusID_FF))
{//
CRCmodbus=0xFFFF; // CRC
return;//
}
//
Sost=!0;
#if ModBusMaxPauseResp!=0
return;//
#endif
}
/////////////////////////////////////////////////////////////////////////////
if(Sost!=0
#if ModBusMaxPauseResp!=0
&& (ModBusSysTimer-TimModbus)>=ModBusMaxPauseResp
#endif
)
{//
Sost=0;
/////////////////////////////////////////////////////////////////////////////
// //
/////////////////////////////////////////////////////////////////////////////
// 01 - Coils ( ).
/*- .
0.
- ,
8 .
, .
.
01
ON/OFF .
.
: 1-16 0-15.
20-56 17.
(Hex)
11 0
01 1
Hi 00 2
Lo 13 3
Hi 00 4
Lo 25 5
(CRC LRC) --
.
, 0.
.
(Hex)
11 0
01 1
05 2
( 27-20) CD 3
( 35-28) 6B 4
( 43-36) B2 5
( 51-44) 0E 6
( 56-52) 1B 7
(CRC LRC) --
*/
#if ModBusUseFunc1!=0
if(PaketRX[1]==0x01)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if((AdresBit+KolvoBit)>(ModBusMaxOutBit) || KolvoBit>ModBusMaxOutBitTX || KolvoBit==0)
{//
CRCmodbus=0xFFFF; // CRC
return;//
}
Prg2ModBusOutBit();// (GlobalDate->ModBus)
//
//
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]);
//
ModBusPUT(1);
CRCmodbus=CRCfunc(CRCmodbus,1);
//
ModBusPUT((KolvoBit+7)>>3);
CRCmodbus=CRCfunc(CRCmodbus,((KolvoBit+7)>>3));
//
unsigned char TxByte=0;//
unsigned char Bit=AdresBit&7;// ModBusOutBit[]
AdresBit=AdresBit>>3;// ModBusOutBit[]
// ModBusOutBit[]
int i=0;
while(!0)
{
if((ModBusOutBit[AdresBit].byte)&(1<<Bit))
{
TxByte=TxByte|(1<<(i&7));
}
//
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
break;
}
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
// 2 -
/*02 Read Input Status
ON/OFF ( 1) .
. 0.
10197-10218 17.
(Hex)
11 0
02 1
. 00 2
. C4 3
- . 00 4
- . 16 5
--
.
, 0.
.
(Hex)
11 0
01 1
03 2
( 10204-10197) AC 3
( 10212-10205) DB 4
( 10218-10213) 35 5
(CRC LRC) --
*/
#if ModBusUseFunc2!=0
if(PaketRX[1]==0x02)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if((AdresBit+KolvoBit)>(ModBusMaxInBit) || KolvoBit>ModBusMaxInBitTX || KolvoBit==0)
{//
CRCmodbus=0xFFFF; // CRC
return;//
}
Prg2ModBusInBit();// (GlobalDate->ModBus)
//
//
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]);
//
ModBusPUT(2);
CRCmodbus=CRCfunc(CRCmodbus,2);
//
ModBusPUT((KolvoBit+7)>>3);
CRCmodbus=CRCfunc(CRCmodbus,((KolvoBit+7)>>3));
//
unsigned char TxByte=0;//
unsigned char Bit=AdresBit&7;//
AdresBit=AdresBit>>3;//
// ModBusInBit[]
int i=0;
while(!0)
{
if((ModBusInBit[AdresBit].byte)&(1<<Bit))
{//
TxByte=TxByte|(1<<(i&7));
}
//
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
break;
}
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
// 03 - / .
/*- / ( ),
. 0.
03 Read Holding Registers
( 4) .
.
0: 1-16 0-15.
40108-40110 17.
(Hex)
11 0
03 1
. 00 2
. 6B 3
- . 00 4
- . 03 5
--
.
, .
125 984-8 (984-685 ..),
32 . .
:
(Hex)
11 0
03 1
06 2
( 40108) . 02 3
( 40108) . 2B 4
( 40109) . 00 5
( 40109) . 00 6
( 40110) . 00 7
( 40110) . 64 8
--
*/
#if ModBusUseFunc3!=0
if(PaketRX[1]==0x03)
{
//
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || (KolvoWord>ModBusMaxOutRegTX))
{//
CRCmodbus=0xFFFF;// CRC
return;//,
}
Prg2ModBusOutReg();// (GlobalDate->ModBus)
//
//
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]);
//
ModBusPUT(3);
CRCmodbus=CRCfunc(CRCmodbus,3);
//
ModBusPUT(KolvoWord<<1);
CRCmodbus=CRCfunc(CRCmodbus,(KolvoWord<<1));
// ModBusOutReg[]
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(ModBusOutReg[AdresWord+i]>>8);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusOutReg[AdresWord+i]>>8));
ModBusPUT(ModBusOutReg[AdresWord+i]>>0);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusOutReg[AdresWord+i]>>0));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
// 04 -
/*04 Read Input Registers
( 3) .
.
30009 17.
(Hex)
11 0
03 1
. 00 2
. 6B 3
- . 00 4
- . 03 5
--
.
, .
125 984-8 (984-685 ..),
32 . .
:
(Hex)
11 0
03 1
02 2
( 30009) . 00 3
( 30009) . 2A 4
--
*/
#if ModBusUseFunc4!=0
if(PaketRX[1]==0x04)
{
//
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if(((AdresWord+KolvoWord)>ModBusMaxInReg) || (KolvoWord>ModBusMaxInRegTX))
{//
CRCmodbus=0xFFFF;// CRC
return;//,
}
Prg2ModBusInReg();// (GlobalDate->ModBus)
//
//
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,(PaketRX[0]));
//
ModBusPUT(4);
CRCmodbus=CRCfunc(CRCmodbus,4);
//
ModBusPUT(KolvoWord<<1);
CRCmodbus=CRCfunc(CRCmodbus,(KolvoWord<<1));
// ModBusInReg[]
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(ModBusInReg[AdresWord+i]>>8);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusInReg[AdresWord+i]>>8));
ModBusPUT(ModBusInReg[AdresWord+i]>>0);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusInReg[AdresWord+i]>>0));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
// 05 - /
/*05 Force Single Coil
( 0) ON OFF.
.
.
. 0. 1 0.
, (ON/OFF) .
FF00 Hex - ON. 0000 - OFF. .
173 ON 17.
(Hex)
11 0
05 1
. 00 2
. AC 3
. FF 4
. 00 5
--
.
(Hex)
11 0
05 1
. 00 2
. AC 3
. FF 4
. 00 5
--
*/
#if ModBusUseFunc5!=0
if(PaketRX[1]==0x05)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
if(AdresBit>=ModBusMaxOutBit)
{//
CRCmodbus=0xFFFF; // CRC
return;//,
}
//
switch (((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])))
{
case 0xFF00:
//
ModBusOutBit[(AdresBit>>3)].byte|=(1<<(AdresBit&7));
break;
case 0x0000:
//
ModBusOutBit[(AdresBit>>3)].byte&=(~(1<<(AdresBit&7)));
break;
default:
{//
CRCmodbus=0xFFFF; // CRC
return;//,
}
}
//
for(int i=0;i<8;i++) ModBusPUT(PaketRX[i]);//
ModBus2PrgOutBit();// (ModBus->GlobalDate)
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
// 06 - / .
/* 05, ().
/ .
06 Preset Single Register
. ( 4).
.
.
, . 0.
, .
M84 484 10- , 0.
16 .
40002 0003 Hex 17.
(Hex)
11 0
06 1
. 00 2
. 01 3
. 00 4
. 03 5
--
.
(Hex)
11 0
06 1
. 00 2
. 01 3
. 00 4
. 03 5
--
*/
#if ModBusUseFunc6!=0
if(PaketRX[1]==0x06)
{
//
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
if(AdresWord>=(ModBusMaxOutReg))
{//
CRCmodbus=0xFFFF; // CRC
return;//,
}
//
ModBusOutReg[AdresWord]=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//
for(int i=0;i<8;i++) ModBusPUT(PaketRX[i]);//
ModBus2PrgOutReg();// (ModBus->GlobalDate)
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
// 0x0F - / .
/* , , .
15 (0F Hex) Force Multiple Coils
( 0) ON OFF.
.
.
. 0.
20 ( 19)
17.
2 : CD 01 Hex (1100 1101 0000 0001 ).
:
: 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1
: 27 26 25 24 23 22 21 20 - - - - - - 29 28
(Hex)
11 0
0F 1
. 00 2
. 13 3
- . 00 4
- . 0A 5
02 6
( 27-20) CD 7
( 29-28) 01 8
-- 9
, , , .
.
(Hex)
11 0
0F 1
. 00 2
. 13 3
- . 00 4
- . 0A 5
--
*/
#if ModBusUseFunc15!=0
if(PaketRX[1]==0x0F)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoBit=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//
if(((AdresBit+KolvoBit)>ModBusMaxOutBit) || (KolvoBit>ModBusMaxOutBitRX))
{//
CRCmodbus=0xFFFF; // CRC
return;//,
}
//
unsigned char Bit=(AdresBit&7);// ModBusOutBit[]
AdresBit=AdresBit>>3;// ModBusOutBit[]
//
for(int i=0;i<KolvoBit;i++)
{
if(PaketRX[7+(i>>3)]&(1<<(i&7)))// PaketRX 1
{// ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)|((unsigned char)(1<<Bit));
}
else
{// ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)&((unsigned char)(~(1<<Bit)));
}
//
Bit++;if(Bit==8){Bit=0;AdresBit++;}
}
// CRC
CRCmodbus=0xFFFF;
for(int i=0;i<6;i++)
{
ModBusPUT(PaketRX[i]);
CRCmodbus=CRCfunc(CRCmodbus,(PaketRX[i]));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
ModBus2PrgOutBit();// (ModBus->GlobalDate)
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
// 0x10 / .
/*16 (10 Hex) Preset Multiple Regs
( 4).
, .
.
. 0.
.
M84 484 10- , 0.
16 .
40002 00 0A 01 02 Hex,
17:
(Hex)
11 0
10 1
00 2
01 3
- . 00 4
- . 02 5
04 6
. 00 7
. 0A 8
. 01 9
. 02 10
--
, , , .
*/
#if ModBusUseFunc16!=0
if(PaketRX[1]==0x10)
{
//
unsigned short b=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short c=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//
if(((b+c)>ModBusMaxOutReg) || c>ModBusMaxOutRegRX || c==0)
{//
CRCmodbus=0xFFFF;// CRC
return;//,
}
// ModBusOutReg[]
for(int i=0;i<c;i++)
{
ModBusOutReg[b+i]=(((unsigned short)PaketRX[7+(i<<1)])<<8)|(PaketRX[8+(i<<1)]);
}
// CRC
CRCmodbus=0xFFFF;
for(int i=0;i<6;i++)
{
ModBusPUT(PaketRX[i]);
CRCmodbus=CRCfunc(CRCmodbus,(PaketRX[i]));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
ModBus2PrgOutReg();// (ModBus->GlobalDate)
//
CRCmodbus=0xFFFF; // CRC
return;//
}
#endif
/////////////////////////////////////////////////////////////////////////////
//
CRCmodbus=0xFFFF; // CRC
return;////, ,
}
return;//
}
//
static inline unsigned char Hex2Dig(unsigned char h)
{
if((h>='0')&&(h<='9')) return (h -'0');
if((h>='A')&&(h<='F')) return (h -'A'+10);
return 0;
}
static unsigned char LRCmodbus;// LRC
static unsigned char Simvol0;//
#define ASCII_CR (0x0D)//
#define ASCII_LF (0x0A)//
static const unsigned char BCD[]="0123456789ABCDEF";//
// ASCII
void ModBusASCII(void)
{
if(Sost==0)
{//
while(!0)
{//
unsigned short Tmp=ModBusGET(); //
if(Tmp==0) return; //
//
Tmp=Tmp&0xFF;//
//
if(Tmp==':')
{//
LRCmodbus=0;// LRC
UkPaket=0;// ,
continue;//
}
//
if(!(
((Tmp>='0')&&(Tmp<='9'))||
((Tmp>='A')&&(Tmp<='F'))||
(Tmp==ASCII_CR)||
(Tmp==ASCII_LF)
))
{
return;//,
}
//
if((UkPaket&1)==0)
{// 0,2,4,6...
Simvol0=Tmp; //
UkPaket++; //
continue;//
}
else
{// 1,3,5,7...
if(Tmp!=ASCII_LF)
{//
PaketRX[UkPaket>>1]=(Hex2Dig(Simvol0)<<4)|(Hex2Dig(Tmp));//
LRCmodbus=LRCmodbus-PaketRX[UkPaket>>1];// LRC
UkPaket++;//
if(UkPaket>(ModBusMaxPaketRX<<1))//
{//
UkPaket=0;//
return;//,
}
}
else break;
}
}
// LCR
if(LRCmodbus!=0) return;//,
//
if((PaketRX[0]!=ModBusID)&&(PaketRX[0]!=ModBusID_FF))
{//
return;//
}
//
Sost=!0;
TimModbus=ModBusSysTimer;//
#if ModBusMaxPauseResp!=0
return;//
#endif
}
/////////////////////////////////////////////////////////////////////////////
if(Sost!=0
#if ModBusMaxPauseResp!=0
&& (ModBusSysTimer-TimModbus)>=ModBusMaxPauseResp
#endif
)
{//
Sost=0;
/////////////////////////////////////////////////////////////////////////////
// //
/////////////////////////////////////////////////////////////////////////////
#if ModBusUseFunc1!=0
//01
if(PaketRX[1]==0x01)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if((AdresBit+KolvoBit)>(ModBusMaxOutBit) || KolvoBit>ModBusMaxOutBitTX || KolvoBit==0)
{//
return;//,
}
Prg2ModBusOutBit();// (GlobalDate->ModBus)
//
ModBusPUT(':');
//
ModBusPUT(BCD[PaketRX[0]>>4]);//
ModBusPUT(BCD[PaketRX[0]&0x0F]);//
LRCmodbus=0-PaketRX[0];// LRC
//
ModBusPUT(BCD[1>>4]);//
ModBusPUT(BCD[1&0x0F]);//
LRCmodbus=LRCmodbus-1;// LRC
//
ModBusPUT(BCD[((KolvoBit+7)>>3)>>4]);//
ModBusPUT(BCD[((KolvoBit+7)>>3)&0x0F]);//
LRCmodbus=LRCmodbus-((KolvoBit+7)>>3);// LRC
//
unsigned char TxByte=0;//
unsigned char Bit=AdresBit&7;// ModBusOutBit[]
AdresBit=AdresBit>>3;// ModBusOutBit[]
// ModBusOutBit[]
int i=0;
while(!0)
{
if((ModBusOutBit[AdresBit].byte)&(1<<Bit))// ModBusOutBit[] 1
{//
TxByte=TxByte|(1<<(i&7));
}
//
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(BCD[TxByte>>4]);//
ModBusPUT(BCD[TxByte&0x0F]);//
LRCmodbus=LRCmodbus-TxByte;// LRC
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(BCD[TxByte>>4]);//
ModBusPUT(BCD[TxByte&0x0F]);//
LRCmodbus=LRCmodbus-TxByte;// LRC
break;
}
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//
return;//
}
#endif
#if ModBusUseFunc2!=0
//02 Read Input Status
if(PaketRX[1]==0x02)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if((AdresBit+KolvoBit)>(ModBusMaxInBit) || KolvoBit>ModBusMaxInBitTX || KolvoBit==0)
{//
return;//,
}
Prg2ModBusInBit();// (GlobalDate->ModBus)
//
ModBusPUT(':');
//
ModBusPUT(BCD[PaketRX[0]>>4]);//
ModBusPUT(BCD[PaketRX[0]&0x0F]);//
LRCmodbus=0-PaketRX[0];// LRC
//
ModBusPUT(BCD[2>>4]);//
ModBusPUT(BCD[2&0x0F]);//
LRCmodbus=LRCmodbus-2;// LRC
//
ModBusPUT(BCD[((KolvoBit+7)>>3)>>4]);//
ModBusPUT(BCD[((KolvoBit+7)>>3)&0x0F]);//
LRCmodbus=LRCmodbus-((KolvoBit+7)>>3);// LRC
//
unsigned char TxByte=0;//
unsigned char Bit=AdresBit&7;// ModBusOutBit[]
AdresBit=AdresBit>>3;// ModBusOutBit[]
// ModBusOutBit[]
int i=0;
while(!0)
{
if((ModBusInBit[AdresBit].byte)&(1<<Bit))// ModBusOutBit[] 1
{//
TxByte=TxByte|(1<<(i&7));
}
//
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(BCD[TxByte>>4]);//
ModBusPUT(BCD[TxByte&0x0F]);//
LRCmodbus=LRCmodbus-TxByte;// LRC
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(BCD[TxByte>>4]);//
ModBusPUT(BCD[TxByte&0x0F]);//
LRCmodbus=LRCmodbus-TxByte;// LRC
break;
}
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//
return;//
}
#endif
#if ModBusUseFunc3!=0
//03 Read Holding Registers
if(PaketRX[1]==0x03)
{
//
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || KolvoWord>ModBusMaxOutRegTX)
{//
return;//,
}
Prg2ModBusOutReg();// (GlobalDate->ModBus)
//
ModBusPUT(':');
//
ModBusPUT(BCD[PaketRX[0]>>4]);//
ModBusPUT(BCD[PaketRX[0]&0x0F]);//
LRCmodbus=0-PaketRX[0];// LRC
//
ModBusPUT(BCD[3>>4]);//
ModBusPUT(BCD[3&0x0F]);//
LRCmodbus=LRCmodbus-3;// LRC
//
ModBusPUT(BCD[(KolvoWord<<1)>>4]);//
ModBusPUT(BCD[(KolvoWord<<1)&0x0F]);//
LRCmodbus=LRCmodbus-(KolvoWord<<1);// LRC
// ModBusOutReg[]
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(BCD[((ModBusOutReg[AdresWord+i])>>8)>>4]);//
ModBusPUT(BCD[((ModBusOutReg[AdresWord+i])>>8)&0x0F]);//
LRCmodbus=LRCmodbus-((ModBusOutReg[AdresWord+i])>>8);// LRC
ModBusPUT(BCD[(((ModBusOutReg[AdresWord+i])>>0)>>4)&0x0F]);//
ModBusPUT(BCD[(((ModBusOutReg[AdresWord+i])>>0)>>0)&0x0F]);//
LRCmodbus=LRCmodbus-((ModBusOutReg[AdresWord+i])>>0);// LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//
return;//
}
#endif
#if ModBusUseFunc4!=0
//04 Read Input Registers
if(PaketRX[1]==0x04)
{
//
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//
if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || KolvoWord>ModBusMaxOutRegTX)
{//
return;//,
}
Prg2ModBusInReg();// (GlobalDate->ModBus)
//
ModBusPUT(':');
//
ModBusPUT(BCD[PaketRX[0]>>4]);//
ModBusPUT(BCD[PaketRX[0]&0x0F]);//
LRCmodbus=0-PaketRX[0];// LRC
//
ModBusPUT(BCD[4>>4]);//
ModBusPUT(BCD[4&0x0F]);//
LRCmodbus=LRCmodbus-4;// LRC
//
ModBusPUT(BCD[(KolvoWord<<1)>>4]);//
ModBusPUT(BCD[(KolvoWord<<1)&0x0F]);//
LRCmodbus=LRCmodbus-(KolvoWord<<1);// LRC
// ModBusOutReg[]
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(BCD[((ModBusInReg[AdresWord+i])>>8)>>4]);//
ModBusPUT(BCD[((ModBusInReg[AdresWord+i])>>8)&0x0F]);//
LRCmodbus=LRCmodbus-((ModBusInReg[AdresWord+i])>>8);// LRC
ModBusPUT(BCD[(((ModBusInReg[AdresWord+i])>>0)>>4)&0x0F]);//
ModBusPUT(BCD[(((ModBusInReg[AdresWord+i])>>0)>>0)&0x0F]);//
LRCmodbus=LRCmodbus-((ModBusInReg[AdresWord+i])>>0);// LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//
return;//
}
#endif
#if ModBusUseFunc5!=0
//05 Force Single Coil
if(PaketRX[1]==0x05)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
if(AdresBit>=ModBusMaxOutBit)//
{//
return;//,
}
//
switch (((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])))
{
case 0xFF00:
//
ModBusOutBit[(AdresBit>>3)].byte|=(1<<(AdresBit&7));
break;
case 0x0000:
//
ModBusOutBit[(AdresBit>>3)].byte&=(~(1<<(AdresBit&7)));
break;
default:
{ //
return;//,
}
}
//
ModBusPUT(':');
for(int i=0;i<7;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//
ModBusPUT(BCD[PaketRX[i]&0x0F]);//
}
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutBit();// (ModBus->GlobalDate)
//
return;//
}
#endif
#if ModBusUseFunc6!=0
//06 Preset Single Register
if(PaketRX[1]==0x06)
{
//
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
if(AdresWord>=(ModBusMaxOutReg))//
{//
return;//,
}
//
ModBusOutReg[AdresWord]=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//
ModBusPUT(':');
for(int i=0;i<7;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//
ModBusPUT(BCD[PaketRX[i]&0x0F]);//
}
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutReg();// (ModBus->GlobalDate)
//
return;//
}
#endif
#if ModBusUseFunc15!=0
//15 (0F Hex) Force Multiple Coils
if(PaketRX[1]==0x0F)
{
//
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short KolvoBit=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//
if(((AdresBit+KolvoBit)>ModBusMaxOutBit) || (KolvoBit>ModBusMaxOutBitRX))
{//
return;//,
}
//
unsigned char Bit=(AdresBit&7);// ModBusOutBit[]
AdresBit=AdresBit>>3;// ModBusOutBit[]
//
for(int i=0;i<KolvoBit;i++)
{
if(PaketRX[7+(i>>3)]&(1<<(i&7)))// PaketRX 1
{// ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)|((unsigned char)(1<<Bit));
}
else
{// ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)&((unsigned char)(~(1<<Bit)));
}
//
Bit++;if(Bit==8){Bit=0;AdresBit++;}
}
// LRC
LRCmodbus=0;
ModBusPUT(':');
for(int i=0;i<6;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//
ModBusPUT(BCD[PaketRX[i]&0x0F]);//
LRCmodbus=LRCmodbus-PaketRX[i];// LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutBit();// (ModBus->GlobalDate)
//
return;//
}
#endif
#if ModBusUseFunc16!=0
//16 (10 Hex) Preset Multiple Regs
if(PaketRX[1]==0x10)
{
//
unsigned short b=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//
unsigned short c=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//
if(((b+c)>ModBusMaxOutReg) || c>ModBusMaxOutRegRX)
{
//
return;//,
}
// ModBusOutReg[]
for(int i=0;i<c;i++)
{
ModBusOutReg[b+i]=(((unsigned short)PaketRX[7+(i<<1)])<<8)|(PaketRX[8+(i<<1)]);
}
// LRC
LRCmodbus=0;
ModBusPUT(':');
for(int i=0;i<6;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//
ModBusPUT(BCD[PaketRX[i]&0x0F]);//
LRCmodbus=LRCmodbus-PaketRX[i];// LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutReg();// (ModBus->GlobalDate)
//
return;//
}
#endif
}
//
return;//, ,
}
ModBus2Prg.c
#define __MODBUS2PRG_C
#include "modbus.h"
//
//
void Prg2ModBusOutBit(void)
{//
return;
}
void Prg2ModBusInBit(void)
{//
//ModBusInBit[0].bit0=1;
return;
}
void Prg2ModBusOutReg(void)
{// 4 /
return;
}
void Prg2ModBusInReg(void)
{// 3
return;
}
//
//
void ModBus2PrgOutBit(void)
{//
return;
}
void ModBus2PrgOutReg(void)
{// 4 /
return;
}
يحتوي ملف modbus.h على التعريفات المطلوبة وخيارات الترجمة وثوابت التوليف. دعنا نصف بإيجاز الخيارات الرئيسية وضبط المعلمات.
ModBusUseFunc1 - ModBusUseFunc15 - خيار التحويل البرمجي الذي يحدد استخدام وظائف بروتوكول ModBus. تعمل التطبيقات العملية لأجهزة ModBus مع مجموعة محدودة من وظائف البروتوكول ، في أغلب الأحيان ، الوظائف 3،6 و 16. ليست هناك حاجة لتضمين رمز إضافي في المشروع.
ModBusID ، ModBusID_FF - عناوين على ناقل ModBus. يدعم هذا التطبيق للبروتوكول عنوانين. يمكن أن يكون هذا مفيدًا لتشغيل الأجهزة ، ويكون عنوان ModBusID هو عنوان الجهاز القابل للتكوين ، وعنوان ModBusID_FF هو عنوان تخصيص الجهاز.
ModBusMaxPause- يتم تعيين التوقف المؤقت بين الأحرف لتحديد بداية الحزمة في الكم ModBusSysTimer. عادةً ما يكون كم ModBusSysTimer 1 مللي ثانية. بالنسبة لمعظم التطبيقات ، يعد الامتثال للمهلة الموضحة في معيار البروتوكول أمرًا مستحيلًا.
على سبيل المثال ، لن يتمكن ModBus Master الذي يعمل على جهاز Win من توفير المهلات التي يتطلبها البروتوكول. لذلك ، يمكن اعتبار تعيين شريحة زمنية أقل من 1 مللي ثانية غير عملي. تظهر الملاحظات العملية أن قيمة ModBusMaxPause يجب أن تكون في حدود 5-10 مللي ثانية.
ModBusMaxPauseResp - توقف مؤقتًا بين طلب السيد واستجابة التابع. العديد من أجهزة ModBus Master لديها تأخير في التبديل من الإرسال إلى الاستقبال ، ويمكن تعويض هذا التأخير عن طريق هذا الثابت.
ModBusMaxInBit و ModBusMaxOutBit و ModBusMaxInReg و ModBusMaxOutReg- عدد المدخلات والمخرجات وسجلات القراءة وسجلات القراءة / الكتابة. يحتفظ البرنامج بالذاكرة لتسجيلات ModBus. إذا لم يتم استخدام نوع معين من السجلات ، فيجب تحديد القيمة على أنها صفر.
ModBusMaxInBitTX و ModBusMaxOutBitTX و ModBusMaxInRegTX و ModBusMaxOutRegTX - الحد الأقصى لعدد المدخلات والمخرجات وسجلات القراءة المنفصلة وسجلات القراءة / كتابة سجلات الإخراج في الحزمة المرسلة. يجب أن يتطابق هذا الإعداد مع الإعداد المقابل في ModBus Master.
لنقل المكتبة إلى أي نظام أساسي ، يجب تحديد ثلاث وظائف من خلال وحدات الماكرو.
ModBusSysTimer- مؤقت النظام ، متغير يتزايد كل مللي ثانية في سلسلة منفصلة للتنفيذ. يمكن أن يكون هذا المتغير uwTick ، من مكتبة STM32 HAL ، أو ساعة وظيفة C القياسية () .
ModBusPUT باطلة (حرف غير موقعة) - كتابة بايت إلى دفق تسلسلي.
ModBusGET قصير بدون توقيع (باطل) - قراءة بايت من دفق تسلسلي. إذا لم تكن هناك بيانات في الدفق التسلسلي ، فإن الدالة ترجع 0 ، إذا كانت هناك بيانات ، فإن القيمة المعادة هي البايت العالي 0x01 ، بيانات قراءة البايت المنخفضة.
لاستخدام المكتبة ، تحتاج إلى ملء نص وظائف Prg2ModBusOutBit () و Prg2ModBusInBit () و Prg2ModBusOutReg () و Prg2ModBusInReg ()مسؤول عن نسخ متغيرات المستخدم إلى سجلات ModBus. أيضًا ، من الضروري ملء نص
وظائف ModBus2PrgOutBit () و ModBus2PrgOutReg () المسؤولة عن نسخ سجلات ModBus في متغيرات المستخدم. في نص هذه الوظائف ، يمكنك تنفيذ بعض الإجراءات المتعلقة بتغيير السجلات ، على سبيل المثال ، التحقق من القيم الصالحة.
على سبيل المثال:
void Prg2ModBusOutReg(void)
{// , 4 /
ModBusOutReg[0]=A;
ModBusOutReg[1]=B;
ModBusOutReg[2]=C;
return;
}
void ModBus2PrgOutReg(void)
{ // 4, /
if(ModBusOutReg[0] < MaxA) A= ModBusOutReg[0];
B=ModBusOutReg[1];
C=ModBusOutReg[2];
return;
}
يُسمح بعدم ملء نص الوظائف المحددة ، ولكن يُسمح بالعمل مع السجلات مباشرةً ، أثناء استخدام خيار ModBusUseGlobal .
لتهيئة جهاز ModBus ، قم باستدعاء الدالة ModBusIni () . توفر وظائف ModBusRTU () أو ModBusASCII () تشغيل الجهاز عبر بروتوكولات RTU و ASCII ، على التوالي. يجب استدعاؤهم في الحلقة الرئيسية للبرنامج:
ModBusIni();
while(!0)
{
if(ModBusTip==RTU) ModBusRTU(); else ModBusASCII();
}
لا تنسَ أنه قبل التهيئة واستدعاء الوظيفة التي تضمن تشغيل جهاز ModBus ، يجب أن تهتم بتهيئة الدفق التسلسلي (UART). تعتمد قرارات البرامج المتعلقة بتنظيم دفق الإدخال / الإخراج على النظام الأساسي للأجهزة وهي خارج نطاق هذه المقالة.
تم اختبار هذه المكتبة باستخدام خادم Kepware OPC ، ولوحات SIMATIC و Wientek ، بالإضافة إلى ModBus Masters الأخرى ، في مجموعة متنوعة من الأجهزة القائمة على متحكمات PIC و STM32 ، وأظهرت أداء 142٪. ستجعل سهولة نقل هذه المكتبة من السهل تكييفها مع أنواع أخرى من ميكروكنترولر 8-16-32 بت.