|
|
View previous topic :: View next topic |
Author |
Message |
neurus
Joined: 31 Mar 2004 Posts: 23 Location: Switzerland
|
RS232 communication with Modbus Protocol Interrupt problem!! |
Posted: Wed Jun 16, 2004 6:09 am |
|
|
hi
I'm trying to write a programm with the Modbus Protocol for a Slave device. According to the modbus protocol the message frames are separated by a silent interval of at least 3.5 character times and 1.5 character times between two characters. I've the int_RDA to receive data. When I receive a character, it starts a Timer(Interrupt), which is waiting for about 3.5 character times and when the timer gets an overflow, set the readydata flag and start the sequence again.
here the code:
Code: |
//Receive routine
#int_RDA
void RDA_isr()
{
modbus.rxData[index]=getc();
index++;
if (modbus.rxData[0] == modbus.address)
{
if (modbus.rxData[1] == (0x03 || 0x05 || 0x10))
{
set_timer1(Timer3_5);
//enable_interrupts(int_TIMER3);
enable_interrupts(int_TIMER1);
}
}
else
{
index--;
}
}
|
Code: |
#int_TIMER1 // 3.5 Char
TIMER1_isr()
{
if (index>7)
{
disable_interrupts(int_TIMER1);
NewData = 1; // data ready to process....
}
else
{
disable_interrupts(int_TIMER1);
modbus.error = 0x05;
errorflag = 1;
}
}
|
I don't know, if my code is right. I'm newbie in C and I've some problems with the interrupts handling.
The whole code from my programm is attached.
please look at the code and say me, if you find a misstake
Thanks and sorry for my english
Pablo
I can't to attach my code, ????
here the code:
Main
Code: |
//Main program to Modbus
//7. Jun 2004
#include <header_Modbus.h>
#include <Driver_Modbus4.c>
void main()
{
output_low(pin_b5);
output_low(pin_b6);
errorflag = 0;
index=0;
while(true)
{
Modbus_Main();
if (NewData)
{
NewData = 0;
Modbus_Conversion();
}
if (errorflag)
{
send_error();
errorflag = 0; // Error Flag reset
}
}
}
|
Driver-Modbus
Code: |
// Driver to RS485 Interface with Modbus Protocol
// 8. Jun 2004
struct ModbusDaten
{
int address;
char rxData[32]; //Array to save the sendet Data from Master
char txData[16]; //Array to save the answer for the Master
int16 wait3_5; //waiting Interrupt
int8 CRCLow, CRCHigh;
char error; //Error data
} modbus;
//Reg to send data
#byte PIR1 = 0xF9E
// Variable definition
char MessSigReg[]={0x02,0x03,0x04,0x1A,0x23,0x45,0x67,0x89,0x10,0x15}; //Constants to test the program
int16 RefSigReg, AdcGain;
int16 CalReg, AverageReg, RangeReg, Timer1_5,Timer3_5;
int1 StartMess, SwitchOn, SwitchOff, Reset, errorflag;
int8 index,ContIndex,SendIndex; //Index for the memory reg. of data
int8 temp;
int ByteCount;
int1 NewData;
#int_TIMER1 // 3.5 Char
TIMER1_isr()
{
if (index>7)
{
disable_interrupts(int_TIMER1);
NewData = 1; // data ready to process....
}
else
{
disable_interrupts(int_TIMER1);
modbus.error = 0x05;
errorflag = 1;
}
}
#int_TIMER3 // 1.5 Char
TIMER3_isr()
{
if (index<8)
{
modbus.error = 0x04;
errorflag = 1;
}
set_timer1(Timer1_5);
}
//Receive routine
#int_RDA
void RDA_isr()
{
modbus.rxData[index]=getc();
index++;
if (modbus.rxData[0] == modbus.address)
{
if (modbus.rxData[1] == (0x03 || 0x05 || 0x10))
{
set_timer1(Timer3_5);
//enable_interrupts(int_TIMER3);
enable_interrupts(int_TIMER1);
}
}
else
{
index--;
}
}
//Transmit routine
#int_TBE
void TBE_isr()
{
int i;
putc(modbus.txData[i]);
i++;
if (i>SendIndex)
{
disable_interrupts(INT_TBE);
i = 0;
SendIndex = 0;
index=0;
ByteCount=0;
}
}
//Subroutine to test the CRC
//data_to-control = complete data packet
//n = packet lengh without the last 2 byte CRCH and CRCL
int1 Mb_test_CRC(char data_to_control[], int n)
{
int16 crc, carry_flag,a;
int8 i, j;
crc = 0xFFFF;
for(i=0;i<n;i++)
{
crc = crc ^ data_to_control[i];
for (j=0;j<8;j++)
{
a = crc;
carry_flag = a&0x0001;
crc = crc >> 1;
if (carry_flag==1) crc=crc^0xa001;
}
}
if ((data_to_control[n+1]!=(crc>>8)) || (data_to_control[n]!=(crc&255)))
return 1;
else
return 0;
}
//Subroutine to calculate the CRC
//data_to_control = data packet without CRC bytes
//n = packet lengh without CRC
int16 Mb_calcul_crc(char data_to_control[],int n)
{
int16 crc,carry_flag,a;
int8 i,j;
crc=0xffff;
for (i=0;i<n;i++)
{
crc=crc^data_to_control[i];
for (j=0;j<8;j++)
{
a=crc;
carry_flag=a&0x0001;
crc=crc>>1;
if (carry_flag==1) crc=crc^0xa001;
}
}
//data_to_control[n+1]=crc>>8;
//data_to_control[n]=crc&255;
return crc;
}
void send_error()
{
switch(modbus.rxData[1]) //Calculate the CRC
{
case 0x03:
modbus.txData[0] = modbus.rxData[0]; //Address slave
modbus.txData[1] = modbus.rxData[1]+0x80; //Command = 0x83
modbus.txData[2] = modbus.error; //Errornumber
modbus.CRCHigh = Mb_calcul_crc(modbus.txData,3) >> 8;
modbus.CRCLow = Mb_calcul_crc(modbus.txData,3) & 0x00FF;
modbus.txData[3] = modbus.CRCLow;
modbus.txData[4] = modbus.CRCHigh;
SendIndex = 4; //Index for the Send routine
enable_interrupts(INT_TBE);
break;
case 0x05:
modbus.txData[0] = modbus.rxData[0]; //Address slave
modbus.txData[1] = modbus.rxData[1]+0x80; //Command = 0x83
modbus.txData[2] = modbus.error; //Errornumber
modbus.CRCHigh = Mb_calcul_crc(modbus.txData,3) >> 8;
modbus.CRCLow = Mb_calcul_crc(modbus.txData,3) & 0x00FF;
modbus.txData[3] = modbus.CRCLow;
modbus.txData[4] = modbus.CRCHigh;
SendIndex = 4; //Index for the Send routine
enable_interrupts(INT_TBE);
break;
case 0x10:
modbus.txData[0] = modbus.rxData[0]; //Address slave
modbus.txData[1] = modbus.rxData[1]+0x80; //Command = 0x83
modbus.txData[2] = modbus.error; //Errornumber
modbus.CRCHigh = Mb_calcul_crc(modbus.txData,3) >> 8;
modbus.CRCLow = Mb_calcul_crc(modbus.txData,3) & 0x00FF;
modbus.txData[3] = modbus.CRCLow;
modbus.txData[4] = modbus.CRCHigh;
SendIndex = 4; //Index for the Send routine
enable_interrupts(INT_TBE);
break;
}
}
void Modbus_Check()
{
if (modbus.rxData[0] == modbus.address)
{
if (index>7)
{
if (modbus.rxData[1] == (0x03 || 0x05))
{
ContIndex = index -2;
index = 0;
}
}
else if (index > 0x0c)
{
if (modbus.rxData[1] == 0x10)
{
ByteCount = (int)modbus.rxData[6]+2;
ContIndex = index -2;
index = 0;
}
}
}
}
//Main routine from Modbus driver
void Modbus_Main()
{
Timer1_5 = 65536-(t1_5*Xtal_Freq/4);
Timer3_5 = 65536-(t3_5*Xtal_Freq/4);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
modbus.address=0x01; //Slave Addresse
enable_interrupts(INT_RDA);
enable_interrupts(global);
//set_timer1(Timer3_5);
//set_timer3(Timer1_5); //set the value for the Timer2
Modbus_Check();
}
//here will be analized the received data from Master and prepare it to send to the Master
void Modbus_Conversion()
{
int n;
switch (modbus.rxData[1])
{
case 0x03:
n=0;
//control the CRC from received data
if (Mb_test_CRC(modbus.rxData,ContIndex)==1)
{
//send error to master
modbus.error = 0x04;
send_error();
break;
}
else
{
//prepare the data to send back
modbus.txData[0] = modbus.rxData[0]; //Address slave
modbus.txData[1] = modbus.rxData[1]; //Command = 0x03
modbus.txData[2] = modbus.rxData[5]*2; //Byte Count = ByteCount+CRCH+CRCL
for (n=0; n<modbus.txData[2];n++) //Data
{
modbus.txData[n+3] = MessSigReg[n];
}
modbus.CRCHigh = Mb_calcul_crc(modbus.txData,n+3) >> 8;
modbus.CRCLow = Mb_calcul_crc(modbus.txData,n+3) & 0x00FF;
modbus.txData[n+3] = modbus.CRCLow;
modbus.txData[n+4] = modbus.CRCHigh;
SendIndex = n + 5; // Index for the Send subroutine
enable_interrupts(INT_TBE);
//TXREG = modbus.txData[0];
break;
}
case 0x05:
n=0;
//control the CRC from received data
if (Mb_test_CRC(modbus.rxData,ContIndex)==1)
{
//send error to master
modbus.error = 0x04;
send_error();
break;
}
else
{
//prepare the data to send back
for (n=0;n<=ContIndex;n++)
{
modbus.txData[n] = modbus.rxData[n]; //Address slave
}
modbus.CRCHigh = Mb_calcul_crc(modbus.txData,ContIndex) >> 8;
modbus.CRCLow = Mb_calcul_crc(modbus.txData,ContIndex) & 0x00FF;
modbus.txData[6] = modbus.CRCLow;
modbus.txData[7] = modbus.CRCHigh;
switch (modbus.txData[3]) //here it will chosee
{
case 0x00:
if (modbus.txData[4] & 0xFF)
{
output_high(pin_b5);
}
else
{
output_low(pin_b5);
}
break;
case 0x01:
if (modbus.txData[4] & 0xFF)
{
output_high(pin_b4);
}
else
{
output_low(pin_b4);
}
break;
}
SendIndex = n + 2; //Bytes to send
enable_interrupts(INT_TBE);
break;
}
case 0x10:
n=0;
//control the CRC from received data
if (Mb_test_CRC(modbus.rxData,ContIndex)==1)
{
//send error to master
modbus.error = 0x04;
send_error();
break;
}
else
{
for (n=0;n<ByteCount;n++) //the data will be writed to memory
{
MessSigReg[n] = modbus.rxData[n+7];
}
//prepare the data to send to the master
for (n=0;n<6;n++)
{
modbus.txData[n]=modbus.rxData[n];
}
modbus.CRCHigh = Mb_calcul_crc(modbus.txData,6) >> 8;
modbus.CRCLow = Mb_calcul_crc(modbus.txData,6) & 0x00FF;
modbus.txData[6] = modbus.CRCLow;
modbus.txData[7] = modbus.CRCHigh;
SendIndex = n + 1; //Bytes to send
enable_interrupts(INT_TBE);
break;
}
}
} |
Header
Code: |
// Header Datei
//7. Jun 2004
#include<18F252.h>
#fuses XT,NOWDT,NOLVP //Quarz, no Watchdog, no Low Voltage Program
#use delay(clock=4000000) // Clock Frequency 4MHz
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,enable=PIN_C2,errors)
#define Xtal_Freq 4000000
#define Baudrate 9600
#define t1_5 1.5625e-3
#define t3_5 3.6458e-3
|
|
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Wed Jun 16, 2004 8:05 am |
|
|
You can start with this and bend it to your requirements. You don't need to re-invent the wheel. It only supports function code 3 and 6. The maximum time between bytes in a packet is 1.5 byte periods. If you go for more than 1.5 byte periods without recieving the packet is finished. Then you must wait 3.5 byte periods minimum to reply. The delay before the reply should be longer if you are using RS485 to allow the serial link to switch from transmit mode to listen mode.
http://www.ccsinfo.com/forum/viewtopic.php?t=19306
This technic has worked very well for me but if you manage to improve it please post said improvements. |
|
|
neurus
Joined: 31 Mar 2004 Posts: 23 Location: Switzerland
|
|
Posted: Fri Jun 18, 2004 1:05 am |
|
|
hi neutone
thanks for your answer. I've writed a Modbus driver and it works well with the RS232. When I want to need it with the RS485, it does a error and I don't know why. My Driver supports the functions code 03, 05 and 10.
When I work with RS485:
with the function 10 I can write a Data Array on the pic and with 03 I can read this Array. The problem is, that if I write the Array (code 10) and after this I read the array, the data into the Array was overwrited, exactly the first byte(array[0]). So works my driver:
Master send Data (code 10) ---> Slave receives Data, Process it and reply the answer to the Master. In this moment, my Receive-Routine(int_RDA) receives the identic send data to the master and process it and writes it over. If I'm using the RS232, I don't have this problem.
As Master I've a Computer with the Programm Modbus Poll and a USB->RS485 Converter from Adlink
I'm working with the 18F252 and CCS with MPLAB.
If you have time to look into my code, I can send it to you. Maybe lies the problem in the USB->RS485 Converter.
In your reply you mention some problem about the time, when I'm using RS485. Can it be the problem?? If I'm understandig right, when the Slave gets the frame from Master, it have to wait the 3.5 byte times or longer before reply this frame? If it is so, I know where the problem lies.
I'm appreciate, if you give me an answer.
Thanks and best regards from Switzerland
Pablo |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Fri Jun 18, 2004 8:15 am |
|
|
The 485 converter must hold the transciever chip in transmit mode for a period to allow a zero to be transmitted. I use a circuit that holds transmit for xmS after the last marking bit. x couild be as small as 1.5 byte times or as large as 5 byte times. Some of these circuits use dip switches to select baud rate. Waiting less than 5 byte periods to reply is probably a mistake for most RS485 equipment. My best solution is to use a variable delay with RS485. With RS232 the delay between packets is not usually important due to the link being full duplex. I recommend having a look at this link and concider using function codes 3, 6 and 16 or maybe just 3 and 16. A lot of PLC's just use 3 and 16.
http://home.houston.rr.com/neutone/Older%20Modbus%20spec.pdf |
|
|
Guest
|
|
Posted: Fri Jun 18, 2004 12:26 pm |
|
|
I have written many modbus drivers and it seems best to not be concerned with these times. To make it more simple you should just buffer the data then analyse it byte by byte. By the time you analyse the buffered data the minimum delay will have passed so you can respond without timing issues. For example, if you buffer the complete modbus data packet you can do anything to it and it will be very fast. At any given time if you find bad or wrong data, clear the buffer and reply with the proper error message. |
|
|
neurus
Joined: 31 Mar 2004 Posts: 23 Location: Switzerland
|
|
Posted: Mon Jun 21, 2004 12:44 am |
|
|
hi
Thaks for you answer.
@Neutone
I guess, that my problem is a time problem. At the moment I'm triyng to write the code for the Master Pic. As soon I'm finished with them, I can tell you, if it work or not.
The doc(Link) is very interesting, thanks.
@Gast
I'm working exactly, how you describe in your post. I receive a data packet, store it and my receive routine is now waiting for the next packet. During it, the program process the data again, until the next packet has come.
Can you post your code, so I can compare with my code. It would be nice.
Thanks und aloha
Pablo |
|
|
|
|
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
|