CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Interrupt driven data streams on USART with MODBUS packets
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

Interrupt driven data streams on USART with MODBUS packets
PostPosted: Thu May 13, 2004 2:23 pm     Reply with quote

Comments and suggestions are welcome.

Code part 1 Updated per bug reports

Code:
// MODBUS exception codes
#define Illegal_Function     1
#define Illegal_Data_Address 2
#define Illegal_Data_Value   3
#define Slave_Device_Failure 4
#define Acknowledge          5
#define slave_device_Busy    6
#define Negative_Acknoledge  7
#define Memory_Parity_Error  8

/* Table of CRC values for high order byte */
char Table_CRC_Hi[256] = {                                  // A global stored in RAM is faster than a constant by 50%
//const char Table_CRC_Hi[256] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
} ;                                                         // If RAM is used a #locate is recomended to place table in a single bank of ram

/* Table of CRC values for low order byte */
char Table_CRC_Lo[256] = {                                  // A global stored in RAM is faster than a constant by 50%
//const char Table_CRC_Lo[256] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
};                                                          // If RAM is used a #locate is recomended to place table in a single bank of ram

#use rs232(baud=19200,xmit=pin_c6,rcv=pin_c7,errors,stream=COMM_1)

struct COMM_Port
{  Int8  Network_Address;
   Int8  CRClo_Index;
   Int8  CRChi_Index;
   int8  CRC_Lo;
   int8  CRC_Hi;
   Int8  Index;
   Int8  Table_Index;
   Int8  Start;
   Int8  x;
   Int8  y;
   Int8  Framming_Errors;
   Int8  Finish;
   Int16 Three_Half_Byte_Periods;
   Int8  Quantity;
   Int8  Baud;
   Int8  Parity;
   Int1  Respond;
   Int1  Initialized;
   Int1  Packet;
   Int1  Baud_Lock;
};
struct COMM_Port COMM1;
#locate COMM1 = 0x0F00

Int8  COMM1_Buffer[256];                                    // Accessing an array that is part of a structure is
#locate COMM1_Buffer = 0x0100                               // Not handled in an efficient manner by the compiler

Int16 Registry_Map[128];
#locate Registry_Map = 0x200
Int8  REG_Map[256];
#locate REG_Map = Registry_Map

Int16 Recieved_Packets;                                     // Accessed in holding register 400001 via MODBUS
#locate Recieved_Packets = Registry_Map

Int16 Reply_Packets;                                        // Accessed in holding register 400002 via MODBUS
#locate Reply_Packets = Registry_Map + 2

/***********************************************************
*    COMM1 Receive Complete timer Interrupt                *
***********************************************************/
#int_TIMER1
TIMER1_isr()
{  disable_interrupts(int_TIMER1);                          // TMR1 Overflow Interrupt Enable bit off
   COMM1.Index--;                                           // Index last byte recieved
   COMM1.CRChi_Index = COMM1.Index;                         // Index last byte recieved
   COMM1.Packet=1;                                          // Tag packet for processing
   ++Recieved_Packets;                                      // Recieved packet count (for debugging)
}
/***********************************************************
*    COMM1 Receive Interrupt                               *
***********************************************************/
#int_RDA                                                    //**************
RDA_isr()                                                   // BYTE RECIEVED
{  COMM1.x = fgetC(COMM_1);                                 // Get incomming byte from buffer
   if(!COMM1.Packet)                                        // Don't recieved while working on a packet or transmitting
   {  if(bit_test(RS232_ERRORS,2))                          // Found Framing Errors
      {  ++COMM1.Framming_Errors;
         if(COMM1.Framming_Errors & 8)
         {  if(++COMM1.Baud & 8)
               COMM1.Baud=0;
/**********************    These bauds not available W/4Mhz Xtal
            if(COMM1.Baud==0)
            {  set_uart_speed(57600,COMM_1);
               if(((16.5*Xtal_Freq)/57600)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*(INT32)Xtal_Freq)/57600);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
            if(COMM1.Baud==1)
            {  set_uart_speed(38400,COMM_1);
               if(((16.5*Xtal_Freq)/38400)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*(INT32)Xtal_Freq)/38400);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
            if(COMM1.Baud==2)
            {  set_uart_speed(28800,COMM_1);
               if(((16.5*Xtal_Freq)/28800)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*(INT32)Xtal_Freq)/28800);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
*/
            if(COMM1.Baud==3)
            {  set_uart_speed(19200,COMM_1);
               if(((16.5*Xtal_Freq)/19200)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*Xtal_Freq)/19200);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
            if(COMM1.Baud==4)
            {  set_uart_speed(9600,COMM_1);
               if(((16.5*Xtal_Freq)/9600)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*Xtal_Freq)/9600);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
            if(COMM1.Baud==5)
            {  set_uart_speed(4800,COMM_1);
               if(((16.5*Xtal_Freq)/4800)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*Xtal_Freq)/4800);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
            if(COMM1.Baud==6)
            {  set_uart_speed(2400,COMM_1);
               if(((16.5*Xtal_Freq)/2400)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*Xtal_Freq)/2400);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
            if(COMM1.Baud==7)
            {  set_uart_speed(1200,COMM_1);
               if(((16.5*Xtal_Freq)/1200)<65536)
               {  COMM1.Three_Half_Byte_Periods=65536-((18*Xtal_Freq)/1200);
               }
               else
               {  COMM1.Three_Half_Byte_Periods=0;
               }
            }
         }
      }
      else
      {  COMM1_Buffer[COMM1.Index] = COMM1.x;               // Place incomming byte in PacketBuffer
         COMM1.Index++;                                     // Place incomming byte in PacketBuffer
         set_timer1(COMM1.Three_Half_Byte_Periods);         // Wait 1.5 byte periods then interupt (set for 9600bps now)
         clear_interrupt(int_TIMER1);                       // Clear timer1 overflow Interrupt Flag bit
         enable_interrupts(int_TIMER1);                     // TMR1 Overflow Interrupt Enable bit on
      }
   }
}
/***********************************************************
*    COMM1 Transmit Interrupt                              *
***********************************************************/
#int_TBE                                                    //**************
TBE_isr()                                                   // BYTE TRANSIMITED
{  if(COMM1.Index <= COMM1.CRChi_Index)                     // Transmit until the entire packet has been sent
   {  COMM1.x = COMM1_Buffer[COMM1.Index];                  // Store the byte to be sent in a directly addressable location
      fputC(COMM1.x,COMM_1);                                // Start the byte transmition
      COMM1.Index++;                                        // Index the next byte to be sent
   }
   else
   {  disable_interrupts(INT_TBE);                          // Stop transmittion
      COMM1.Index = 0;                                      // Index for first byte to be recieved
   }
}


Last edited by Neutone on Thu Jun 29, 2006 11:22 am; edited 2 times in total
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Thu May 13, 2004 2:24 pm     Reply with quote

Code part 2 Updated per bug reports
Code:
/***********************************************************
*    COMM1 Main Service                                    *
***********************************************************/
#inline
void COMM1_Service(void)
{  if(!COMM1.Initialized)
   {  setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);               // Timer used by COMM1
      enable_interrupts(INT_RDA);                           // Enable recieved bytes interupt
      enable_interrupts(global);                            // Enable recieved bytes interupt
      COMM1.Index = 0;                                      // Index for first byte to be recieved
      COMM1.Packet = 0;                                     // Allow new packet reception to begin
      COMM1.Initialized=1;
      COMM1.Network_Address=1;
      COMM1.Baud=0;
      COMM1.Three_Half_Byte_Periods=65536-((18*Xtal_Freq)/19200);
   }
   if(COMM1.Packet)                                         // Packet ready to process
   {  COMM1.CRClo_Index=COMM1.CRChi_Index-1;                // Solve this index once instead of once per byte
      COMM1.Index = 0;                                      // Start at begining of packet
      COMM1.CRC_Lo = COMM1.CRC_Hi = 0xFF;                   // Prepare to CRC
      while(COMM1.Index < COMM1.CRClo_Index)                // Use all bytes prior to CRClo_Index
      {  COMM1.Table_Index = COMM1_Buffer[COMM1.Index];     // Generate CRC
         COMM1.Table_Index ^= COMM1.CRC_Lo;                 // Generate CRC
         COMM1.CRC_Lo = Table_CRC_Hi[COMM1.Table_Index];    // Generate CRC
         COMM1.CRC_Lo ^= COMM1.CRC_Hi;                      // Generate CRC
         COMM1.CRC_Hi = Table_CRC_Lo[COMM1.Table_Index];    // Generate CRC
         COMM1.Index++;
      }
      COMM1.Index = 0;                                      // Zero index for Transmition
      if(COMM1.CRC_Lo != COMM1_Buffer[COMM1.CRClo_Index])   // Detect Bad CRClo
      {  COMM1.Packet = 0;                                  // Allow new packet reception to begin
      }
      if(COMM1.CRC_Hi != COMM1_Buffer[COMM1.CRChi_Index])   // Detect Bad CRChi
      {  COMM1.Packet = 0;                                  // Allow new packet reception to begin
      }
      COMM1.Index = 0;                                      // Zero index for recieving
      if(COMM1.Packet) COMM1.Framming_Errors=0;             // Zero Framing errors on recieving good packet
      if(COMM1_Buffer[0]==COMM1.Network_Address)            // This device is directly addressed
      {  COMM1.Respond=COMM1.Packet;                        // This packet must be replied to
      }
      else
      {  COMM1.Respond=0;                                   // This packet must not be replied to
         if(COMM1_Buffer[0]!=0)                             // This is not a network brodcast on address 0
         {  if(COMM1_Buffer[0]!=255)                        // This is not a network brodcast on address 255
            {  COMM1.Packet = 0;                            // Allow new packet reception to begin
            }
         }
      }
      if(COMM1.Packet)                                      // Decode by modbus functions
      {  COMM1.CRChi_Index=0;                               // Prepair to test for invalid functions
         if(COMM1_Buffer[1] == 3)
         {  COMM1.start=COMM1_Buffer[3];                    // Starting register address
            COMM1.Quantity=COMM1_Buffer[5];                 // Quantity of registers to read
            if(COMM1_Buffer[2])  COMM1.Start=256;           // Invalid Address Range
            if(COMM1_Buffer[4])  COMM1.Start=256;           // Invalid Address Range
            COMM1.finish=COMM1.start+COMM1.quantity;        // Ending register address
            COMM1_Buffer[2]=COMM1.Quantity<<1;              // Data Byte Count
            if(COMM1.finish<=128)                           // Access to first 128 words
            {  COMM1.Index=3;                               // Set the index to the first byte
               for(COMM1.y=COMM1.start;COMM1.y<COMM1.finish;COMM1.y++)
               {  COMM1.x=(COMM1.y*2)+1;
                  COMM1_Buffer[COMM1.Index] = REG_Map[COMM1.x];
                  COMM1.Index++;
                  --COMM1.x;
                  COMM1_Buffer[COMM1.Index] = REG_Map[COMM1.x];
                  COMM1.Index++;
               }
               COMM1.CRChi_Index=COMM1.Index+1;
            }
            else
            {  bit_set(COMM1_Buffer[1],7);                  // Report_Exception
               COMM1_Buffer[2]=Illegal_Data_Address;        // Report_Exception type
               COMM1.CRChi_Index=4;                         // Index CRChi
            }
         }
         if(COMM1_Buffer[1] == 6)
         {  COMM1.start=COMM1_Buffer[3];                    // Starting register address
            if(COMM1_Buffer[2])  COMM1.Start=256;           // Invalid Address Range
            if(COMM1.start<=127)
            {  COMM1.Index=COMM1.start*2;
               REG_Map[COMM1.Index]=COMM1_Buffer[5];
               COMM1.Index++;
               REG_Map[COMM1.Index]=COMM1_Buffer[4];
               COMM1.CRChi_Index=7;                         // Index CRChi
            }
            else
            {  bit_set(COMM1_Buffer[1],7);                  // Report_Exception
               COMM1_Buffer[2]=Illegal_Data_Address;        // Report_Exception type
               COMM1.CRChi_Index=4;                         // Index CRChi
            }
         }
         if(COMM1.CRChi_Index == 0)
         {  bit_set(COMM1_Buffer[1],7);                     // Report_Exception
            COMM1_Buffer[2]=Illegal_Function;               // Report_Exception type
            COMM1.CRChi_Index=4;                            // Index CRChi
         }
         if(!COMM1.Respond)
         {  COMM1.Packet = 0;                               // Allow new packet reception to begin
            COMM1.Index = 0;                                // Start at begining of packet
         }
      }
      if(COMM1.Respond)                                     // Add CRC to outgoing data
      {  COMM1.CRC_Lo = COMM1.CRC_Hi = 0xFF;                // Prepare to generate CRC
         COMM1.CRClo_Index=COMM1.CRChi_Index-1;             // Solve this index once instead of once per byte
         COMM1.Index = 0;                                   // Start at begining of packet
         while(COMM1.Index < COMM1.CRClo_Index)             // Use all bytes prior to CRClo_Index
         {  COMM1.Table_Index = COMM1_Buffer[COMM1.Index];  // Generate CRC
            COMM1.Table_Index ^= COMM1.CRC_Lo;              // Generate CRC
            COMM1.CRC_Lo = Table_CRC_Hi[COMM1.Table_Index]; // Generate CRC
            COMM1.CRC_Lo ^= COMM1.CRC_Hi;                   // Generate CRC
            COMM1.CRC_Hi = Table_CRC_Lo[COMM1.Table_Index]; // Generate CRC
            COMM1.Index++;
         }
         COMM1.Index = 0;                                   // Zero index for Transmition
         COMM1_Buffer[COMM1.CRClo_Index]=COMM1.CRC_Lo;      // Place CRC_Lo within packet
         COMM1_Buffer[COMM1.CRChi_Index]=COMM1.CRC_Hi;      // Place CRC_Hi within packet
         enable_interrupts(INT_TBE);                        // Kick Off Xmit of Data
         Reply_Packets++;                                   // Reply packet count (for debugging)
      }
      COMM1.Packet = 0;                                     // Allow new packet reception to begin
   }
}


Last edited by Neutone on Tue Jan 16, 2007 11:04 am; edited 5 times in total
yerpa



Joined: 19 Feb 2004
Posts: 58
Location: Wisconsin

View user's profile Send private message Visit poster's website

Modbus program
PostPosted: Tue Sep 14, 2004 10:01 am     Reply with quote

Hello,
I am wondering which PIC chip this program is designed for, and which version of the CCS compiler is used. I get compiler errors "subscript out of range" when using PCM 2.679 ( I know it is old but it worked for me so far). Also, I noticed that you replaced the CRC computations with table lookups - is there a good way to verify these tables?

Hope this isn't too many questions, and thanks for contributing the code.

yerpa

http://www.reprolabs.com
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

Re: Modbus program
PostPosted: Tue Sep 21, 2004 10:45 am     Reply with quote

yerpa wrote:
Hello,
I am wondering which PIC chip this program is designed for, and which version of the CCS compiler is used. I get compiler errors "subscript out of range" when using PCM 2.679 ( I know it is old but it worked for me so far). Also, I noticed that you replaced the CRC computations with table lookups - is there a good way to verify these tables?

Hope this isn't too many questions, and thanks for contributing the code.

yerpa

http://www.reprolabs.com


I wrote this for the PIC18 series of chips. I would guess I have used syntax that only works with compiler version 3.191 or newer. The basic flow of the code should be portable to a PIC16 series chip but I can't even guess how much code space it would take. I'm sure it would be bigger in a PIC16. The CRC computation with bit shifts runs at a quarter of the speed of a RAM based lookup table. With all the extra RAM and ROM in the PIC18 series it just makes sence to switch to lookup tables. As for verifying the tables I have used several MODBUS host tools that require a valid CRC and this code creates one. I'm not sure declaring all the variables in a structure was a good idea. That was really a bit of an experiment to see if it would work. It did work but did not improve performance any. Maybe the code is more readable. Also for chips with 2 USARTS this code concept can handel two independent slave ports at the same time.
balony-fish



Joined: 19 Oct 2004
Posts: 1

View user's profile Send private message

PostPosted: Tue Oct 19, 2004 5:57 am     Reply with quote

could someone please just post a quick run through of how to use this modbus library to configure a basic slave device on a modbus network? For example, I would like to set up an 18Fxxx pic to basically recieve some command packet from a master, asking for some response, and then the PIC to respond with some data value.

I have brought this library into my project, but im baffled with regards on where to go next. Question

any help would be *greatly* appreciated!

the balony fish
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue Oct 19, 2004 7:58 am     Reply with quote

balony-fish wrote:
could someone please just post a quick run through of how to use this modbus library to configure a basic slave device on a modbus network? For example, I would like to set up an 18Fxxx pic to basically recieve some command packet from a master, asking for some response, and then the PIC to respond with some data value.

I have brought this library into my project, but im baffled with regards on where to go next. Question

any help would be *greatly* appreciated!

the balony fish


The posted code only supports function codes 3 and 6. That is read 16-bit registers and write a single 16-bit register. If you define one of the registers to control your program your mostly there. A good test of your program would be to have a function that reads an analog input and stores the result in Registry_Map[1] and then poll from the master to read register 40001 and you should be reading the analog value. If you only call the COMM1_Service function from a while loop you should be able to estabilish comunications.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Tue Oct 19, 2004 8:20 am     Reply with quote

You didn't post the timeout routine you used for timer1.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue Oct 19, 2004 11:44 am     Reply with quote

Mark wrote:
You didn't post the timeout routine you used for timer1.

I may not have explained very well but it is there.
Code:
/***********************************************************
*    COMM1 Receive Complete timer Interrupt                *
***********************************************************/
#int_TIMER1
TIMER1_isr()
{  disable_interrupts(int_TIMER1);                          // TMR1 Overflow Interrupt Enable bit off
   Comm1.Index--;                                           // Index last byte recieved
   Comm1.CRChi_Index = Comm1.Index;                         // Index last byte recieved
   Comm1.Packet=1;                                          // Tag packet for processing
   ++Recieved_Packets;                                      // Recieved packet count (for debugging)
}


One thing that is missing is a means to delay the reply. On a multidrop RS485 network it takes some time for the hardware to change from the master transmitting to the slave replying. As written this code works fine with a simple RS232 connection. I used this code on a PICDEM2 without any problems.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Tue Oct 19, 2004 1:24 pm     Reply with quote

I like to use the transmit to transmit "dummy" char (the delay in char times). Once transmitted, the transceiver can be enabled and the real data transmitted. This works well for waiting for ACKs from a device.
kypec



Joined: 20 Sep 2003
Posts: 54

View user's profile Send private message

Modified MODBUS routines = MASTER + multiple SLAVES
PostPosted: Thu Sep 15, 2005 1:53 am     Reply with quote

Hello everybody,

I'd like to share some of my routines which are based upon Neutone's ideas.
We use this code in our welding machines successfully for more than 2 years now.
The protocol is built like this: Firstly the MASTER scans the bus to find out what SLAVE(S) is/are present in the system. Its behaviour (commands issued to particular SLAVES) depends on the results of this scanning process. The main loops in all members must be designed so that they take 1 msec because all bus timings (end of frame recognition) rely on that. The advantage is that no other timers are needed for the communication.
The code will run on PIC16F877@20MHz or PIC18F452 and it's been compiled with PCM or PCH 3.190
Well, let's start with MASTER main part:
Code:

#define MACHINE_NAME_X 'P'  //first ASCII char
#define MACHINE_NAME_Y 'D'  //second ASCII char
#define UPDATE_RS485_DIR output_b(LATB); //necessary for PIC16F* device
#define TIMER0_MODE (RTCC_INTERNAL|RTCC_DIV_32)
#define MAIN_LOOP_TIME -156 //TMR0 reload constant for 1,0 msec

#include "modbus_master.c"
#include "mb_protocol.h"

int1 scanning_bus=TRUE; //TRUE when RS485 bus is being scanned for connected members
int1 messages_done=FALSE; //TRUE when message flow counter for RS485 bus must be set back to zero
int8 counter_modbus=0; //RS485 communicaton flow control
int8 local_a_member=0; //non-zero value means address of connected local bus member
int8 local_b_member=0; //non-zero value means address of connected local bus member
int8 remote_member=0; //non-zero value means address of connected remote bus member
int8 update_flags=0;
#bit update_common=update_flags.0 //common parameters were updated
#bit update_norm=update_flags.1 //normal phase parameters were updated
#bit update_hs=update_flags.2 //hot-start phase parameters were updated
#bit update_cf=update_flags.3 //crater-fill phase parameters were updated

////////////////////////////////////////////////////////////////////////////////
//RS485 bus management
////////////////////////////////////////////////////////////////////////////////
void request_fast(int8 recipient) {
   if (counter_modbus&1) { //writing out on every odd pass
      if (!mode_mma) {
         modbus_write_single(0,1,make16(now_flags,error_flags)); //broadcast important flags
      }
      else { //MMA mode: now_flags must be presented as zero to other members
         modbus_write_single(0,1,make16(0,error_flags)); //broadcast important flags with fake now_flags=0
      }
   }
   else { //reading in on every even pass
      modbus_read(recipient,0,1); //ask for important flags
   }
   update_flags=0; //clear flags in order to signal no particular block of parameters gets updated
}

void request_only_local(int8 local_recipient) {
   switch (counter_modbus) {
      //unicasted messages
      case 2:
         modbus_read(local_recipient,8,11); //ask for common parameters
         update_common=TRUE;
         break;
      case 5:
         modbus_read(local_recipient,19,7); //ask for hot-start phase parameters
         update_hs=TRUE;
         break;
      case 8:
         modbus_read(local_recipient,26,7); //ask for normal phase parameters

         update_norm=TRUE;
         break;
      case 11:
         modbus_read(local_recipient,33,7); //ask for crater-fill parameters
         update_cf=TRUE;
         break;
      //broadcasted messages
      case 14:
         other.v_real=voltage_to_real(average_voltage);
         other.i_real=current_to_real(average_current);
         modbus_write_multiple(0,40,2); //send measured voltage and current
         break;
      /*case 17:
         other.t1_real=temperature_to_real(temperature1);
         other.t2_real=temperature_to_real(temperature2);
         other.t3_real=temperature_to_real(temperature3);
         other.t4_real=temperature_to_real(temperature4);
         other.t5_real=temperature_to_real(temperature5);
         other.t6_real=temperature_to_real(temperature6);
         modbus_write_multiple(0,42,3); //send measured temperatures
         break;*/
      case 17:
         modbus_write_multiple(0,45,3); //send license data
         messages_done=TRUE; //indicates the very last message in this round
         break;
      default:
         request_fast(local_recipient);
   }
}

int8 choose_next_member(int8 recent) {
   switch (recent) { //try next bus member
      case MD_BUS0:
         return MD_BUS1;
      case MD_BUS1:
         return MD_BUS2;
      case MD_BUS2:
         return RC_BUS0;
      case RC_BUS0:
         return RC_BUS1;
      case RC_BUS1:
         return RC_BUS2;
      case RC_BUS2:
         return 0; //all possibilities were tried
      default:
         return MD_BUS0;
   }
}

////////////////////////////////////////////////////////////////////////////////
//main function
////////////////////////////////////////////////////////////////////////////////
void main(void) {
   setup_timer_0(TIMER0_MODE);
   modbus_init();
   mb.respond=TRUE; //the first member will be picked from a list
   enable_interrupts(GLOBAL);
   while (TRUE) { //main loop
      set_timer0(MAIN_LOOP_TIME); //re-init TMR0
      TMR0IF=FALSE;
      //RS485 communication
      modbus_master();
      if (mb.idle) { //new action can be taken
         if (scanning_bus) { //looking for all connected members
            if (mb.timeout) { //last expected reply has not arrived
               if (mb.retry>4) mb.respond=TRUE; //this member is not connected -> pick another one
               else mb.retry++; //give the same member another try
            }
            if (mb.updated) { //recently addressed member replied properly
               if (!local_a_member) local_a_member=mb.member; //save the first found member's address
               else { //distinct approach must be taken
                  if (mb.member<=MD_BUS2) local_b_member=mb.member; //save the second found member's address
                  else remote_member=mb.member; //save the second found member's address
               }
               mb.respond=TRUE; //this member is connected -> pick another one
            }
            if (mb.respond) {
               mb.member=choose_next_member(mb.member); //pick another member from the list
               mb.respond=FALSE;
               mb.retry=0; //give a new member fresh try
            }
            if (!mb.member) {
               scanning_bus=FALSE; //cease scanning once all listed members have been inquired
               if (!local_a_member) reset_cpu(); //no connected member was found
            }
            else modbus_read(mb.member,1,1); //inquire a member
         }
         else { //inquire single or multiple members
            if (mb.timeout) { //last expected reply has not arrived
               if (mb.retry>50) reset_cpu(); //too many missed replies
               else mb.retry++; //try it once more
            }
            else { //member replied properly or it was a broadcast message sent recently
               if (mb.updated) { //member replied with some data
                  if (mode_flags!=make8(mb_area[0],0)) { //operation mode has changed
                     mode_flags=make8(mb_area[0],0);
                     now_flags=0; //avoid unintended transition effects
                  }
                  trigger_flags=make8(mb_area[0],1);
               }
               //limit message flow by number of connected members
               if (messages_done) {
                  counter_modbus=0; //begins a new round of messages
                  messages_done=FALSE;
               }
               else {
                  counter_modbus++; //proceed with next message in this round
               }
               mb.retry=0; //zero counter of missed replies
            }
            request_only_local(local_a_member); //single feeder configuration
         }
      }
      while (!TMR0IF); //wait until TMR0 overflows
   }
}

Here comes the included file modbus_master.c ...
Code:

#include "modbus_common.c"

//"Read Holding Registers"
void modbus_read(int8 recipient,int8 first_reg,int8 count_reg) {
   mb_buffer[0]=recipient; //slave address
   mb.exception=recipient; //remember which slave should reply
   mb_buffer[1]=FUNCTION_RHR; //function code
   mb_buffer[2]=first_reg; //starting reg. address
   mb_buffer[3]=count_reg; //quantity of registers
   mb.crc_hi_index=5;
   modbus_send_packet();
   mb.start=first_reg; //remember which regs. is to be read
   mb.quantity=count_reg*2; //remember how many bytes is to be read
   mb.finish=TIME35+3+count_reg; //timeout will be roughly the length of a response @ 19200 baud
   mb.idle=FALSE;
}

//"Write Single Register"
void modbus_write_single(int8 recipient,int8 reg_address,int16 reg_value) {
   mb_buffer[0]=recipient; //slave address
   mb.exception=recipient; //remember which slave should reply
   mb_buffer[1]=FUNCTION_WSR; //function code
   mb_buffer[2]=reg_address; //reg. address
   mb_buffer[3]=make8(reg_value,0); //reg. value LSB
   mb_buffer[4]=make8(reg_value,1); //reg. value MSB
   mb.crc_hi_index=6;
   modbus_send_packet();
   mb.finish=TIME35+4; //timeout will be roughly the length of a response @ 19200 baud
   mb.idle=FALSE;
}

//"Write Multiple Registers"
void modbus_write_multiple(int8 recipient,int8 first_reg,int8 count_reg) {
   mb_buffer[0]=recipient; //slave address
   mb.exception=recipient; //remember which slave should reply
   mb_buffer[1]=FUNCTION_WMR; //function code
   mb_buffer[2]=first_reg; //starting reg. address
   mb_buffer[3]=count_reg; //quantity of registers
   mb.index=4; //set the index to the first data byte
   while (count_reg>0) {
      mb_buffer[mb.index++]=make8(mb_area[first_reg],0); //put reg. value LSB
      mb_buffer[mb.index++]=make8(mb_area[first_reg],1); //put reg. value MSB
      first_reg++; //pointing to next holding reg.
      count_reg--; //one holding reg. less to put
   }
   mb.crc_hi_index=mb.index+1;
   modbus_send_packet();
   mb.finish=TIME35+4; //timeout will be roughly the length of a response @ 19200 baud
   mb.idle=FALSE;
}

////////////////////////////////////////////////////////////////////////////////
//frame processing
////////////////////////////////////////////////////////////////////////////////
void modbus_master(void) {
   if (!RS485_DIR) { //RS485 configured for RX
      if (mb.listen) { //waiting for a reply
         if (mb.received && mb.timer>=TIME35) { //inter-frame timeout with at least one byte received
            mb.busy=TRUE; //stop further reception until this packet is processed
            if (mb_buffer[0]==mb.exception) { //response from expected slave
               mb.crc_hi_index=--mb.index; //index the last byte received as CRC high order byte
               generate_crc();
               if (mb.crc_lo==mb_buffer[mb.crc_lo_index] && mb.crc_hi==mb_buffer[mb.crc_hi_index]) { //CRC alright
                  if (mb_buffer[1]==FUNCTION_RHR) { //"Read Holding Registers" function code
                     if (mb.quantity==mb_buffer[2]) { //proper number of bytes arrived
                        mb.quantity/=2; //number of holding regs. is half a number of bytes
                        mb.index=3; //set the index to the first data byte
                        while (mb.quantity>0) { //process all data bytes
                           mb_area[mb.start++]=make16(mb_buffer[mb.index+1],mb_buffer[mb.index]); //put it in holding reg.
                           mb.index+=2; //index the next holding reg.
                           mb.quantity--; //one holding reg. was processed
                        }
                        mb.updated=TRUE;
                     }
                  }
               }
               //else { //received CRC does not match with generated one
               //}
            }
            //else { //response from unexpected slave
            //}
            modbus_restart_rx();
            mb.idle=TRUE; //another request may be issued
         }
         else if (mb.timer>mb.finish) { //response timeout expired
            mb.timeout=TRUE;
            mb.idle=TRUE; //another request may be issued
         }
         else mb.timer++; //keep on response timeout counting
      }
      else { //no reply is expected
         if (mb.timer>TIME35) mb.idle=TRUE; //another request may be issued for turnaround delay expired
         else mb.timer++; //keep on turnaround delay counting
      }
   }
   else { //RS485 configured for TX
      mb.timer=0; //reset timeout counter
      mb.timeout=FALSE; //reset timeout flag
      if (TRMT && !mb.sending) { //UART transmit register is empty and the last byte was sent already
         if (mb_buffer[0]!=0) mb.listen=TRUE; //unicast message therefore expect a reply
         else mb.listen=FALSE; //broadcast message therefore no reply is expected
         mb.updated=FALSE; //no holding register was updated yet
         modbus_restart_rx();
         modbus_direction_rx(); //only listen to the bus traffic
      }
   }
}

and its companions modbus_common.c
Code:

#use rs232(BAUD=19200,XMIT=PIN_C6,RCV=PIN_C7)
#define TIME35 2 //provided that one loop takes 1 msec

#include "modbus_area.c"

#define BUS1_MASK 0x01
#define BUS2_MASK 0x02
#define MD_BUS0 0x10
#define MD_BUS1 (MD_BUS0|BUS1_MASK)
#define MD_BUS2 (MD_BUS0|BUS2_MASK)
#define RC_BUS0 0x20
#define RC_BUS1 (RC_BUS0|BUS1_MASK)
#define RC_BUS2 (RC_BUS0|BUS2_MASK)
#define ID_BUS0 0x40
#define ID_BUS1 (ID_BUS0|BUS1_MASK)
#define ID_BUS2 (ID_BUS0|BUS2_MASK)

#if (MACHINE_NAME_X=='P' || MACHINE_NAME_X=='G' || MACHINE_NAME_X=='X') && MACHINE_NAME_Y=='D'
 #define MEMBER_NUMBER 0x00 //PD/GD/XD module alias Power Source Unit = MODBUS MASTER
#elif MACHINE_NAME_X=='M' && MACHINE_NAME_Y=='D'
 #define MEMBER_NUMBER MD_BUS0 //MD module alias Wire Feeder Unit = MODBUS SLAVE
#elif MACHINE_NAME_X=='R' && MACHINE_NAME_Y=='C'
 #define MEMBER_NUMBER RC_BUS0 //RC module alias Remote Control Unit = MODBUS SLAVE
#elif MACHINE_NAME_X=='I' && MACHINE_NAME_Y=='D'
 #define MEMBER_NUMBER ID_BUS0 //ID module alias Robot Interface Unit = MODBUS SLAVE
#else #error Unknown or unsupported hardware unit's name defined
#endif

//function codes
#define FUNCTION_RHR 0x03 //"Read Holding Registers"
#define FUNCTION_WSR 0x06 //"Write Single Register"
#define FUNCTION_WMR 0x10 //"Write Multiple Registers"
//exception codes
#define ILLEGAL_FUNCTION 1
#define ILLEGAL_DATA_ADDRESS 2
//table of CRC pre-calculated values for high order byte
const int8 table_crc_hi[256]={
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40
};
//table of CRC pre-calculated values for low order byte
const int8 table_crc_lo[256]={
0x00,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,0xC6,0x06,0x07,0xC7,0x05,0xC5,0xC4,0x04,
0xCC,0x0C,0x0D,0xCD,0x0F,0xCF,0xCE,0x0E,0x0A,0xCA,0xCB,0x0B,0xC9,0x09,0x08,0xC8,
0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC,
0x14,0xD4,0xD5,0x15,0xD7,0x17,0x16,0xD6,0xD2,0x12,0x13,0xD3,0x11,0xD1,0xD0,0x10,
0xF0,0x30,0x31,0xF1,0x33,0xF3,0xF2,0x32,0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4,
0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,0xFA,0x3A,0x3B,0xFB,0x39,0xF9,0xF8,0x38,
0x28,0xE8,0xE9,0x29,0xEB,0x2B,0x2A,0xEA,0xEE,0x2E,0x2F,0xEF,0x2D,0xED,0xEC,0x2C,
0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,
0xA0,0x60,0x61,0xA1,0x63,0xA3,0xA2,0x62,0x66,0xA6,0xA7,0x67,0xA5,0x65,0x64,0xA4,
0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,0x6E,0xAE,0xAA,0x6A,0x6B,0xAB,0x69,0xA9,0xA8,0x68,
0x78,0xB8,0xB9,0x79,0xBB,0x7B,0x7A,0xBA,0xBE,0x7E,0x7F,0xBF,0x7D,0xBD,0xBC,0x7C,
0xB4,0x74,0x75,0xB5,0x77,0xB7,0xB6,0x76,0x72,0xB2,0xB3,0x73,0xB1,0x71,0x70,0xB0,
0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,
0x9C,0x5C,0x5D,0x9D,0x5F,0x9F,0x9E,0x5E,0x5A,0x9A,0x9B,0x5B,0x99,0x59,0x58,0x98,
0x88,0x48,0x49,0x89,0x4B,0x8B,0x8A,0x4A,0x4E,0x8E,0x8F,0x4F,0x8D,0x4D,0x4C,0x8C,
0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40
};

struct modbus {
   int8 member; //master: which slave is being addressed, slave: its own address
   int8 index; //indexing through buffer during transfers, CRC generation, frame processing
   int8 table_index; //indexing through CRC look-up tables
   int8 crc_lo_index; //one less than crc_hi_index
   int8 crc_hi_index; //index of the very last byte being transferred or processed
   int8 crc_lo; //CRC generated of the frame
   int8 crc_hi;
   int8 start; //starting reg. address
   int8 quantity; //quantity of registers (each reg. is 16-bit)
   int8 finish; //ending reg. address
   int8 in; //temporary storage during transfers and processing
   int8 out;
   int8 exception; //slave: exception code in case of invalid request
   int8 timer; //time elapsed since last byte reception
   int8 retry; //master: number of retries for single request
   int1 busy; //TRUE when message frame is being processed
   int1 received; //TRUE when at least one byte has been received
   int1 updated; //TRUE when holding registers were updated with new values
   int1 listen; //master: TRUE when response from slave is expected
   int1 idle; //master: TRUE when new request can be sent
   int1 timeout; //master: TRUE when no proper response arrived in time
   int1 respond; //slave: TRUE when response is required, master: TRUE when proper response has been received
   int1 sending; //TRUE when just transmitting any byte
} mb={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,FALSE,FALSE};

////////////////////////////////////////////////////////////////////////////////
//UART receive interrupt
////////////////////////////////////////////////////////////////////////////////
#int_rda
void uart_rx_handler(void) {
   mb.in = getc(); //get incoming byte from buffer
   if (!mb.busy && !RS485_DIR) { //do not receive while working on a packet or transmitting
      mb_buffer[mb.index++]=mb.in; //place incoming byte in buffer and index the next one
      mb.received=TRUE; //set flag once a byte was received
      if (mb.index>=sizeof(mb_buffer)) mb.index=sizeof(mb_buffer)-1; //prevents from writing off the buffer
   }
   mb.timer=0; //prevents from timeout which causes the frame processing
}

////////////////////////////////////////////////////////////////////////////////
//UART transmit interrupt
////////////////////////////////////////////////////////////////////////////////
#int_tbe
void uart_tx_handler(void) {
   if (mb.index<=mb.crc_hi_index) { //transmit until the entire packet has been sent
      mb.out=mb_buffer[mb.index++]; //prepare the byte to be sent
      putc(mb.out); //start the byte transmission
   }
   else { //stop transmission
      disable_interrupts(INT_TBE);
      mb.index=0; //zero index for the first byte to be received
      mb.sending=FALSE; //no more bytes to be sent out
   }
}

////////////////////////////////////////////////////////////////////////////////
//CRC generation
////////////////////////////////////////////////////////////////////////////////
void generate_crc(void) {
   mb.crc_lo=0xFF; //load initial polynomial to generate CRC
   mb.crc_hi=0xFF;
   mb.crc_lo_index=mb.crc_hi_index-1; //solve this index once instead of once per byte
   mb.index=0; //start at the beginning of packet
   while (mb.index<mb.crc_lo_index) { //use all bytes prior to CRC low order byte
      mb.table_index=mb_buffer[mb.index];
      mb.table_index^=mb.crc_lo; //XOR low byte
      mb.crc_lo=table_crc_hi[mb.table_index];
      mb.crc_lo^=mb.crc_hi; //XOR with high byte
      mb.crc_hi=table_crc_lo[mb.table_index];
      mb.index++;
   }
}

////////////////////////////////////////////////////////////////////////////////
//direction control and initialization
////////////////////////////////////////////////////////////////////////////////
void modbus_direction_rx(void) {
   TXEN=0; //disable UART transmitter (TX pin goes to high impedance state)
   RS485_DIR=0; //set direction for reception again
   #if __device__==877
   UPDATE_RS485_DIR
   #endif
}

void modbus_direction_tx(void) {
   RS485_DIR=1; //set direction for transmission
   #if __device__==877
   UPDATE_RS485_DIR
   #endif
   TXEN=1; //enable UART transmitter (TX pin goes to output state)
   enable_interrupts(INT_TBE); //launch transmission
}

void modbus_restart_rx(void) {
   mb.index=0; //zero index for the first byte
   mb.received=FALSE; //flag that no byte has been received yet
   mb.busy=FALSE; //allow new packet reception to begin
   if (OERR) { //overrun occurred and it must be cleared
      CREN=0; //disable receive logic
      CREN=1; //re-enable it again
   }
}

void modbus_init(void) {
   for (mb.index=0;mb.index<sizeof(mb_buffer);mb.index++) mb_buffer[mb.index]=0;
   for (mb.index=0;mb.index<(sizeof(mb_area)/2);mb.index++) mb_area[mb.index]=0;
   modbus_restart_rx();
   modbus_direction_rx(); //only listen to the bus
   enable_interrupts(INT_RDA); //enable UART RX interrupt
}

////////////////////////////////////////////////////////////////////////////////
//self identification
////////////////////////////////////////////////////////////////////////////////
int8 modbus_identify(int1 bus1,int1 bus2) {
   int8 member;
   member=MEMBER_NUMBER; //connected internally
   if (bus1) member|=BUS1_MASK; //connected externally to M-BUS 1
   if (bus2) member|=BUS2_MASK; //connected externally to M-BUS 2
   return member;
}

////////////////////////////////////////////////////////////////////////////////
//MASTER sends request to SLAVE or SLAVE sends response to MASTER
////////////////////////////////////////////////////////////////////////////////
void modbus_send_packet(void) {
   generate_crc();
   mb_buffer[mb.crc_lo_index]=mb.crc_lo; //append CRC to outgoing data
   mb_buffer[mb.crc_hi_index]=mb.crc_hi;
   mb.index=0; //zero index for transmission
   modbus_direction_tx(); //initiate transmission
   //putc(mb_buffer[0]); //initiate transmission
   //TXREG=mb_buffer[0];
   mb.sending=TRUE; //sending has started
   mb.received=FALSE; //prepare for future reception
}

and modbus_area.c
Code:

int16 mb_area[48]; //all the parameters and settings shared between modules reside here
int8 mb_buffer[57]; //array used for bus transfers
#if __device__==252 || __device__==452
   #locate mb_area=0x310
   #locate mb_buffer=0x370
#elif __device__==877 || __device__==1320
   #locate mb_area=0x110
   #locate mb_buffer=0x1B0
#else
   #error Unsupported device selected
#endif


The SLAVE main part must contain something like this:
Code:

#define MACHINE_NAME_X 'M'  //first ASCII char
#define MACHINE_NAME_Y 'D'  //second ASCII char
#define UPDATE_RS485_DIR output_c(LATC);
#define TIMER0_MODE (RTCC_INTERNAL|RTCC_DIV_32)
#define MAIN_LOOP_TIME -156 //TMR0 reload constant for 1,0 msec

#include "modbus_slave.c"
#include "mb_protocol.h"

////////////////////////////////////////////////////////////////////////////////
//main function
////////////////////////////////////////////////////////////////////////////////
void main(void) {
   setup_timer_0(TIMER0_MODE);
   modbus_init();
   //load fixed address of this slave according to jumpers MBUS1 and MBUS2
   mb.member=modbus_identify(MBUS1,MBUS2);
   enable_interrupts(GLOBAL);
   while (TRUE) { //main loop
      set_timer0(MAIN_LOOP_TIME); //re-init TMR0
      TMR0IF=FALSE;
      //RS485 communication
      modbus_slave();
      if (mb.updated) { //something has been written from outside
         error_flags=make8(mb_area[1],0);
         now_flags=make8(mb_area[1],1);
         mb.updated=FALSE;
      }
      mb_area[0]=make16(trigger_flags,mode_flags);
      while(!TMR0IF); //wait until TMR0 overflows
   }
}

Required file modbus_slave.c follows:
Code:

#include "RS485\modbus_common.c"

////////////////////////////////////////////////////////////////////////////////
//frame processing
////////////////////////////////////////////////////////////////////////////////
void modbus_slave(void) {
   if (!RS485_DIR) { //RS485 configured for RX
      if (mb.received && mb.timer>=TIME35) { //inter-frame timeout with at least one byte received
         mb.busy=TRUE; //stop further reception until this packet is processed
         if (mb_buffer[0]==mb.member) { //unicast message to this slave
            mb.respond=TRUE; //response must be sent back
         }
         else if (mb_buffer[0]==0) { //broadcast message
            mb.respond=FALSE; //response must not be sent back
         }
         else {
            modbus_restart_rx(); //discard this message
         }
      }
      else {
         mb.timer++; //keep on timeout counting
      }
      if (mb.busy) { //packet must be processed
         mb.crc_hi_index=--mb.index; //index the last byte received as CRC high order byte
         generate_crc();
         if (mb.crc_lo==mb_buffer[mb.crc_lo_index] && mb.crc_hi==mb_buffer[mb.crc_hi_index]) { //CRC alright
            if (mb_buffer[1]==FUNCTION_RHR) { //"Read Holding Registers" function code
               mb.start=mb_buffer[2]; //reg. starting address
               mb.quantity=mb_buffer[3]; //no. of registers to read
               mb.finish=mb.start+mb.quantity-1; //reg. ending address
               if (mb.start>47 || mb.quantity>25 || mb.finish>47) { //invalid address range
                  mb.exception=ILLEGAL_DATA_ADDRESS;
               }
               else { //process data in buffer
                  mb.exception=0;
                  mb_buffer[2]=mb.quantity*2; //data bytes count
                  mb.index=3; //set the index to the first data byte
                  while (mb.quantity>0) {
                     mb.out=make8(mb_area[mb.start],0); //get LSB from holding reg.
                     mb_buffer[mb.index++]=mb.out; //put it in the message
                     mb.out=make8(mb_area[mb.start],1); //get MSB from holding reg.
                     mb_buffer[mb.index++]=mb.out; //put it in the message
                     mb.start++; //pointing to next holding reg.
                     mb.quantity--; //one holding reg. less to read
                  }
                  mb.crc_hi_index=mb.index+1;
               }
            }
            else if (mb_buffer[1]==FUNCTION_WSR) { //"Write Single Register" function code
               mb.start=mb_buffer[2]; //reg. address
               if (mb.start>47) { //invalid address range
                  mb.exception=ILLEGAL_DATA_ADDRESS;
               }
               else { //process data in buffer
                  mb.exception=0;
                  mb_area[mb.start]=make16(mb_buffer[4],mb_buffer[3]); //store LSB & MSB from the message
                  mb.crc_hi_index=3;
                  mb.updated=TRUE; //signal that some data were just updated in MODBUS area
               }
            }
            else if (mb_buffer[1]==FUNCTION_WMR) { //"Write Multiple Registers" function code
               mb.start=mb_buffer[2]; //reg. starting address
               mb.quantity=mb_buffer[3]; //no. of registers to write
               mb.finish=mb.start+mb.quantity-1; //reg. ending address
               if (mb.start>47 || mb.quantity>25 || mb.finish>47) { //invalid address range
                  mb.exception=ILLEGAL_DATA_ADDRESS;
               }
               else { //process data in buffer
                  mb.exception=0;
                  mb.index=4; //set the index to the first data byte
                  while (mb.quantity>0) {
                     mb.in=mb_buffer[mb.index++]; //get LSB from message
                     mb.out=mb_buffer[mb.index++]; //get MSB from message
                     mb_area[mb.start]=make16(mb.out,mb.in); //put LSB & MSB in holding reg.
                     mb.start++; //pointing to next holding reg.
                     mb.quantity--; //one holding reg. less to write
                  }
                  mb.crc_hi_index=3;
                  mb.updated=TRUE; //signal that some data were just updated in MODBUS area
               }
            }
            else { //unsupported function code
               mb.exception=ILLEGAL_FUNCTION;
            }
            if (mb.exception) { //build error response
               bit_set(mb_buffer[1],7); //function code OR'ed with 0x80
               mb_buffer[2]=mb.exception; //exception type
               mb.crc_hi_index=4;
            }
            if (mb.respond) { //send a reply
               modbus_send_packet();
            }
            else { //do not send a reply
               modbus_restart_rx(); //wait for the next message
            }
         }
         else { //received CRC does not match with generated one
            modbus_restart_rx();
         }
      }
   }
   else { //RS485 configured for TX
      mb.timer=0; //reset timeout counter
      if (TRMT && !mb.sending) { //UART transmit register is empty and the last byte was sent already
         mb.updated=FALSE; //no holding register was updated yet
         modbus_restart_rx();
         modbus_direction_rx(); //only listen to the bus
      }
   }
}

If you wonder for what previously mentioned mb_protocol.h is, here comes the answer. It allows us to access the MODBUS array of 16-bit holding registers in structured manner:
Code:

//structures are used for easier access to MODBUS area of 16-bit holding regs.

//sy-MIG, Tower, MPS, MMS, i-MIG
struct params_command {
   int8 command;
   int8 mode;
   int32 data;
} local,remote;

struct params_phase { //must be declared outside other structure because of pointers issue
   int8 r_peak_rise;
   int8 r_peak_fall;
   int16 v_peak;
   int16 s_wire;
   int16 i_base;
   int16 i_fall;
   int8 inductance;
   int8 pulse_width;
   int16 pulse_frequency;
} mig_hot,mig_normal,mig_crater;

struct params_mig {
   int16 t_hot;
   int16 t_crater;
   int16 t_burn;
   int16 t_shed;
   int16 v_shed;
   int16 t_pregas;
   int16 t_postgas;
   int16 s_start;
   int8 r_p_rise;
   int8 r_p_fall;
   int8 r_s_rise;
   int8 r_s_fall;
   //struct params_phase hot,normal,crater;
} mig;

struct params_mma {
   int16 t_hot;
   int16 t_loss;
   int16 v_idle;
   int16 i_hot;
   int16 i_normal;
} mma;

struct params_tig {
   int16 t_strike;
   int16 i_strike;
   int16 t_1;
   int16 t_2;
   int16 i_normal;
   int16 t_pregas;
   int16 t_postgas;
   int16 i_start;
   int8 r_p_rise;
   int8 r_p_fall;
   int8 ac_frequency;
   int8 ac_balance;
   int16 i_final;
} tig;

struct params_other {
   int16 v_real;
   int16 i_real;
   int8 t1_real;
   int8 t2_real;
   int8 t3_real;
   int8 t4_real;
   int8 t5_real;
   int8 t6_real;
   int16 legal_group;
   int16 legal_product;
   int16 legal_piece;
} other;

#locate local=mb_area+4 //[2]*2
#locate remote=mb_area+10 //[5]*2
//MIG & MMA & TIG parameters overlay each other
#locate mig=mb_area+16 //[8]*2
#locate mma=mb_area+16 //[8]*2
#locate tig=mb_area+16 //[8]*2
#locate mig_hot=mb_area+38 //[19]*2
#locate mig_normal=mb_area+52 //[26]*2
#locate mig_crater=mb_area+66 //[33]*2
#locate other=mb_area+80 //[40]*2


Please bear with me if you find some errors, missing variable declarations or like. I have excerpted the parts of my exisiting code which is far more complex and of course copyrighted. Just post your problems or questions here and I'll try to solve it.

Have a nice day, all of you Wink
kypec
Eugeneo



Joined: 30 Aug 2005
Posts: 155
Location: Calgary, AB

View user's profile Send private message

PostPosted: Mon Oct 17, 2005 3:28 am     Reply with quote

It looks like everybody is using the lookup table method to calculate the crc. Here is what I used

Code:

void calculate_crc(int length)
{
   j=1;
   i=1;
   crc.l=0xFFFF;                  // fill all register with 1's

   for (i=0;i<=length-1;i=i+1)
   {
      crc.hl.l^=out_buffer[i];      // xor
      for (j=1;j<=8;++j)
      {
         shift = bit_test(crc.l,0);          // if bit 0 is 1
         (long)crc.L=(long)crc.L>>1;
         if(shift)
            (long)crc.L^=(long)0xA001;
      }
   }

}
reynomj



Joined: 27 Mar 2006
Posts: 2
Location: UK

View user's profile Send private message

Thanks
PostPosted: Mon Mar 27, 2006 5:55 am     Reply with quote

Thanks everyone in this thread I used the idears to develop a modbus master with the comms running totally under interupt control.
Please find the code attached.

The 'IO.c' & 'IO.h' are just some simple code to drive a lcd display and keypad fitted to the PIC

Thanks again
Code:

#include <18F452.H>
#fuses HS,NOPROTECT,NOPUT,NOWDT,NOBROWNOUT,NOLVP,NOCPD,NOWRT

#use delay(clock=10000000)

#use rs232(baud=9600,parity=E,xmit=PIN_C6,rcv=PIN_C7,errors,stream=COMM_1)

#define XTAL_FREQ 10000000

// MODBUS exception codes
#define Illegal_Function     1
#define Illegal_Data_Address 2
#define Illegal_Data_Value   3
#define Slave_Device_Failure 4
#define Acknowledge          5
#define slave_device_Busy    6
#define Negative_Acknoledge  7
#define Memory_Parity_Error  8


struct ModBus
{  int8  CRClo_Index;
   int8  CRChi_Index;
   int8  CRC_Lo;
   int8  CRC_Hi;
   int8  Index;
   int8  Start;
   int8  Slave;
   int8  Quantity;
   int8  x;
   int8  y;
   int16 Three_Half_Byte_Periods;
   int16 Rx_Response_Timeout;
   int1  Idle;
   int1  Initialized;
   int1  Packet;
   int1  Rx_Timeout;
   int1  Shift;
};
struct ModBus mb;


#define REG_Map_MAX        100
#define MAX_REG_Block_SIZE 20
#define MAX_Buffer_SIZE    (MAX_REG_Block_SIZE*2)+10

int8  Buffer[MAX_Buffer_SIZE];   // Tx-Rx buffer
int16 REG_Map[REG_Map_MAX];      // register array


#include <io.h>
#include <io.c>



/***********************************************************
*    COMM1 Rx response timer Interrupt                     *
***********************************************************/
#int_TIMER0
TIMER0_isr()
{
   disable_interrupts(INT_TIMER0);  // TMR0 Overflow Interrupt Enable bit off

   mb.Rx_Timeout=TRUE;              // timeout of Rx char

   mb.Idle=TRUE;                    // set comms Idle flag
}


/***********************************************************
*    COMM1 Receive Complete timer Interrupt                *
***********************************************************/
#int_TIMER1
TIMER1_isr()
{
   disable_interrupts(INT_TIMER1);              // TMR1 Overflow Interrupt Enable bit off
   mb.Index--;                                  // Index last byte recieved
   mb.CRChi_Index = mb.Index;                   // Index last byte recieved
   mb.Packet=1;                                 // Tag packet for processing
}


/***********************************************************
*    COMM1 Receive Interrupt                               *
***********************************************************/
#int_RDA                                        //**************
RDA_isr()                                       // BYTE RECIEVED
{
   mb.x = fgetc(COMM_1);                        // Get incomming byte from buffer

   if(!mb.Packet)                               // Don't recieved while working on a packet or transmitting
   {
      Buffer[mb.Index] = mb.x;                  // Place incomming byte in PacketBuffer
      if(mb.Index<MAX_Buffer_SIZE) mb.Index++;  // Check & Increment Packet Buffer Index
      set_timer1(mb.Three_Half_Byte_Periods);   // Wait 1.5 byte periods then interupt (set for 9600bps now)
      clear_interrupt(INT_TIMER1);              // Clear timer1 overflow Interrupt Flag bit
      enable_interrupts(INT_TIMER1);            // TMR1 Overflow Interrupt Enable bit on
   }

   disable_interrupts(INT_TIMER0);              // disable Rx timeout timer
}


/***********************************************************
*    COMM1 Transmit Interrupt                              *
***********************************************************/
#int_TBE                                        //**************
TBE_isr()                                       // BYTE TRANSIMITED
{
   if(mb.Index <= mb.CRChi_Index)               // Transmit until the entire packet has been sent
   {
      mb.x = Buffer[mb.Index];                  // Store the byte to be sent in a directly addressable location
      fputc(mb.x,COMM_1);                       // Start the byte transmition
      mb.Index++;                               // Index the next byte to be sent
   }
   else
   {
      disable_interrupts(INT_TBE);              // Stop transmittion
      mb.Index = 0;                             // Index for first byte to be recieved

      // start rx response timer
      set_timer0(mb.Rx_Response_Timeout);       // set default time for timer
      clear_interrupt(INT_TIMER0);              // Clear timer0 overflow Interrupt Flag bit
      enable_interrupts(INT_TIMER0);            // TMR0 Overflow Interrupt Enable bit on
   }
}


/***********************************************************
*    CRC calculate                                         *
***********************************************************/
void calculate_crc(int length)
{
   union crc_name
   {
      long _WORD;
      int _BYTE[2];
   } crc;

   crc._WORD=0xFFFF;                            // fill all register with 1's

   for (mb.x=0 ; mb.x<=length-1 ; mb.x++)
   {
      crc._BYTE[0]^=Buffer[mb.x];               // xor

      for (mb.y=1 ; mb.y<=8 ; ++mb.y)
      {
         mb.Shift = bit_test(crc._BYTE[0],0);   // if bit 0 is 1
         crc._WORD >>= 1;
         if(mb.Shift)
            crc._WORD ^= 0xA001;
      }
   }
   mb.CRC_Lo=crc._BYTE[0];
   mb.CRC_Hi=crc._BYTE[1];
}


/***********************************************************
*    COMM1 Main Service                                    *
***********************************************************/
#inline
void mb_Service(void)
{
   if(!mb.Initialized)
   {
      setup_timer_0(RTCC_INTERNAL|RTCC_DIV_4);  // Timer used for Rx timeout
      setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);   // Timer used for Rx packet
      enable_interrupts(INT_RDA);               // Enable recieved bytes interupt
      enable_interrupts(GLOBAL);                // Enable recieved bytes interupt

      mb.Index = 0;                             // Index for first byte to be recieved
      mb.Packet = 0;                            // Allow new packet reception to begin
      mb.Idle=TRUE;

      // clear buffers
      for(mb.x=0;mb.x<sizeof(Buffer);mb.x++) Buffer[mb.x]=0;
      for(mb.x=0;mb.x<(sizeof(REG_Map)/2);mb.x++) REG_Map[mb.x]=mb.x;

      mb.Initialized=1;

      set_uart_speed(9600);                     // set baud rate & timeout period
      mb.Three_Half_Byte_Periods=65536-((18*XTAL_FREQ)/9600);

      mb.Rx_Response_Timeout=0x8000;           // rx response time

   }

   if(mb.Packet && mb.Idle==FALSE)              // Packet ready to process
   {
      if (Buffer[0]==mb.Slave)                  // response from expected slave
      {
         mb.CRClo_Index=mb.CRChi_Index-1;       // Set CRC lo index

         calculate_crc(mb.CRClo_Index);         // Generate CRC

         if(mb.CRC_Lo == Buffer[mb.CRClo_Index] && mb.CRC_Hi == Buffer[mb.CRChi_Index])   // Detect Bad CRClo
         {

            if (Buffer[1]==0x03)                //"Read Holding Registers" function code
            {
               if (mb.Quantity==Buffer[2])      //proper number of bytes arrived
               {
                  mb.Quantity/=2;               //number of holding regs. is half a number of bytes
                  mb.Index=3;                   //set the index to the first data byte

                  while (mb.Quantity>0)         //process all data bytes
                  {
                     REG_Map[mb.Start++]=make16(Buffer[mb.Index],Buffer[mb.Index+1]); //put it in holding reg.
                     mb.Index+=2;               //index the next holding reg.
                     mb.Quantity--;             //one holding reg. was processed
                  }
               }
            }
         }
         else
         {
            // invalid crc
         }

         mb.Idle=TRUE;
      }
      else
      {
         // invalid slave response
         mb.Idle=TRUE;
      }
   }
}


//----------------------------------------------------------
void modbus_send_packet(void)
{
   mb.CRClo_Index=mb.CRChi_Index-1;       // Set CRC lo index

   calculate_crc(mb.CRClo_Index);         // Generate CRC

   mb.Index = 0;                          // Zero index for Transmition
   Buffer[mb.CRClo_Index]=mb.CRC_Lo;      // Place CRC_Lo within packet
   Buffer[mb.CRChi_Index]=mb.CRC_Hi;      // Place CRC_Hi within packet

   enable_interrupts(INT_TBE);            // Kick Off Xmit of Data
}

//----------------------------------------------------------
void modbus_write_multiple(int8 slave,int8 first_reg,int8 count_reg)
{
   Buffer[0]=slave;              // slave address
   mb.Slave=slave;               // store slave address for response
   Buffer[1]=0x10;               // function code
   Buffer[2]=0;                  // starting reg. address MSB
   Buffer[3]=first_reg;          // starting reg. address LSB
   Buffer[4]=0;                  // quantity of registers MSB
   Buffer[5]=count_reg;          // quantity of registers LSB
   Buffer[6]=count_reg*2;        // byte count
   mb.Index=7;                   // set the index to the first data byte

   while (count_reg>0)
   {
      Buffer[mb.Index++]=make8(REG_Map[first_reg],1); // put reg. value MSB
      Buffer[mb.Index++]=make8(REG_Map[first_reg],0); // put reg. value LSB
      first_reg++;               // pointing to next holding reg.
      count_reg--;               // one holding reg. less to put
   }
   mb.CRChi_Index=mb.Index+1;    // Index CRChi

   mb.Rx_Timeout=FALSE;          // reset timeout of RX char
   mb.Packet = 0;                // Allow new packet reception to begin

   modbus_send_packet();         // Tx Buffer

   mb.Idle=FALSE;
}

//----------------------------------------------------------
void modbus_write_single(int8 slave,int8 reg_address,int16 reg_value)
{
   Buffer[0]=slave;              // slave address
   mb.Slave=slave;               // store slave address for response
   Buffer[1]=0x06;               // function code
   Buffer[2]=0;                  // reg. address MSB
   Buffer[3]=reg_address;        // reg. address LSB
   Buffer[4]=make8(reg_value,1); // reg. value MSB
   Buffer[5]=make8(reg_value,0); // reg. value LSB
   mb.CRChi_Index=7;             // Index CRChi

   mb.Rx_Timeout=FALSE;          // reset timeout of RX char
   mb.Packet = 0;                // Allow new packet reception to begin

   modbus_send_packet();         // Tx Buffer

   mb.Idle=FALSE;
}

//----------------------------------------------------------
void modbus_read(int8 slave,int8 first_reg,int8 count_reg)
{
   Buffer[0]=slave;              //slave address
   mb.Slave=slave;               // store slave address for response
   Buffer[1]=0x03;               //function code
   Buffer[2]=0;                  //starting reg. address MSB
   Buffer[3]=first_reg;          //starting reg. address LSB
   Buffer[4]=0;                  //quantity of registers MSB
   Buffer[5]=count_reg;          //quantity of registers LSB
   mb.CRChi_Index=7;             // Index CRChi

   mb.Rx_Timeout=FALSE;          // reset timeout of RX char
   mb.Packet = 0;                // Allow new packet reception to begin

   modbus_send_packet();         // Tx Buffer

   mb.Start=first_reg;           //remember which regs. is to be read
   mb.Quantity=count_reg*2;      //remember how many bytes is to be read

   mb.Idle=FALSE;                // reset comms Idle flag
}

/***********************************************************
*    COMM1 Main                                            *
***********************************************************/
main()
{
int16 index;

   delay_ms ( 200 );                            // start up delay

   port_b_pullups ( TRUE );                     // pull up all port B pins


   // display welcome screen
   LCD_Init();                // set up LCD for 4-wire bus, etc.
   LCD_PutCmd ( LCD_CLEAR_DISP );
   printf ( LCD_PutChar, "********************" );
   LCD_SetPosition ( LCD_LINE_2 );
   printf ( LCD_PutChar, "*   PIC  MASTER    *" );
   LCD_SetPosition ( LCD_LINE_3 );
   printf ( LCD_PutChar, "*   MODBUS TEST    *" );
   LCD_SetPosition ( LCD_LINE_4 );
   printf ( LCD_PutChar, "********************" );

   delay_ms ( 2000 );

   LCD_PutCmd ( LCD_CLEAR_DISP );
   LCD_SetPosition ( LCD_LINE_2 );
   printf(LCD_PutChar,"Rx timeout:" );
   LCD_SetPosition ( LCD_LINE_3 );
   printf(LCD_PutChar,"mb.Idle:" );


   // init variables
   mb.Initialized=0;
   index=0;

   while (TRUE)                     //main loop
   {
      // servive modbus comms
      mb_Service();

      if (mb.Idle)                  //new action can be taken
      {
         if(get_key()==KEY_UP)
         {
            modbus_read(5,1,10);
            set_led(LED_RED);
         }

         if(get_key()==KEY_DOWN)
         {
            modbus_write_multiple(5,1,10);
            set_led(LED_RED);
         }

         if(get_key()==KEY_ENTER)
         {
            modbus_write_single(5,0,index);
            set_led(LED_RED);
         }
      }

//while(get_key()!=0);
set_led(LED_NONE);

LCD_SetPosition ( LCD_LINE_1 );
printf(LCD_PutChar,"%04lX",index++ );

LCD_SetPosition ( LCD_LINE_2+11 );
if(mb.Rx_Timeout) LCD_PutChar("T" );
else LCD_PutChar("F" );
if(get_key()==KEY_ESC) mb.Rx_Timeout=FALSE;

LCD_SetPosition ( LCD_LINE_3+8 );
if(mb.Idle) LCD_PutChar("T" );
else LCD_PutChar("F" );

   }
}


mkr



Joined: 08 Aug 2006
Posts: 49

View user's profile Send private message

modbus byte interval arriving at UART
PostPosted: Tue Sep 05, 2006 9:44 am     Reply with quote

Hello All,
Sorry I posted the same question in a new thread. I had a question about modbus communication regarding byte interval. This equation calculates the time between each byte comming into the uart. How did you arrive at the number 18 in this equation : 65536-((18*XTAL_FREQ)/BAUDRATE) or 16.5 for equation : 65536-((16.5*XTAL_FREQ)/BAUDRATE)
_________________
Thanks

mkr
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

Re: modbus byte interval arriving at UART
PostPosted: Wed Sep 06, 2006 11:57 am     Reply with quote

mkr wrote:
Hello All,
Sorry I posted the same question in a new thread. I had a question about modbus communication regarding byte interval. This equation calculates the time between each byte comming into the uart. How did you arrive at the number 18 in this equation : 65536-((18*XTAL_FREQ)/BAUDRATE) or 16.5 for equation : 65536-((16.5*XTAL_FREQ)/BAUDRATE)


MODBUS specification states a maximum time between bytes within a packet.

Spec wrote:
In RTU mode, messages start with a silent interval of at least 3.5 character times....

The entire message frame must be transmitted as a continuous stream. If a silent
interval of more than 1.5 character times occurs before completion of the frame,
the receiving device flushes the incomplete message and assumes that the next
byte will be the address field of a new message.


Because there are 11 bits in a byte the 1.5 character time will be ~16.5 bit periods. I don't remember why I used 18. It may have been for a 12 bit byte.
rwskinner



Joined: 08 Dec 2006
Posts: 125
Location: Texas

View user's profile Send private message

PostPosted: Mon Jan 15, 2007 5:41 pm     Reply with quote

Neutone,
Thanks for the Modbus examples. Does that modbus slave attempt to automatically do baud rate detection ?

{ ++COMM1.Framming_Errors;
if(COMM1.Framming_Errors & 8)
{ if(++COMM1.Baud & 8)
COMM1.Baud=0;
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group