CANBUS

ModBus App Notes

Modbus protocol for CRC calculation and response events -v49.03.

Added MODBUS protocol to handle CRC calculation and response events.

 
Modbus RTU is available on RS2, RS4, AS1, AS2 - v49.32.
More than one of these can be active at the same time if required.

Setup example:
					
				SETUP(RS2)				
				{
					baud = 19200;
					parity = N;
					rxi = Y;
					txi = Y;
					proto = ModbusRTU;	//define modbus as the protocol to use
				}
				


All low level handling of Modbus requests and responses are handled by the TFT firmware and the 'interface' to the itronSMART code is via system variables (xxx refers to the interface name: RS2, RS4, AS1 or AS2):
Name Type Use
MB_xxx_MODE U8 Modbus mode (0=master, 1=slave)
MB_xxx_REGS PTR Pointer to a U16 array to hold read / write registers
MB_xxx_COILS PTR Pointer to a U8 array to hold read / write coil bit values
MB_xxx_SLAVEID U8 Slave address
MB_xxx_FUNC U8 Modbus function code (1/3/15/16)
MB_xxx_REGOFF U16 Offset into U16 register array for read / write action
MB_xxx_REGCNT U16 Number of registers to read / write (or have been read / written)
MB_xxx_COILOFF U16 Offset into U16 coil register array for read / write action
MB_xxx_COILCNT U16 Number of coil registers to read / write (or have been read / written)
MB_xxx_STATUS U8 Modbus status (0=idle, 1=tx, 1=rx, 2+ error)
MB_xxx_TIMEOUT U16 Response timeout in milliseconds
MB_xxx_DATAOK PTR Pointer to function that is called after a successful transaction
MB_xxx_DATAERR PTR Pointer to function that is called after an error has occurred

COIL data is converted from bits to bytes to make itronSMART access easier. For example a Modbus request to read 12 coils (2 Modbus data bytes) with offset of 6 will result in bytes 6 – 17 in the U8 coil registers being used.
 
Master Slave
					 // New modbus test
					 lib(fnt, "SDHC/asc_32.fnt");
					 style(ps, page){back = black;}
					 style(ts, text){font = fnt; col = white; currel = TL;}
					 style(ts_cc, text){font = fnt; col = white; currel = CC;}
					 
					 setup(RS2)
					 {
						 baud = 19200;
						 parity = N;
						 encode = sd;
						 txi = Y;
						 rxi = Y;
						 proto = ModbusRTU;
					 }
					 
					 var(regs, 0, U16, 128);
					 
					 load(MB_RS2_MODE, 0);
					 load(MB_RS2_REGS > "regs");
					 load(MB_RS2_SLAVEID, 1);
					 load(MB_RS2_TIMEOUT, 1000);
					 
					 var(v1, 10, U16);
					 var(v2, 30, U16);
					 var(v3, 80, U16);
					 var(v4, 0, U16);
					 var(v5, 0, U16);
					 var(v6, 0, U16);
					 
					 int(tim0, TIMER0, clear_status);
					 
					 page(p, ps)
					 {
						 posn(100, 40); text(t1, "WR", ts);
						 posn(+0, +35); text(t2, "0", ts);
						 posn(+0, +30); text(t3, "1", ts);
						 posn(+0, +30); text(t4, "2", ts);
						 
						 posn(130, 75); text(t5, v1, ts);
						 posn(+0, +30); text(t6, v2, ts);
						 posn(+0, +30); text(t7, v3, ts); 
						 
						 posn(340, 40); text(t8, "RD", ts);
						 posn(+0, +35); text(t9, "0", ts);
						 posn(+0, +30); text(t10, "1", ts);
						 posn(+0, +30); text(t11, "2", ts);
						 
						 posn(370, 75); text(t12, "", ts);
						 posn(+0, +30); text(t13, "", ts);
						 posn(+0, +30); text(t14, "", ts);
						 
						 posn(120, 136); key(k1, write, 240, 272, TOUCH);
						 posn(360, 136); key(k2, read, 240, 272, TOUCH);
						 
						 posn(240, 256); text(status, "", ts_cc);
						 posn(460, 256); text(status_error, "", ts_cc);
					 }
					 
					 func(read)
					 {
						 load(MB_RS2_FUNC, 3);
						 load(MB_RS2_REGOFF, 0);
						 load(MB_RS2_REGCNT, 3);
						 load(MB_RS2_DATAOK > "read_ok");
						 load(MB_RS2_DATAERR > "read_error");
						 load(RS2, 1);
					 }
					 
					 func(read_ok)
					 {
						 load(v4, regs.0); text(t12, v4);
						 load(v5, regs.1); text(t13, v5);
						 load(v6, regs.2); text(t14, v6);;
						 text(status, "READ OK");;
						 load(TIMER0, 1500, 1);
					 }
					 
					 func(read_error)
					 {
						 text(status, "READ ERROR");
						 text(status_error, MB_RS2_STATUS);;
						 load(TIMER0, 1500, 1);
					 }
					 
					 func(write)
					 {
						 load(MB_RS2_FUNC, 16);
						 load(MB_RS2_REGOFF, 0);
						 load(MB_RS2_REGCNT, 3);
						 load(regs.0, v1);
						 load(regs.1, v2);
						 load(regs.2, v3);
						 load(MB_RS2_DATAOK > "write_ok");
						 load(MB_RS2_DATAERR > "write_error");
						 load(RS2, 1);
					 }
					 
					 func(write_ok)
					 {
						 text(status, "WRITE OK");;
						 load(TIMER0, 1500, 1);
					 }
					 
					 func(write_error)
					 {
						 text(status, "WRITE ERROR");
						 text(status_error, MB_RS2_STATUS);;
						 load(TIMER0, 1500, 1);
					 }
					 
					 func(clear_status)
					 {
						 text(status, "");;
						 text(status_error, "");;
					 }
					 
					 show(p);
					 
					 // New modbus test - slave
					 
					 lib(fnt, "SDHC/asc_32.fnt");
					 
					 style(ps, page){back = black; }
					 style(ts, text){font = fnt; col = white; currel = TL;}
					 
					 setup(RS2)
					 {
						 baud = 19200;
						 parity = N;
						 encode = sd;
						 txi = Y;
						 rxi = Y;
						 proto = ModbusRTU;
					 }
					 
					 var(regs, 0, U16, 128);
					 
					 load(MB_RS2_MODE, 1);
					 load(MB_RS2_REGS > "regs");
					 load(MB_RS2_SLAVEID, 1);
					 load(MB_RS2_DATAOK > "ok");
					 load(MB_RS2_DATAERR > "error");
					 
					 load(regs.0, 10);
					 load(regs.1, 9102);
					 load(regs.2, 234);
					 
					 int(tim0, TIMER0, clear_status);
					
					 page(p, ps)
					 {
						 posn(240, 50); text(t1, regs.0, ts);
						 posn(240, 90); text(t2, regs.1, ts);
						 posn(240, 130); text(t3, regs.2, ts);
						 posn(240, 256); text(status, "", ts);
					 }
					 
					 func(ok)
					 {
						if(MB_RS2_FUNC==16?[text(status, "WRITE OK");;run(update_values);]
						:[text(status, "READ OK");;]);
						load(TIMER0, 1500, 1);
					 }
					 
					 func(error)
					 {
					 if(MB_RS2_FUNC==16?[text(status, "WRITE ERROR");;]
					 :[text(status, "READ ERROR");;]);
						load(TIMER0, 1500, 1);
					 }
					 
					 func(update_values)
					 {
						 text(t1, regs.0);
						 text(t2, regs.1);
						 text(t3, regs.2);;
					 }
					 
					 func(clear_status)
					 {
						text(status, "");;
					 }
					 
					 show(p);
					

Master operation
Write regs / coils
Load values into U16 / U8 variable that MB_xxx_REGS/MB_xxx_COILS points to.
Setup MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF and use command LOAD(RS2, 1); to start request.
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the success response is received from the slave.
Read regs / coils
Setup MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF and use command LOAD(RS2, 1); to start request.
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the success response is received from the slave.
Values can then be read from variable that MB_xxx_REGS/MB_xxx_COILS points to.

Slave operation
Write regs / coils
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the successful request from the master.
Values can then be read from variable that MB_xxx_REGS/MB_xxx_COILS points to.
MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_xxx_COILOFF values should be read to know which registers were updated.
Read regs / coils
MB_xxx_DATAOK/MB_xxx_DATAERR function will be called after the successful request from the master.
MB_xxx_REGCNT/MB_xxx_COILCNT and MB_xxx_REGOFF/MB_COILOFF values can be read to know which registers were read.