| Kevin Bloch 
 
 
 Joined: 18 Jan 2007
 Posts: 1
 Location: Western Australia
 
 
			    
 
 | 
			
				| Serial MODBUS supporting PDU03 and PDU16 |  
				|  Posted: Thu Jan 18, 2007 6:08 pm |   |  
				| 
 |  
				| //************************************************************************ //*                         MODBUS Slave Device                          *
 //************************************************************************
 //*                                                                      *
 //*   MODBUS Slave Device, Accepts PDU 03 (read multiple reg's)          *
 //*   and PDU 16 (write multiple reg's).                                 *
 //*                                                                      *
 //*   The major constraint to this code is that only about 25 registers  *
 //*   can be sent or received due to the lack of memory in the PIC and   *
 //*   the large amount required for the receive and transmit buffer.     *
 //*   - Ideally external memory should be added to increase this limit   *
 //*                                                                      *
 //*   Note: If you need to read or write a large number of registers in  *
 //*   one read/write request you may have to increase the buffer size.   *
 //*                                                                      *
 //*   WARNING! use this code at your risk.                               *
 //************************************************************************
 
 #include <16F88.h>
 #fuses HS,NOWDT,NOPROTECT,NOLVP,PUT
 #use delay(clock=20000000)
 #use rs232(baud=9600, xmit=PIN_B2,rcv=PIN_B1, STREAM=PC)         // --- TX = pin 10, RX = pin 11
 
 #include <STDLIB>
 #include <string.h>
 
 #DEFINE LED       PIN_B3 //DIAG LED
 #DEFINE MAXREG    25
 
 int SlaveAddress = 1;
 
 unsigned int buffer[31];
 unsigned int tx_buffer[35];
 
 int16 registers[MAXREG];
 int16 crc = 0;
 int stop = 0;
 
 int  cal_crc(unsigned int dataLength,char check); //CRC 16 for modbus checksum
 unsigned char  mb_req_pdu();
 void mb_rsp_pdu_16(void); //response for modbus 16 function preset multiple registers
 void writeBadRequest(unsigned char error); //response for error in modbus poll
 void mb_rsp_pdu_03(void); //response for modbus 3 function read multiple registers
 void crc16(char value);
 void split(unsigned int16 value);
 
 typedef struct {
 unsigned int high_byte;
 unsigned int low_byte;
 } s_word;
 
 s_word split_word;
 
 VOID MAIN()
 {
 int c = 0;
 
 registers[0] = 10; //for read testing
 registers[1] = 15; //for read testing
 
 FOR(;;)
 {
 
 c = 0;
 c = mb_req_pdu(); //MODBUS Request PDU
 
 if (c == 3)
 mb_rsp_pdu_03(); //MODBUS Response PDU (func 03)
 else
 {
 if (c==16)
 mb_rsp_pdu_16();
 else
 writeBadRequest(c);
 }
 }
 }
 
 
 unsigned char mb_req_pdu()
 {
 unsigned int counter = 0;
 unsigned int16 aux;
 long timeout = 0;
 int temp;
 
 stop = 0;
 
 while ((++timeout < 32000)&&(stop == 0))
 {
 if(!kbhit(PC))
 {
 delay_us(1); //wait for keypress
 }
 else
 {
 timeout = 0;
 if (counter<26> 0)&&(buffer[0] != SlaveAddress)||(counter > 25))
 {
 stop = 1;
 counter = 0;
 }
 }
 }
 
 if(stop == 0)
 {
 if ((buffer[1] == 16)&(((unsigned int) buffer[6] + 9)!=counter)) return 2; //wrong  nr of registers
 if (counter > 25) return 2; //to many bytes in frame
 if (counter <7>0)|( buffer[5] > 32)) return 2; // to many registers
 if( cal_crc(counter - 2,1)) //(counter-2)  = data received less the 2byte crc
 {
 return buffer[1]; //OK
 }
 else
 return 0;   //bad CRC
 }
 else
 {
 for(temp=0;temp<25>0;i--)
 if((crc)&0x0001)
 crc = (crc>>1)^0xa001;
 else
 crc>>=1;
 }
 
 int cal_crc(unsigned int dataLength,char check)
 {
 int j;
 int i;
 int ser_data;
 unsigned char lowCRC;
 unsigned char highCRC;
 
 if(check != 2)
 {
 crc = 0xffff;
 for (j=0; j<dataLength>>8;
 crc<<8>>8;
 }
 else
 {
 crc = 0xffff;
 for (j=0; j<dataLength>>8;
 crc<<8>>8;
 }
 
 if (check==2)
 {
 tx_buffer[dataLength] = lowCRC;
 tx_buffer[dataLength+1] = highCRC;
 return 1;
 }
 else
 if (check==1) //input crc checking
 {
 if ((buffer[dataLength+1] == highCRC) & (buffer[dataLength] == lowCRC ))
 return 1;
 else
 return 0;
 }
 else
 if (check == 0)
 {
 buffer[dataLength] = lowCRC;
 buffer[dataLength+1] = highCRC;
 return 1;
 }
 }
 
 void mb_rsp_pdu_03(void) //response for modbus 03 function (read registers)
 {
 int16 start_addr = 0;
 int16 number_points = 0;
 int16 counter = 0;
 unsigned int counter2 = 0;
 
 int16 i; //buffer[x]: 0=address,1=function,2=start address high, 3=start address low
 
 start_addr = (255*buffer[2]) + buffer[3];
 number_points = (255*buffer[4]) + buffer[5];
 
 tx_buffer[0] = buffer[0];
 tx_buffer[1] = buffer[1];
 tx_buffer[2] = 2*number_points;
 
 counter2 = 3; //0=address,1=function,2=byte count, 3=first data val...
 
 for(counter=start_addr;counter < (start_addr+number_points);counter++)
 {
 split(registers[counter]);
 tx_buffer[counter2] = split_word.high_byte;
 tx_buffer[counter2+1] = split_word.low_byte;
 counter2 += 2;
 }
 
 i=cal_crc(counter2,2);
 
 for(i=0;i<counter2>0)
 {
 buffer[1]+=0x80;
 buffer[2]=error;
 cal_crc(3,0);
 for (i=0; i<5; i++)
 putc(buffer[i]);
 }
 }
 
 void mb_rsp_pdu_16(void) //response for modbus 16 function preset multiple registers
 {
 int16 start_addr = 0;
 int16 number_points = 0;
 int16 counter = 0;
 unsigned int counter2 = 0;
 int16 i;
 
 start_addr = (255*buffer[2]) + buffer[3];
 number_points = buffer[6]/2;
 
 counter2 = 7; //0=address,1=function,2=start address hi, 3=start address lo, 4=no. registers hi
 //5=no. registers lo, 6=byte count, (7=, 8=[register hi, register lo...])
 
 for(counter=start_addr;counter < (start_addr+number_points);counter++)
 {
 registers[counter] = (255*buffer[counter2])+buffer[counter2+1];
 counter2 += 2;
 }
 
 i=cal_crc(6,0);
 
 for(i=0;i<8;i++)
 {
 printf("%C", buffer[i]);
 }
 }
 
 //*****************************************************************************************
 //* Program: Kevin Bloch                                                                  *
 //* Name: split                                                                           *
 //* Inputs: value - a 16bit unsigned integer                                              *
 //* Returns: the high and low byte of value, returned as .high_byte and .low_byte         *
 //* Function: Split an integer into a high and low byte                                   *
 //* Last update: 18th Jan 2004                                                            *
 //*****************************************************************************************
 void split(unsigned int16 value)
 {
 int16 temp;
 
 temp = (value & 65280) / 256;
 split_word.high_byte = (unsigned int)temp;
 temp= (unsigned int) value & 255;
 split_word.low_byte = temp;
 }
 |  |