Es gibt viele öffentlich verfügbare Bibliotheken für die Implementierung von ModBus Slave-Geräten, aber sie enthalten häufig redundante Funktionen, sind schwer zu erlernen und enthalten schwerwiegende Fehler. Dieser Artikel beschreibt eine Bibliothek, die nach bescheidener Meinung des Autors diese Mängel nicht aufweist.
Die Bibliothekssoftware wird als Open Source C-Code geliefert.
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;
}
Die Datei modbus.h enthält die erforderlichen Deklarationen, Kompilierungsoptionen und Optimierungskonstanten. Lassen Sie uns kurz die Hauptoptionen und Optimierungsparameter beschreiben.
ModBusUseFunc1 - ModBusUseFunc15 - Kompilierungsoption, die die Verwendung von Funktionen des ModBus-Protokolls bestimmt. Praktische Implementierungen von ModBus-Geräten arbeiten mit einem begrenzten Satz von Protokollfunktionen, meistens den Funktionen 3, 6 und 16. Es ist nicht erforderlich, zusätzlichen Code in das Projekt aufzunehmen.
ModBusID, ModBusID_FF - Adressen auf dem ModBus-Bus. Diese Implementierung des Protokolls unterstützt zwei Adressen. Dies kann nützlich sein, um Geräte in Betrieb zu nehmen. Die ModBusID-Adresse ist die konfigurierbare Geräteadresse und die ModBusID_FF-Adresse ist die Adresse zum Anpassen des Geräts.
ModBusMaxPause- Die Pause zwischen den Zeichen, um den Beginn des Pakets zu bestimmen, wird in den ModBusSysTimer-Quanten festgelegt. Typischerweise beträgt das ModBusSysTimer-Quantum 1 ms. Für die meisten Anwendungen ist die Einhaltung der im Protokollstandard beschriebenen Zeitüberschreitungen einfach unmöglich.
Beispielsweise kann ModBus Master, der auf einem Win-Computer ausgeführt wird, niemals die vom Protokoll geforderten Zeitüberschreitungen bereitstellen. Daher kann das Einstellen einer Zeitscheibe von weniger als 1 ms als unpraktisch angesehen werden. Praktische Beobachtungen zeigen, dass der ModBusMaxPause-Wert in der Größenordnung von 5-10 mS liegen sollte.
ModBusMaxPauseResp - Pause zwischen der Master-Anfrage und der Slave-Antwort. Viele ModBus-Master-Geräte haben eine Verzögerung beim Umschalten von Senden zu Empfangen. Diese Verzögerung kann durch diese Konstante ausgeglichen werden.
ModBusMaxInBit, ModBusMaxOutBit, ModBusMaxInReg, ModBusMaxOutReg- Die Anzahl der diskreten Ein- und Ausgänge, Register zum Lesen, Register zum Lesen / Schreiben. Das Programm reserviert Speicher für ModBus-Register. Wenn ein bestimmter Registertyp nicht verwendet wird, muss der Wert als Null angegeben werden.
ModBusMaxInBitTX, ModBusMaxOutBitTX, ModBusMaxInRegTX, ModBusMaxOutRegTX - Die maximale Anzahl von diskreten Eingängen, Ausgängen, Registern zum Lesen, Registern zum Lesen / Schreiben von Ausgangsregistern im übertragenen Paket. Diese Einstellung muss mit der entsprechenden Einstellung am ModBus Master übereinstimmen.
Um die Bibliothek auf eine beliebige Plattform zu portieren, müssen Sie drei Funktionen über Makros angeben.
ModBusSysTimer- Systemtimer, eine Variable, die jede Millisekunde in einem separaten Ausführungsthread erhöht wird. Diese Variable kann uwTick aus der STM32 HAL-Bibliothek oder die Standard-C-Funktionsuhr () sein .
void ModBusPUT (vorzeichenloses Zeichen A) - Schreiben eines Bytes in einen seriellen Stream.
unsigned short ModBusGET (void) - Liest ein Byte aus einem seriellen Stream. Wenn der serielle Stream keine Daten enthält, gibt die Funktion 0 zurück. Wenn Daten vorhanden sind, ist der Rückgabewert das High-Byte 0x01, das Low-Byte die gelesenen Daten.
Um die Bibliothek verwenden zu können, müssen Sie den Hauptteil der Funktionen Prg2ModBusOutBit (), Prg2ModBusInBit (), Prg2ModBusOutReg (), Prg2ModBusInReg () ausfüllenverantwortlich für das Kopieren von Benutzervariablen in ModBus-Register. Außerdem muss der Hauptteil der Funktionen
ModBus2PrgOutBit () und ModBus2PrgOutReg () ausgefüllt werden , die für das Kopieren von ModBus-Registern in Benutzervariablen verantwortlich sind. Im Hauptteil dieser Funktionen können Sie einige Aktionen ausführen, die sich auf das Ändern von Registern beziehen, z. B. das Überprüfen auf gültige Werte.
Zum Beispiel:
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;
}
Es ist erlaubt, nicht den Hauptteil der angegebenen Funktionen auszufüllen, sondern direkt mit Registern zu arbeiten, während die Option ModBusUseGlobal verwendet wird .
Rufen Sie zum Initialisieren eines ModBus-Geräts die Funktion ModBusIni () auf . Die Funktionen ModBusRTU () oder ModBusASCII () stellen den Gerätebetrieb über die Protokolle RTU und ASCII bereit. Sie müssen in der Hauptschleife des Programms aufgerufen werden:
ModBusIni();
while(!0)
{
if(ModBusTip==RTU) ModBusRTU(); else ModBusASCII();
}
Vergessen Sie nicht, dass Sie sich vor dem Initialisieren und Aufrufen der Funktion, die den Betrieb des ModBus-Geräts sicherstellt, um die Initialisierung des seriellen Streams (UART) kümmern müssen. Softwareentscheidungen im Zusammenhang mit der Organisation von Streaming-E / A hängen von der Hardwareplattform ab, und ihre Berücksichtigung geht über den Rahmen dieses Artikels hinaus.
Diese Bibliothek wurde mit dem Kepware OPC-Server, SIMATIC- und Wientek-Panels sowie anderen ModBus-Mastern in einer Vielzahl von Geräten getestet, die auf den PIC- und STM32-Mikrocontrollern basieren, und zeigte eine Leistung von 142%. Die einfache Portierung dieser Bibliothek erleichtert die Anpassung an andere Arten von 8-16-32-Bit-Mikrocontrollern.