|
|
View previous topic :: View next topic |
Author |
Message |
LogicM
Joined: 23 Dec 2015 Posts: 2
|
PIC16F887 Modbus RTU Slave over RS485 |
Posted: Tue Jan 05, 2016 9:09 am |
|
|
Hello ALL,
I am very new to the forum and beginner. I got some code around this forum and used example Modbus slave code from CCS, modified to match with my controller. Finally I am trying to test a PIC 16F887 as Modbus RTU slave to communicate with a PLC through RS 485 for one of my simple application. Half duplex, used Max 485 at PIC side. The communication is ok, but when I try to read the data from PLC , I am getting MODBUS_RTU_SLAVE_ERROR 0x96. I am just trying to read single Holding registers and write an LED. Could anyone please help me to move further? Thank you all in advance! My code is here: I am using CCS compiler Version 5.052
Code: |
////////////////////////////////////////////////////////////////////////////////
//// Slave ////
//// ////
//////////////////////////////////////////////////////////////////////////////
//#define USE_WITH_PC 1
#include <16F887.h>
#fuses HS,NOWDT
#use delay(clock=16000000)
#ZERO_RAM
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#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
#define MODBUS_SERIAL_TX_PIN PIN_C6 // Data transmit pin
#define MODBUS_SERIAL_RX_PIN PIN_C7 // Data receive pin
#define MODBUS_SERIAL_ENABLE_PIN PIN_B1 // Controls DE pin for RS485
#define MODBUS_SERIAL_RX_ENABLE PIN_B1 // Controls RE pin for RS485
#include <modbus.c>
#define MODBUS_ADDRESS 0x01
#define LED_1 PIN_A3
#define HIGH_ADDRESS_READ_DATA1 0x00
#define LOW_ADDRESS_READ_DATA1 0x02
#define HIGH_ADDRESS_WRITE_LED_1 0x00
#define LOW_ADDRESS_WRITE_LED_1 0x05
#BYTE TRISA = 0x85
#BYTE TRISB = 0x86
#BYTE TRISC = 0X87
#BYTE PORTA = 0x05
#BYTE PORTB = 0x06
#BYTE PORTC = 0x07
#USE fast_io(A)
#USE fast_io(B)
#USE fast_io(C)
int8 data1=60;
/*This function may come in handy for you since MODBUS uses MSB first.*/
/*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()
{
set_tris_a(0b11110111);
set_tris_b(0b11011100);
set_tris_c(0b10111111);
port_b_pullups(true);
int8 coils = 0b00000101;
int8 inputs = 0b00001001;
int16 hold_regs[] = {0x8800,0x7700,0x6600,0x5500,0x4400,0x3300,0x2200,0x1100};
int16 input_regs[] = {0x1100,0x2200,0x3300,0x4400,0x5500,0x6600,0x7700,0x8800};
int16 event_count = 0;
setup_adc_ports(NO_ANALOGS);
output_low(LED_1);
delay_ms(500);
modbus_init();
while(TRUE)
{
data1=~data1+99;
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]== HIGH_ADDRESS_READ_DATA1) && (modbus_rx.data[1]==LOW_ADDRESS_READ_DATA1)){
modbus_read_discrete_input_rsp(MODBUS_ADDRESS,1,&data1);
}
else
{
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
}
break;
case FUNC_READ_HOLDING_REGISTERS:
if((modbus_rx.data[0]== HIGH_ADDRESS_READ_DATA1) && (modbus_rx.data[1]==LOW_ADDRESS_READ_DATA1)){
modbus_read_discrete_input_rsp(MODBUS_ADDRESS,1,&data1);
}
else
{
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
}
case FUNC_READ_INPUT_REGISTERS:
if((modbus_rx.data[0]== HIGH_ADDRESS_READ_DATA1) && (modbus_rx.data[1]==LOW_ADDRESS_READ_DATA1)){
modbus_read_discrete_input_rsp(MODBUS_ADDRESS,1,&data1);
}
else
{
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
}
break;
case FUNC_WRITE_SINGLE_COIL: //write coil
if((modbus_rx.data[0]== HIGH_ADDRESS_WRITE_LED_1) && (modbus_rx.data[1]==LOW_ADDRESS_WRITE_LED_1)){
if(modbus_rx.data[2] == 0xFF){
output_high(LED_1);
}
else{
output_low(LED_1);}
}
else{
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_DATA_ADDRESS);
}
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
{
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);
}
}
}
} |
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Tue Jan 05, 2016 10:05 am |
|
|
A number of things: first, you need to use the code button to keep the formatting of your code. It makes it much easier to read, like this:
Code: | #ZERO_RAM // Do you really need this? Probably not. Just included because someone else did maybe?
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA // This uses the hardware UART.
#define MODBUS_TYPE MODBUS_TYPE_SLAVE // Slave mode - fine, but you talk about talking to a PLC, which is also a slave, probably...
#define MODBUS_SERIAL_TYPE MODBUS_RTU //use MODBUS_ASCII for ASCII mode
// #define MODBUS_SERIAL_RX_BUFFER_SIZE 64 // Fine, the driver will default to this anyway.
#define MODBUS_SERIAL_BAUD 9600 // Good, its the commonest MODBUS speed.
// Don't define the serial pins. You've selected the hardware UART, and that's enough. Anything else can cause confusion.
//#define MODBUS_SERIAL_TX_PIN PIN_C6 // Data transmit pin
//#define MODBUS_SERIAL_RX_PIN PIN_C7 // Data receive pin
// Don't define both enables if you are connecting both to one pin. Just use the MODBUS_SERIAL_ENABLE_PIN only.
#define MODBUS_SERIAL_ENABLE_PIN PIN_B1 // Controls DE pin for RS485
// This may well be what's causing the problem.
//#define MODBUS_SERIAL_RX_ENABLE PIN_B1 // Controls RE pin for RS485
#include <modbus.c>
#define MODBUS_ADDRESS 0x01 \\ Are you sure this is okay? personally I'd select something less likely to already have been used...
#define LED_1 PIN_A3
#define HIGH_ADDRESS_READ_DATA1 0x00
#define LOW_ADDRESS_READ_DATA1 0x02
#define HIGH_ADDRESS_WRITE_LED_1 0x00
#define LOW_ADDRESS_WRITE_LED_1 0x05
// Don't try to use fast_io unless you really a) need it and b) know how it works.
// Let the compiler do it all for you - it will make fewer mistakes than you! Also, you can override the settings used by the MODBUS driver.
//#BYTE TRISA = 0x85
//#BYTE TRISB = 0x86
//#BYTE TRISC = 0X87
//#BYTE PORTA = 0x05
//#BYTE PORTB = 0x06
//#BYTE PORTC = 0x07
//#USE fast_io(A)
//#USE fast_io(B)
//#USE fast_io(C)
// Also don't use the set tris functions.
|
I do not know what MODBUS error 0x96 is, it is not a standard MODBUS error. It's probably specific to whatever master you are using to talk to the bus. So you need to look up in whatever documentation you have for it to find out what it means.
I don't like the 50us delay after checking modbus_kbhit(). MODBUS is timing dependant and, while I doubt 50us will have all that much of an effect, its possible it will cause problems. Why did you put it in?
I can't be sure you've not got some addressing problems with the read and write functions. The lack of formatting makes that code very hard to read.
I'm also going to assume you've got the hardware set up correctly, particularly the terminating and biassing resistors, not to mention the decoupling capacitors. Get any of that wrong, and it can be very difficult to debug. Personally, I would have gone with slower, lower power bus drivers, such as the MAX483, which can be rather more forgiving and easier to use. |
|
|
LogicM
Joined: 23 Dec 2015 Posts: 2
|
PIC16F887 Modbus RTU Slave over RS485 |
Posted: Thu Jan 07, 2016 1:17 pm |
|
|
Hi RF_Developer,
Thank you for your quick response and help! Sorry for the late reply, its took some time to get fixed everything..
Now its working fine, great support! Thanks Again! |
|
|
ngthuy1190
Joined: 07 Jan 2022 Posts: 1
|
Re: PIC16F887 Modbus RTU Slave over RS485 |
Posted: Fri Jan 07, 2022 3:05 am |
|
|
LogicM wrote: | Hi RF_Developer,
Thank you for your quick response and help! Sorry for the late reply, its took some time to get fixed everything..
Now its working fine, great support! Thanks Again! |
hi, i am also facing the same problem as you, can you share the finished code for me, thanks |
|
|
|
|
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
|