SOURCE
*******************************
Project : PIC16F Giao Tiếp MODBUS
Date : 11-Nov-2013
Author : quinguyen
Company : Vinasemiconductor
Chip type : PIC16F877A
Program type : Application
Core Clock frequency: 20.000000 MHz
*******************************
MASTER
#include<pic16f877a>
#fuses HS, NOWDT
#use delay(clock=20M)
#define MODBUS_TYPE MODBUS_TYPE_MASTER
#define MODBUS_SERIAL_TYPE MODBUS_RTU //use MODBUS_ASCII for ASCII mode
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 9600
#define ERRLED PIN_D1 //led bao loi truyen
#define ADD1 PIN_D7 //ADD1
#define ADD2 PIN_D6
#define ADD3 PIN_D5
#define ADD4 PIN_D4
#define ADD5 PIN_C5
#define ADD6 PIN_C4
#define ADD7 PIN_D3
#define ADD8 PIN_D2
#ifndef USE_WITH_PC
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, stream=PC, errors)
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_EXT
#define MODBUS_SERIAL_TX_PIN PIN_B1 // Data transmit pin
#define MODBUS_SERIAL_RX_PIN PIN_B0 // Data receive pin
//The following should be defined for RS485 communication
#define MODBUS_SERIAL_ENABLE_PIN PIN_B4 // Controls DE pin for RS485
#define MODBUS_SERIAL_RX_ENABLE PIN_B5 // Controls RE pin for RS485
#define DEBUG_MSG(msg) fprintf(PC, msg)
#define DEBUG_DATA(msg,data) fprintf(PC, msg, data)
#else
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define DEBUG_MSG(msg) if(0)
#define DEBUG_DATA(msg,data) if(0)
#endif
//#define MODBUS_SLAVE_ADDRESS1 0x01
//#define MODBUS_SLAVE_ADDRESS2 0x02
int8 MODBUS_SLAVE_ADDRESS;
int i;
/*This function may come in handy for you since MODBUS uses MSB first.*/
int8 swap_bits(int8 c)
{
return ((c&1)?128:0)|((c&2)?64:0)|((c&4)?32:0)|((c&8)?16:0)|((c&16)?8:0)
|((c&32)?4:0)|((c&64)?2:0)|((c&128)?1:0);
}
void read_all_holding()
{
DEBUG_MSG("Holding Registers:\r\n");
if(!(modbus_read_holding_registers(MODBUS_SLAVE_ADDRESS,0,8)))
{
DEBUG_MSG("Data: ");
/*Started at 1 since 0 is quantity of coils*/
for(i=1; i < (modbus_rx.len); ++i)
DEBUG_DATA("%X ", modbus_rx.data[i]);
DEBUG_MSG("\r\n\r\n");
output_low(ERRLED);
switch (MODBUS_SLAVE_ADDRESS)
{
case 1:
output_high(ADD1);
break;
case 2:
output_high(ADD2);
break;
case 3:
output_high(ADD3);
break;
case 4:
output_high(ADD4);
break;
case 5:
output_high(ADD5);
break;
case 6:
output_high(ADD6);
break;
case 7:
output_high(ADD7);
break;
case 8:
output_high(ADD8);
break;
}
}
else
{
DEBUG_DATA("<-**Exception %X**->\r\n\r\n", modbus_rx.error);
output_high(ERRLED);
}
}
void unknown_func()
{
DEBUG_MSG("Trying unknown function\r\n");
DEBUG_MSG("Diagnostic:\r\n");
if(!(modbus_diagnostics(MODBUS_SLAVE_ADDRESS,0,0)))
{
DEBUG_MSG("Data:");
for(i=0; i < (modbus_rx.len); ++i)
DEBUG_DATA("%X ", modbus_rx.data[i]);
DEBUG_MSG("\r\n\r\n");
}
else
{
DEBUG_DATA("<-**Exception %X**->\r\n\r\n", modbus_rx.error);
}
}
void main()
{
DEBUG_MSG("\r\nInitializing...");
// MODBUS_SLAVE_ADDRESS=MODBUS_SLAVE_ADDRESS1;
modbus_init();
DEBUG_MSG("...ready\r\n");
while(TRUE)
{
output_d(0);
for (MODBUS_SLAVE_ADDRESS=1;MODBUS_SLAVE_ADDRESS<=8;MODBUS_SLAVE_ADDRESS++)
{
read_all_holding();
delay_ms(2000);
}
}
}
SLAVE
#include <pic16f877a>
#fuses HS, NOWDT
#use delay(clock=4000000)
#define MODBUS_TYPE MODBUS_TYPE_SLAVE
#define MODBUS_SERIAL_TYPE MODBUS_RTU //use MODBUS_ASCII for ASCII mode
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 9600
#ifndef USE_WITH_PC
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_EXT
#define MODBUS_SERIAL_TX_PIN PIN_B1 // Data transmit pin
#define MODBUS_SERIAL_RX_PIN PIN_B0 // Data receive pin
//The following should be defined for RS485 communication
#define MODBUS_SERIAL_ENABLE_PIN PIN_B2 // Controls DE pin for RS485
#define MODBUS_SERIAL_RX_ENABLE PIN_B3 // Controls RE pin for RS485
#else
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#endif
#define MODBUS_ADDRESS 1 // Địa chỉ của slave, mỗi slave địa chỉ khác nhau
/*This function may come in handy for you since MODBUS uses MSB first.*/
int8 swap_bits(int8 c)
{
return ((c&1)?128:0)|((c&2)?64:0)|((c&4)?32:0)|((c&8)?16:0)|((c&16)?8:0)
|((c&32)?4:0)|((c&64)?2:0)|((c&128)?1:0);
}
void main()
{
int8 coils = 0b00000101;
int8 inputs = 0b00001001;
int16 hold_regs[] = {0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111};
int16 input_regs[] = {0x1100,0x2200,0x3300,0x4400,0x5500,0x6600,0x7700,0x8800};
int16 event_count = 0;
modbus_init();
while(TRUE)
{
while(!modbus_kbhit());
delay_us(50);
//check address against our address, 0 is broadcast
if((modbus_rx.address == MODBUS_ADDRESS) || modbus_rx.address == 0)
{
switch(modbus_rx.func)
{
case FUNC_READ_COILS: //read coils
case FUNC_READ_DISCRETE_INPUT: //read inputs
if(modbus_rx.data[0] || modbus_rx.data[2] ||
modbus_rx.data[1] >= 8 || modbus_rx.data[3]+modbus_rx.data[1] > 8)
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
else
{
int8 data;
if(modbus_rx.func == FUNC_READ_COILS)
data = coils>>(modbus_rx.data[1]); //move to the starting coil
else
data = inputs>>(modbus_rx.data[1]); //move to the starting input
data = data & (0xFF>>(8-modbus_rx.data[3])); //0 out values after quantity
if(modbus_rx.func == FUNC_READ_COILS)
modbus_read_discrete_input_rsp(MODBUS_ADDRESS, 0x01, &data);
else
modbus_read_discrete_input_rsp(MODBUS_ADDRESS, 0x01, &data);
event_count++;
}
break;
case FUNC_READ_HOLDING_REGISTERS:
case FUNC_READ_INPUT_REGISTERS:
if(modbus_rx.data[0] || modbus_rx.data[2] ||
modbus_rx.data[1] >= 8 || modbus_rx.data[3]+modbus_rx.data[1] > 8)
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
else
{
if(modbus_rx.func == FUNC_READ_HOLDING_REGISTERS)
{
modbus_read_holding_registers_rsp(MODBUS_ADDRESS,(modbus_rx.data[3]*2),hold_regs+modbus_rx.data[1]);
output_high(PIN_E1);
delay_ms(200);
output_low(PIN_E1);
delay_ms(200);
}
else
modbus_read_input_registers_rsp(MODBUS_ADDRESS,(modbus_rx.data[3]*2),input_regs+modbus_rx.data[1]);
event_count++;
}
break;
case FUNC_WRITE_SINGLE_COIL: //write coil
if(modbus_rx.data[0] || modbus_rx.data[3] || modbus_rx.data[1] > 8)
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
else if(modbus_rx.data[2] != 0xFF && modbus_rx.data[2] != 0x00)
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_VALUE);
else
{
//coils are stored msb->lsb so we must use 7-address
if(modbus_rx.data[2] == 0xFF)
bit_set(coils,modbus_rx.data[1]);
else
bit_clear(coils,modbus_rx.data[1]);
modbus_write_single_coil_rsp(MODBUS_ADDRESS,modbus_rx.data[1],((int16)(modbus_rx.data[2]))<<8);
event_count++;
}
break;
case FUNC_WRITE_SINGLE_REGISTER:
if(modbus_rx.data[0] || modbus_rx.data[1] >= 8)
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
else
{
//the registers are stored in little endian format
hold_regs[modbus_rx.data[1]] = make16(modbus_rx.data[3],modbus_rx.data[2]);
modbus_write_single_register_rsp(MODBUS_ADDRESS,
make16(modbus_rx.data[0],modbus_rx.data[1]),
make16(modbus_rx.data[2],modbus_rx.data[3]));
}
break;
case FUNC_WRITE_MULTIPLE_COILS:
if(modbus_rx.data[0] || modbus_rx.data[2] ||
modbus_rx.data[1] >= 8 || modbus_rx.data[3]+modbus_rx.data[1] > 8)
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
else
{
int i,j;
modbus_rx.data[5] = swap_bits(modbus_rx.data[5]);
for(i=modbus_rx.data[1],j=0; i < modbus_rx.data[1]+modbus_rx.data[3]; ++i,++j)
{
if(bit_test(modbus_rx.data[5],j))
bit_set(coils,7-i);
else
bit_clear(coils,7-i);
}
modbus_write_multiple_coils_rsp(MODBUS_ADDRESS,
make16(modbus_rx.data[0],modbus_rx.data[1]),
make16(modbus_rx.data[2],modbus_rx.data[3]));
event_count++;
}
break;
case FUNC_WRITE_MULTIPLE_REGISTERS:
if(modbus_rx.data[0] || modbus_rx.data[2] ||
modbus_rx.data[1] >= 8 || modbus_rx.data[3]+modbus_rx.data[1] > 8) modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
else
{
int i,j;
for(i=0,j=5; i < modbus_rx.data[4]/2; ++i,j+=2)
hold_regs[i] = make16(modbus_rx.data[j+1],modbus_rx.data[j]);
modbus_write_multiple_registers_rsp(MODBUS_ADDRESS,
make16(modbus_rx.data[0],modbus_rx.data[1]),
make16(modbus_rx.data[2],modbus_rx.data[3]));
event_count++;
}
break;
default: //We don't support the function, so return exception
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_FUNCTION);
}
}
}
}
KẾT QUẢ MÔ PHỎNG
Chú ý: Master và slave truyền thông qua chuẩn RS485, chương trình slave là như nhau chỉ khác ở địa chỉ Mô tả: Master đọc dữ liệu 2 slave và in ra màn hình
Chúc các bạn thành công với project này!
Thanks and Best Regards
Nguyễn Ngọc Qui
Automation Engineer
Email: quinguyentgvn@gmail.com
Phone: 0938 430 305
Thứ Ba, 12 tháng 11, 2013
Đăng ký:
Đăng Nhận xét (Atom)
Hello Engineer QUI,
Trả lờiXóaI interesetd with your project "PIC giao tiếp MODBUS "
did it really work in PROTEUS-ISIS !?
i try to compile your code bu it give a lot of errors...
Please could you share your compiled code & isis simulation with me?
trans-47@hotmail.com
Cảm ơn bạn
Best Regards