ModBus Slave RTU / ASCII without SMS and registration

image



There are many publicly available libraries for implementing ModBus Slave devices, but they often contain redundant functionality, are difficult to learn and contain gross errors. This article discusses a library, in the humble opinion of the author, devoid of these shortcomings.



The library software is supplied as open source C code.



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;
  }




The modbus.h file contains the required declarations, compilation options, and tuning constants. Let's briefly describe the main options and tuning parameters.



ModBusUseFunc1 - ModBusUseFunc15 - compilation option that determines the use of functions of the ModBus protocol. Practical implementations of ModBus devices work with a limited set of protocol functions, most often, functions 3,6 and 16. There is no need to include extra code in the project.



ModBusID, ModBusID_FF - Addresses on the ModBus bus. This protocol implementation supports two addresses. This can be convenient for commissioning devices, the ModBusID address is the configurable device address, and the ModBusID_FF address is the address for customizing the device.



ModBusMaxPause- The pause between characters, to determine the beginning of the packet, is set in the ModBusSysTimer quanta. Typically, the ModBusSysTimer quantum is 1ms. For most applications, compliance with the timeouts described in the protocol standard is simply impossible.



For example, ModBus Master running on a Win-machine will never be able to provide the timeouts required by the protocol. Therefore, setting a time slice less than 1 mS may be considered impractical. Practical observations show that the ModBusMaxPause value should be of the order of 5-10 mS.



ModBusMaxPauseResp - Pause between Master's request and Slave's response. Many ModBus Master devices have a delay in switching from transmission to reception, this delay can be compensated for by this constant.



ModBusMaxInBit, ModBusMaxOutBit, ModBusMaxInReg, ModBusMaxOutReg- The number of discrete inputs, outputs, registers for reading, registers for reading / writing. The program reserves memory for ModBus registers. If a certain type of register is not used, the value must be specified as zero.



ModBusMaxInBitTX, ModBusMaxOutBitTX, ModBusMaxInRegTX, ModBusMaxOutRegTX - The maximum number of discrete inputs, outputs, registers for reading, registers for reading / writing output registers in the transmitted packet. This setting must match the corresponding setting on the ModBus Master.



To port the library to any platform, you must specify three functions through macros.



ModBusSysTimer- System timer, a variable that is incremented every millisecond in a separate thread of execution. This variable can be uwTick, from the STM32 HAL library, or the standard C function clock () .



void ModBusPUT (unsigned char A) - Writing a byte to a serial stream.



unsigned short ModBusGET (void) - Read a byte from a serial stream. If there is no data in the serial stream, the function returns 0, if there is data, then the return value is the high byte 0x01, the low byte is the read data.



To use the library, you need to fill in the body of the Prg2ModBusOutBit (), Prg2ModBusInBit (), Prg2ModBusOutReg (), Prg2ModBusInReg () functionsresponsible for copying user variables to ModBus registers. Also, it is necessary to fill in the body of the



ModBus2PrgOutBit (), ModBus2PrgOutReg () functions , which are responsible for copying the ModBus registers into user variables. In the body of these functions, you can perform some actions related to changing registers, for example, checking for valid values.



For instance:



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;
  }


It is allowed not to fill in the body of the specified functions, but to work with registers directly, while using the ModBusUseGlobal option .



To initialize a ModBus device, call the ModBusIni () function . The ModBusRTU () or ModBusASCII () functions provide the device operation using the RTU and ASCII protocols, respectively. They must be called in the main loop of the program:



ModBusIni();
while(!0)
  {
  if(ModBusTip==RTU) ModBusRTU(); else ModBusASCII();
  }


Do not forget that before initializing and calling the function that ensures the operation of the ModBus device, you need to take care of initializing the serial stream (UART). Software decisions related to the organization of streaming I / O depend on the hardware platform, and their consideration is beyond the scope of this article.



This library was tested with Kepware OPC server, SIMATIC and Wientek panels, as well as other ModBus Masters, in a variety of devices based on PIC and STM32 microcontrollers, and showed its 142% performance. The ease of porting this library will make it easy to adapt it to other types of 8-16-32-bit microcontrollers.



All Articles