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 CCS Technical Support

RS232 communication with Modbus Protocol Interrupt problem!!

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
neurus



Joined: 31 Mar 2004
Posts: 23
Location: Switzerland

View user's profile Send private message

RS232 communication with Modbus Protocol Interrupt problem!!
PostPosted: Wed Jun 16, 2004 6:09 am     Reply with quote

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 Very Happy

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

View user's profile Send private message

PostPosted: Wed Jun 16, 2004 8:05 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Jun 18, 2004 1:05 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Jun 18, 2004 8:15 am     Reply with quote

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








PostPosted: Fri Jun 18, 2004 12:26 pm     Reply with quote

Very Happy 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

View user's profile Send private message

PostPosted: Mon Jun 21, 2004 12:44 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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