Flameman/modbus

Modbus

= URL =


 * org
 * brief

= Intro =

Il Modbus è un protocollo di comunicazione seriale creato da Modicon nel 1979 per mettere in comunicazione i propri controllori logici programmabili (PLC). È diventato uno standard de facto nella comunicazione di tipo industriale, ed è ora il protocollo di connessione più diffuso fra i dispositivi elettronici industriali. Le principali ragioni di un così elevato utilizzo del Modbus rispetto agli altri protocolli di comunicazione sono:


 * e' un protocollo pubblicato apertamente e royalty-free
 * può essere implementato in pochi giorni, non in mesi
 * Muove raw bits e words senza porre molte restrizioni ai venditori

Modbus consente la comunicazione fra diversi dispositivi connessi alla stessa rete, per esempio un sistema che misura la temperatura e l'umidità e comunica il risultato a un computer. Modbus è spesso usato per connettere un computer supervisore con un'unità terminale remota (RTU) nel controllo di supervisione e sistemi di acquisizione dati (SCADA).

Esistono due versioni del protocollo:


 * seriale (RS232 default, oppure RS485)
 * Ethernet

Brief
Modbus protocol, despite the passage of time (was developed in the company Modicon in 1980) continues to be standard in most industrial controllers (PLC) and is widely used in industrial automation systems. Modbus can work in two modes, ASCII and RTU, and the use is compatible with RS232 interface (RS422, RS485, modems, etc.) In recent years, great popularity gaining also Modbus TCP / IP -Modbus protocol implementation based on TCP / IP Ethernet network. With the significant decline in prices microcontroller interface RS232 and Ethernet, Modbus recently started to be frequently used in embedded systems.

This site is intended to assist in implementing the Modbus in embedded systems.

Esistono due varianti, con differenti rappresentazioni dei dati numerici e piccole differenze sul protocollo stesso.


 * Modbus RTU è una rappresentazione dei dati compatta di tipo esadecimale
 * Modbus ASCII è facilmente leggibile e ridondante

Entrambe le varianti usano la comunicazione seriale.


 * Il formato RTU fa seguire ai comandi/dati un campo checksum di tipo cyclic redundancy check (CRC)
 * il formato ASCII usa un checksum di tipo LRU (longitudinal redundancy check).

I nodi configurati per la variante RTU non possono comunicare con nodi configurati per l'ASCII e viceversa.

Modbus/TCP è molto simile al Modbus RTU, ma trasmette i pacchetti del protocollo dentro pacchetti di dati TCP/IP.

A ogni periferica che necessita di comunicare per mezzo del Modbus viene assegnato un indirizzo unico. Ognuna di queste può inviare un comando Modbus, sebbene generalmente (nel seriale obbligatoriamente) solo una periferica agisce come master. Un comando Modbus contiene l'indirizzo Modbus della periferica con la quale si vuole comunicare. Solo quest'ultima agirà sul comando, sebbene anche le altre periferiche lo ricevano. Tutti i comandi Modbus contengono informazioni di controllo, che assicurano che il comando arrivato sia corretto. Il comandi base possono chiedere ad un RTU di cambiare un valore in uno dei suoi registri, così come comandare alla periferica di restituire uno o più valori contenuti nei suoi registri.

Ci sono diversi modem che supportano Modbus. Alcuni di questi sono specificatamente progettati per questo protocollo. Alcune implementazioni usano fili, comunicazioni wireless o anche SMS o GPRS. Problemi tipici in cui può imbattersi il progettista sono l'alta latenza e problemi di temporizzazione.

crc16.h

 * 1) ifndef CRC16_H
 * 2) define CRC16_H

extern "C" {
 * 1) ifdef __cplusplus
 * 1) endif

extern unsigned short crcsum(const unsigned char* message,			    unsigned long length,			     unsigned short crc); /* * Verify that the last two bytes is a (LSB first) valid CRC of the * message. */ extern int crcverify(const unsigned char* message,		    unsigned long length); /* * Append a two byte CRC (LSB first) to message. length is size of * message excluding crc. Space for the CRC bytes must be allocated * in advance! */ extern void crcappend(unsigned char* message,		     unsigned long length);

}
 * 1) ifdef __cplusplus
 * 1) endif


 * 1) endif

crc16.c

 * 1) include "crc16.h"

/* CRC16 Definitions */ static const unsigned short crc_table[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 };

/* CRC calculation macros */ crc_table[(crcval ^ newchar) & 0x00ff]
 * 1) define CRC_INIT 0xFFFF
 * 2) define CRC(crcval,newchar) crcval = (crcval >> 8) ^ \

unsigned short crcsum(const unsigned char* message, unsigned long length,      unsigned short crc) { unsigned long i;

for(i = 0; i < length; i++) {     CRC(crc, message[i]); } return crc; }

int crcverify(const unsigned char* message, unsigned long length) { /*   * Returns true if the last two bytes in a message is the crc of the * preceding bytes. */ unsigned short expected;

expected = crcsum(message, length - 2, CRC_INIT); return (expected & 0xff) == message[length - 2] && ((expected >> 8) & 0xff) == message[length - 1]; }

void crcappend(unsigned char* message, unsigned long length) { unsigned long crc;

crc = crcsum(message, length, CRC_INIT); message[length] = (unsigned char)(crc & 0xff); message[length+1] = (unsigned char)((crc >> 8) & 0xff); }

I have used the following for several years on a variety of Modbus systems. It works fine on 20mhz xtal for 9600. Bear in mind going any faster might need table lookups and not calcs. Modbus use the TTY version of CRC checking. Initialise with this before use movlw 0xFF movwf CRC_Low movwf CRC_High add to the checksum with this movfp ModbusBuffer1,WREG call CallAddCRC16M obviously the results in CRC_High and CRC_Low that is calculated on the fly as it comes in is then compared with the last two bytes. CallAddCRC16M xorwf CRC_High,f movlw 8 movwf Temp1 CRC_Loop bcf ALUSTA,C rrcf CRC_Low,f rrcf CRC_High,f btfss ALUSTA,C goto NoXOring movlw B'10100000' xorwf CRC_Low,f movlw B'0000001' xorwf CRC_High,f NoXOring decfsz Temp1,F goto CRC_Loop return
 * ****************************** CRC16 routine  ******************************************
 * --CLASS REGISTERS---
 * Temp1
 * CRC_High
 * CRC_Low
 * CRC_High
 * CRC_Low

- Original Message - From: Marcelo Calcia To: p...@yahoogroups.com Sent: Wednesday, February 04, 2004 9:49 AM   Subject: [piclist] CRC checking error in MODBUS protocol

Hello.....   I�m trying to communicate my PIC 16F873 to a motor speed variator that use the RS485 interface and a MODBUS protocol. This protocol generate a CRC cyclic redundancy check to detect errors. This CRC value is two bytes of 8 bits, CRChigh and CRC low. To generate this two bytes, the routine use all bytes that that are included in the message. The two bytes CRC then are included in the frame message that form the MODBUS protocol. Now, I have to make the CRC routine. I have seen the application note 730 (CRC generatig and checking), where use two implementations. I want to know if this application note generate the CRC that I need for the MODBUS protocol, because I have seen some difference. The Modbus use this procedure: 1. Load a 16 bit CRC register with FFFF hex 2. Exclusive OR the first byte ( 8 bit )of the message with the low order byte of the 16 bit register CRC, putting the result in the        CRC register 3. Shift the CRC register one bit to the right (toward the LSB), zero-filling the MSB. Extract and examine the LSB. 4. If LSB was 0 : repeat step 3 ( another shift) If LSB was 1 : Exclusive OR the CRC register with the polynomial value A001hex (1010 0000 0000 0001 ) 5. Repeat steps 3 and 4 until 8 shifts have been performed. When this is done, a complete data byte will have been processed. 6. Repeat steps 2 through 5 for the next byte of the message. Continue doing this until all bytes have been processed. 7. The final content of the CRC register is the CRC value. 8. When the CRC is placed into the message, its upper byte and lower bytes must be swapped Anybody know something about that ? Or have a routine that can help me ? If someone have used this protocol with PIC, please tell me if it has worked well. Thanks for your help...... Marcelo

= Varianti =

Quasi tutte le implementazioni hanno variazioni dallo standard ufficiale. Differenti varietà non possono comunicare correttamente fra dispositivi di fornitori differenti. Alcune delle più comuni variazioni sono:

* Tipi di dati o Floating Point IEEE o Interi a 32 bit o Dati a 8 bit o tipi di dati misti o Campi bit su interi o moltiplicatori per cambiare i dati da/in interi. 10, 100, 1000, 256 ...

* Estensioni del protocollo o Indirizzi degli slave a 16 bit o dimensione dei dati a 32 bit (1 indirizzo = 32 bit di dati restituiti) o word swapped data

= Limitazioni =

Modbus è stato progettato nel lontano 1970 per comunicare con i PLC. I tipi di dati sono limitati a quelli supportati dai PLC a suo tempo. Oggetti binari di grandi dimensioni non sono supportati. Non esiste un metodo standard per un nodo di trovare la descrizione di un tipo di dato; per esempio, determinare se un valore registro rappresenta una temperatura fra i 30 e i 175 gradi