|
|
View previous topic :: View next topic |
Author |
Message |
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
Interrupt driven data streams on USART with MODBUS packets |
Posted: Thu May 13, 2004 2:23 pm |
|
|
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
|
|
Posted: Thu May 13, 2004 2:24 pm |
|
|
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
|
Modbus program |
Posted: Tue Sep 14, 2004 10:01 am |
|
|
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
|
Re: Modbus program |
Posted: Tue Sep 21, 2004 10:45 am |
|
|
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
|
|
Posted: Tue Oct 19, 2004 5:57 am |
|
|
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.
any help would be *greatly* appreciated!
the balony fish |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Tue Oct 19, 2004 7:58 am |
|
|
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.
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
|
|
Posted: Tue Oct 19, 2004 8:20 am |
|
|
You didn't post the timeout routine you used for timer1. |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Tue Oct 19, 2004 11:44 am |
|
|
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
|
|
Posted: Tue Oct 19, 2004 1:24 pm |
|
|
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
|
Modified MODBUS routines = MASTER + multiple SLAVES |
Posted: Thu Sep 15, 2005 1:53 am |
|
|
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
kypec |
|
|
Eugeneo
Joined: 30 Aug 2005 Posts: 155 Location: Calgary, AB
|
|
Posted: Mon Oct 17, 2005 3:28 am |
|
|
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
|
Thanks |
Posted: Mon Mar 27, 2006 5:55 am |
|
|
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
|
modbus byte interval arriving at UART |
Posted: Tue Sep 05, 2006 9:44 am |
|
|
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
|
Re: modbus byte interval arriving at UART |
Posted: Wed Sep 06, 2006 11:57 am |
|
|
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
|
|
Posted: Mon Jan 15, 2007 5:41 pm |
|
|
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; |
|
|
|
|
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
|