|
|
View previous topic :: View next topic |
Author |
Message |
borseyogesh1436
Joined: 30 Nov 2017 Posts: 29
|
modbus RTU with pic18f46k22 write error |
Posted: Thu Nov 30, 2017 3:08 am |
|
|
Hi friends. I am working with CCS modbus slave with pic18f46k22 that sends data to modbus poll software but it will send wrong values. What is the problem with my code ? Can anyone help me to solve that thing ?
slave id - 02
read holding register 03
I want to know the address and length.
tx :- 02 03 00 00 00 02 C4 38
rx :- 09 F3 02 C6 05 13 09 F3 02
tx :- 02 03 00 00 00 02 C4 38
rx :- 13 09 F3 02 C6 05 13 09 F3
Code: |
#include <18LF46K22.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(crystal=7372800MHz)
#use delay(clock=7.372800MHz)
#use rs232(baud=9600,parity=N,xmit=Pin_c6,rcv=Pin_c7,enable=Pin_a3,bits=8, errors,stream=MODBUS_SERIAL)
#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_TX_PIN PIN_C6 // Data transmit pin
#define MODBUS_SERIAL_RX_PIN PIN_C7 // Data receive pin
//The following should be defined for RS485 communication
#define MODBUS_SERIAL_ENABLE_PIN PIN_A3 // Controls DE pin for RS485
#define MODBUS_SERIAL_RX_ENABLE PIN_A3 // Controls RE pin for RS485
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#include <modbus.c>
#define MODBUS_ADDRESS 0x02
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()
{
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;
modbus_init();
while(TRUE)
{
input_regs[0]=0xAA;
while(!modbus_kbhit())
{
if((modbus_rx.address == MODBUS_ADDRESS) || modbus_rx.address == 0)
{
switch(modbus_rx.func)
{
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]);
else
modbus_read_input_registers_rsp(MODBUS_ADDRESS,(modbus_rx.data[3]*2),input_regs+modbus_rx.data[1]);
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: Mon Dec 04, 2017 8:21 am |
|
|
There's a number of things going wrong here, both in the firmware and in the hardware. Also there's a problem with the master end code.
Quote: |
tx :- 02 03 00 00 00 02 C4 38
rx :- 09 F3 02 C6 05 13 09 F3 02
tx :- 02 03 00 00 00 02 C4 38
rx :- 13 09 F3 02 C6 05 13 09 F3 |
The first command (tx) is a read holding registers (03) command to Modbus device address 2. It requests two (0002) holding registers starting at register 0 (0000), in other words regisers 0 and 1. C4 38 is the Modbus checksum.
The response (rx) is a repeated exception, 02 C6 05 13 09 F3, but as the master is expecting the eight(nine including device address) bytes of the read holding registers reply and cannot cope with modbus exceptions, it's getting out of sync and the replies make no sense. The master should be able to deal with exception replies, and clearly it can't.
I am not even sure the exception code is coming from the PIC. Many implementations of Modbus include exceptions generated in the PC driver code. I use NModbus with C# which does generate such internal exceptions. I say this as the exception code, C2, or 32 (the top bit is always set on exceptions, the code is the byte with the top bit masked out) is not is the range of codes generated by slaves.
It is important that your RS485 hardware is properly terminated and biased. If not it might not work at all, or worse work some of the time and fail at others with seemingly random unexpected characters between bursts of other wise valid Modbus traffic. I suspect this may be the main problem and the exception is the master saying it's seeing illegal characters/malformed messages. It may even be that the master has not opened the port to the slave! You will need to look up the exception codes for your master end software.
There are problems with your set-up code. You have two #use delay()s. The first is invalid, attempting to set an impossible clock frequency. Only the second is needed, delete the first.
modbus.c sets up the serial port for you. It will override any #use rs232() that you provide. Delete it. The selection of which port is it uses is with #define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA which selects the first hardware serial port. Remove your defines for the TX and RX pin.
Unless you have connected RE and DE to different pins, use only: #define MODBUS_SERIAL_ENABLE_PIN PIN_A3 // Controls enable pins for RS485. Delete the define of MODBUS_SERIAL_RX_ENABLE.
#ifndef needs to have a corresponding #endif but you don't need this as you will always need an enable pin defined.
Code: | while (!modbus_kbhit()) | is incorrect, it should be Code: | while (modbus_kbhit()) | or even better Code: | if (modbus_kbhit()) | Your code is trying to read a Modbus message only when there isn't one, and so it will be sending back an error response over and over again. The example code has Code: | while (!modbus_kbhit()); | That semi-colon is important as it makes the code wait in a tight loop until a message is received. That's fine (and very different to your code) provided the main loop doesn't have to do anything else. With any other code, instead of waiting until there a command arrives, you need to check if there's a command to process and if not, do other stuff.
Modbus relies on fairly precise timing to work correctly. It uses a timer, timer 2, and interrupts to implement the Modbus protocol. Do not try to use timer 2 for anything else. Also make sure your main loop loops round fairly quickly - no long delays - as otherwise Modbus code will get confused and my miss commands.
With these changes your set-up needs to look more like this:
Code: |
#use delay(clock=7.372800MHz)
#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_ENABLE_PIN PIN_A3 // Controls DE & RE pin for RS485
// Tells modbus.c to use first hardware serial port.
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#include <modbus.c>
#define MODBUS_ADDRESS 0x02
|
I cannot guaranteee it will work with the above firmware modifications. I suspect there are problems in the hardware and the master software. |
|
|
borseyogesh1436
Joined: 30 Nov 2017 Posts: 29
|
|
Posted: Mon Dec 04, 2017 10:47 pm |
|
|
Thanks for reply RF_Developer.
Thats my first time to use CCS, the code is working as follows
MODBUS POLL communication traffic
Tx:- 02 03 00 00 00 01 84 39 //Read error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Rx:- 02 03 02 00 64 FD AF //CRC error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Rx:- 02 03 02 00 64 FD AF //CRC error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Tx:- 02 03 00 00 00 01 84 39 //Read error
Rx:- 02 03 02 00 64 FD AF //CRC error
Transmitting and receiving leds are blinking one by one of rs485 to usb convertor.
Modbus POLL setting
https://drive.google.com/file/d/1ABO3OOKjGDiVtbXxUz4imB3aHE3pmCJF/view?usp=sharing
The hardware is tested and its working fine. I think there is a problem in modbus poll setting in address and length.
Code: |
#include <18LF46K22.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(crystal=7.372800MHz)
#use delay(clock=7.372800MHz , crystal=7.372800MHz)
#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_RDA
#define MODBUS_SERIAL_TX_PIN PIN_D6 // Data transmit pin
#define MODBUS_SERIAL_RX_PIN PIN_D7 // Data receive pin
//The following should be defined for RS485 communication
#define MODBUS_SERIAL_ENABLE_PIN PIN_A3 // Controls DE pin for RS485
#define MODBUS_SERIAL_RX_ENABLE PIN_A3 // Controls RE pin for RS485
#endif
#include <modbus.c>
#define MODBUS_ADDRESS 0x02
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[] = {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);
modbus_init();
while(TRUE)
{
hold_regs[0]=100;
//! input_regs[1]=0xAA;
//! input_regs[2]=0xAA;
//! hold_regs[0]=0x55;
//! hold_regs[1]=0x55;
//! hold_regs[2]=0x55;
while(!modbus_kbhit());
//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]);
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
{
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
{
hold_regs[modbus_rx.data[1]] = make16(modbus_rx.data[2],modbus_rx.data[3]);
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],modbus_rx.data[j+1]);
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);
}
}
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 05, 2017 12:04 am |
|
|
The CCS sample code, ex_modbus_slave.c, also has a delay of 50 usec
after the test for kbhit. You're missing that delay. See below for the
location of the delay statement:
Code: | setup_adc_ports(NO_ANALOGS);
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)
{ |
|
|
|
borseyogesh1436
Joined: 30 Nov 2017 Posts: 29
|
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Tue Dec 05, 2017 5:21 am |
|
|
How much of what I wrote did you understand? Vey little, apparently. You have changed a lot fo the code, but very few of the changes are to things I pointed out.
You must have only one #use delay line. The #use delay(crystal=7.372800MHz) should work.
Despite you saying the hardware is working, there is still some problem as the PIC is only responding to some commands. Your Modbus Poll set up uses 2 stop bits. The PIC code only uses 1 stop bit. Please change your Modbus Poll set up to 1.
The #ifndef USE_WITH_PC and corresponding #endif is from modbus_
slave_ex.c and is not needed in your code. At the moment, that code is not ued as you have not defined #USE_WITH_PC. I think you should have the defines, but they should not be conditional. If you do not understand what that means then look up and read about conditional compilation in C.
The 50us delay in the example as mentioned by PCM is not required. It will just slow down your code. All the required delays are provided by modbus.c.
The response you get, on the occasions it is sent, appears sensible. I cannot say if the CRC is correct, but I do not know of any problem with the CRC in CCS's Modbus code. It works fine for me.
Modbus register addressing is very confusing. Some documentation sugggests that each type of register is in its own address range: for example holding registers are 40001 to 49999. However the addresses actually sent on the bus are 0-9998 (i.e. one less) and correspond conveniently to C array indexes. In fact each type of item, holding regs, input regs etc, have their own address range from 0 to 65535. this means that the command sent by Modbus Poll is correct, to read holding register 1 (or 40001) it sends start address 0 (0000) and asks for 1 register (0001). The slave should send back 02 (unit address) 03 (function code) 02 (number of bytes) and then the content of register 1 (C array index 0) which in your case is 0x0064 which is the hexadecimal form of decimal 100, which is the avalue you set it to at the start of your main. Therefore it is sending back the correct data, or at least the data it was asked for.
Yes, the LEDs will blink one by one on the convertor. The RS485 bus is half-duplex (if you don't know what that means, look it up) and will only be in one state, transmission or reception at a time. If the RX LED lights but Modbus Poll doesn't show anything received, then that's a clear sign that something was sent that Modbus Poll couldn't understand. Sort out those stop bits.
I cannot say whether the CRC is correct or not, but I think the confusion over stop bits (should be 1) is causing problems, and there may still be biassing and termination issues on the RS485 bus. Please show your circuit for the bus connections.
Another thing, your code says you are using a PIC18LF46K22. The LF is intended for lower voltages, typically 3.3V. A Modbus system is usually 5V, and so I'd expect to see a PIC18F46K22. |
|
|
borseyogesh1436
Joined: 30 Nov 2017 Posts: 29
|
|
|
|
|
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
|