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