|
|
View previous topic :: View next topic |
Author |
Message |
micro_man
Joined: 05 Jun 2013 Posts: 14
|
sending coils data from master to slave |
Posted: Tue Jul 23, 2013 1:51 am |
|
|
Hi
i want to send coils data from modbus master to slave.
first i write the slave program and sends the data from modbus poll (using PC). it works well. the code for the slave is
Code: |
#include "16f870.h"
#fuses HS,NOLVP,NOWDT,NOBROWNOUT,PROTECT, PUT
#use delay(clock=20M)
#define MODBUS_TYPE MODBUS_TYPE_SLAVE
#define MODBUS_SERIAL_RX_BUFFER_SIZE 40
#define MODBUS_SERIAL_BAUD 19200
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7,parity=E, stream=MODBUS_SERIAL)
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define MODBUS_SERIAL_ENABLE_PIN PIN_C4 // Controls DE pin for RS485
#include "modbus.c"
#define MODBUS_ADDRESS 0x05
/*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()
{
int i,one_time=0;
int8 coils1 = 0b11111111;
int8 inputs = 0b11111111;
int poll=0;
setup_timer_0(RTCC_INTERNAL);
modbus_init();
while(1)
{
if(one_time==1)
{
one_time=0;
if(bit_test (coils1, 0)) output_bit(PIN_A0,1); else output_bit(PIN_A0,0);
if(bit_test (coils1, 1)) output_bit(PIN_A1,1); else output_bit(PIN_A1,0);
if(bit_test (coils1, 2)) output_bit(PIN_B4,1); else output_bit(PIN_B4,0);
if(bit_test (coils1, 3)) output_bit(PIN_B5,1); else output_bit(PIN_B5,0);
}
poll++;
if(modbus_kbhit()!=1)
{
}
else
{
poll=0;
//check address against our address, 0 is broadcast
if((modbus_rx.address == MODBUS_ADDRESS) || modbus_rx.address == 0)
{
switch(modbus_rx.func)
{
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(coils1,7-i);
else
bit_clear(coils1,7-i);
}
one_time=1;
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]));
}
break;
default: //We don't support the function, so return exception
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_FUNCTION);
}
}
}
} //while
} //main
|
Then i write the program for master as follows.
Code: |
#include "16f870.h"
#fuses HS,NOLVP,NOWDT,NOBROWNOUT,PROTECT, PUT
#use delay(clock=20M)
#define MODBUS_TYPE MODBUS_TYPE_MASTER
#define MODBUS_SERIAL_TYPE MODBUS_RTU
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 19200
#define MODBUS_SERIAL_TIMEOUT 10000
#USE RS232(BAUD=19200, XMIT=PIN_C6, RCV=PIN_C7, PARITY=E, STREAM=MODBUS_SERIAL)
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define MODBUS_SERIAL_ENABLE_PIN PIN_C5
#include "C:\Documents and Settings\Administrator\Desktop\testing\modbus.c"
#define MODBUS_SLAVE_ADDRESS 0x05
void main()
{
modbus_init();
unsigned int8 coils=0x10,i=0;
while(1)
{
if(!(modbus_write_multiple_coils(MODBUS_SLAVE_ADDRESS,0,8,coils)))
{
for(i=0;i<(modbus_rx.len);++i)
{
}
}
delay_ms(500);
}
} |
i want to know that is this program is correct? when i run the programs, some leds on the slave side ON/OFF. Kindly tell me if i am somewhere wrong.
Actually i want to send multiple coils from master to slave but initially i am sending one coil to the slave to understand the operation. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Tue Jul 23, 2013 3:37 am |
|
|
You don't need to have a #use rs232 for the serial port used for modbus. modbus.c does that for you, and it's definition will override yours.
Code: |
// This is wrong.
if(!(modbus_write_multiple_coils(MODBUS_SLAVE_ADDRESS,0,8,coils)))
// You need to give a pointer to the "coils" like this:
if(!(modbus_write_multiple_coils(MODBUS_SLAVE_ADDRESS,0,8,&coils)))
|
There may be other problems, but I've not tried to compile nor run your code.
In the slave it's better to implement all of the commands relating to each data type rather than just the one you expect to use. In this case, FUNC_READ_COILS, FUNC_WRITE_SINGLE_COIL, as well as just FUNC_WRITE_MULTIPLE_COILS. This will allow you to read back the coil/s you've just set. Good for diagnostics and for understanding.
Also be aware that for simplicity the example code implements eight of each data type. Its up to you to extend that to suit your application. |
|
|
micro_man
Joined: 05 Jun 2013 Posts: 14
|
|
Posted: Tue Jul 23, 2013 5:40 am |
|
|
when i change the coil variable manually.it send the data to slave
e.g unsigned int8 coils=0x01,unsigned int8 coils=0x02,unsigned int8 coils=0x03 slave get those values.
But i want to do it automatically by incrementing the variable like
coils=coils+1; but this is not working
Code: |
void main()
{
modbus_init();
unsigned int8 coils=0x01,i=0;
while(1)
{
if(!(modbus_write_multiple_coils(MODBUS_SLAVE_ADDRESS,0,8,&coils)))
{
for(i=0;i<(modbus_rx.len);++i)
{
}
}
coils=coils + 1;
delay_ms(500);
}
}
|
|
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Tue Jul 23, 2013 9:03 am |
|
|
I may be wrong, but I thought that you cannot pass a local variable by reference?
Try making your coils variable either a global, or a static local. |
|
|
micro_man
Joined: 05 Jun 2013 Posts: 14
|
|
Posted: Wed Jul 24, 2013 2:05 am |
|
|
I look into the modbus driver file and found that data is send in terms of an array e.g values[i]
Code: |
exception modbus_write_multiple_coils(int8 address, int16 start_address, int16 quantity,
int8 *values)
{
int8 i,count;
count = (int8)((quantity/8));
if(quantity%8)
count++;
modbus_serial_send_start(address, FUNC_WRITE_MULTIPLE_COILS);
modbus_serial_putc(make8(start_address,1));
modbus_serial_putc(make8(start_address,0));
modbus_serial_putc(make8(quantity,1));
modbus_serial_putc(make8(quantity,0));
modbus_serial_putc(count);
for(i=0; i < count; ++i)
modbus_serial_putc(values[i]);
modbus_serial_send_stop();
MODBUS_SERIAL_WAIT_FOR_RESPONSE();
return modbus_rx.error;
} |
so i change my variable to an array but it is still not working
Code: | void main()
{
unsigned int8 coils[1]={0x01},i=0;
modbus_init();
while(1)
{
if(!(modbus_write_multiple_coils(MODBUS_SLAVE_ADDRESS,0,8,coils)))
{
for(i=0;i<(modbus_rx.len);++i)
{
}
}
coils[0]=coils[0] + 1;
if(coils[0]>8)
{
coils[0]=1;
}
delay_ms(500);
}
} |
But the code is not working yet. Someone tell me where i am wrong? |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Wed Jul 24, 2013 2:50 am |
|
|
Your posts don't manage to report a problem clearly.
What is "not working"? No response from slave, error message from slave? Send MODBUS data correct/not
correct when watched with a monitor tool? etc... |
|
|
micro_man
Joined: 05 Jun 2013 Posts: 14
|
|
Posted: Wed Jul 24, 2013 3:43 am |
|
|
The problem is slave not responding.
I have some leds on the slave side. When master will send data to the slave, according to the coil bits leds will be on and off.
Firstly i write a program for slave and test it with modbus poll software (on computer). It was working fine.
Then i write a program for master and connect it with the slave, but i am not getting results as i was getting with the master (modbus poll).
But i am not sure about my master program. Is it the right way to send the data to slave or not? |
|
|
micro_man
Joined: 05 Jun 2013 Posts: 14
|
|
Posted: Thu Jul 25, 2013 1:17 am |
|
|
guys any idea... |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jul 25, 2013 1:51 pm |
|
|
I say: KISS
There are many things where your code can go wrong. If it is not working as expected then you have to make things simpler. Create the most basic MODBUS connection you can think of and get this working, then expand from what is working. When it fails again, like in your case, then you have to go back again to what is working and make an even smaller step in adding functionality.
Best if you get yourself an In Circuit Debugger so you can set breakpoints and step through the code, this will save you so much time now and in the future.
Without ICD you have to add many debug output commands so you can see what is happening. Preferably write debug texts to a serial port or LCD. If you don't have these output devices then use LEDs in different output combinations.
You are there sitting behind the real hardware setup, that means you can do all the experiments. We from our computer can only read your program and guess.
One thing that worries me is that in your slave you have: Code: | 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(coils1,7-i);
else
bit_clear(coils1,7-i);
}
| The swap macro reverses all bit in the bytes, but then in the for-loop you do again a reverse. Perhaps this is good, I don't know but it is complicating things.
I would remove all code from the slave, except the code for receiving a message. Then on every message I would toggle one LED. Don't do fancy things with 4 LEDs.
O yes, one other thing: your master is now counting 1 - 7. Even when things are working as you want them to, then only 3 LEDs would be activated in the slave and not all 4. |
|
|
micro_man
Joined: 05 Jun 2013 Posts: 14
|
|
Posted: Mon Jul 29, 2013 2:29 am |
|
|
i simplify the program as follows
MASTER
Code: |
#include "16f870.h"
#fuses HS,NOLVP,NOWDT,NOBROWNOUT,PROTECT, PUT
#use delay(clock=20M)
#define MODBUS_TYPE MODBUS_TYPE_MASTER
#define MODBUS_SERIAL_TYPE MODBUS_RTU
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 19200
#define MODBUS_SERIAL_TIMEOUT 10000
#USE RS232(BAUD=19200, XMIT=PIN_C6, RCV=PIN_C7, PARITY=E, STREAM=MODBUS_SERIAL)
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define MODBUS_SERIAL_ENABLE_PIN PIN_C5
#include "C:\Documents and Settings\Administrator\Desktop\testing\modbus.c"
#define MODBUS_SLAVE_ADDRESS 0x05
void main()
{
unsigned int8 coils=0x02,i=0;
int1 a=0;
modbus_init();
while(1)
{
modbus_write_multiple_coils(MODBUS_SLAVE_ADDRESS,0,8,&coils);
{
for(i=0;i<(modbus_rx.len);++i)
{
}
}
coils=coils + 1;
if(coils>8)
{
coils=1;
}
delay_ms(500);
}
}
|
SLAVE
Code: |
#include <18F26K22.h>
#FUSES HSH,NOPLLEN,NOWDT,PUT,NOLVP
#use delay(clock=20M)
#use rs232(baud=9600,xmit=PIN_B6,rcv=PIN_B7, stream=gsm)
#define MODBUS_TYPE MODBUS_TYPE_SLAVE
#define MODBUS_SERIAL_RX_BUFFER_SIZE 40
#define MODBUS_SERIAL_BAUD 19200
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7,parity=E, stream=MODBUS_SERIAL)
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define MODBUS_SERIAL_ENABLE_PIN PIN_C5 // Controls DE pin for RS485
#include "C:\Documents and Settings\Administrator\Desktop\testing\modbus_hay.c"
#define MODBUS_ADDRESS 0x05
/*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()
{
int one_time=0;
int8 coils1 = 0b11111111;
int8 inputs = 0b11111111;
int poll=0;
setup_timer_0(RTCC_INTERNAL);
modbus_init();
fprintf(gsm," testing ");
while(1)
{
if(modbus_kbhit()!=1)
{
}
else
{
//check address against our address, 0 is broadcast
if((modbus_rx.address == MODBUS_ADDRESS) || modbus_rx.address == 0)
{
switch(modbus_rx.func)
{
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);
fprintf(gsm,"\r\n *");
fprintf(gsm," %x ",modbus_rx.address);
fprintf(gsm," %x ",modbus_rx.data[0]);
fprintf(gsm," %x ",modbus_rx.data[1]);
fprintf(gsm," %x ",modbus_rx.data[2]);
fprintf(gsm," %x ",modbus_rx.data[3]);
fprintf(gsm," %x \r\n",modbus_rx.data[5]);
}
else
{
//int i,j;
modbus_rx.data[5] = swap_bits(modbus_rx.data[5]);
//fprintf(gsm," %x ",modbus_rx.data[5]);
fprintf(gsm," %x ",modbus_rx.address);
fprintf(gsm," %x ",modbus_rx.data[0]);
fprintf(gsm," %x ",modbus_rx.data[1]);
fprintf(gsm," %x ",modbus_rx.data[2]);
fprintf(gsm," %x ",modbus_rx.data[3]);
fprintf(gsm," %x ",modbus_rx.data[5]);
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]));
}
break;
default: //We don't support the function, so return exception
modbus_exception_rsp(MODBUS_ADDRESS,modbus_rx.func,ILLEGAL_FUNCTION);
}
}
}
} //while
} //main
|
i added a serial port at slave side to see what data is coming and it shows an error here.
first time when i program the master and run it the slave receive data like
fprintf(gsm," %x ",modbus_rx.address);= 05 correct
fprintf(gsm," %x ",modbus_rx.data[0]);= 00 correct (starting address H)
fprintf(gsm," %x ",modbus_rx.data[1]);= 00 CORRECT (starting address L)
fprintf(gsm," %x ",modbus_rx.data[2]);= 00 correct (quantity H)
fprintf(gsm," %x ",modbus_rx.data[3]);= 08 correct (quantity L)
fprintf(gsm," %x ",modbus_rx.data[5]);= 40 wrong (data)
then
fprintf(gsm," %x ",modbus_rx.address);= 05 correct
fprintf(gsm," %x ",modbus_rx.data[0]);= 00 correct (starting address H)
fprintf(gsm," %x ",modbus_rx.data[1]);= 01 WRONG (starting address L)
fprintf(gsm," %x ",modbus_rx.data[2]);= 00 correct (quantity H)
fprintf(gsm," %x ",modbus_rx.data[3]);= 08 correct (quantity L)
fprintf(gsm," %x ",modbus_rx.data[5]);= 01 correct (data)
i think this address is a problem.but why i am getting it as master is fixed?
guys tell me about this.what i should check further? |
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Mon Jul 29, 2013 3:51 am |
|
|
Quote: | /*This function may come in handy for you since MODBUS uses MSB first.*/ |
Modbus uses ms BYTE first, not ms BIT first. You may need to swap BYTES, but never need to swap BITS. |
|
|
|
|
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
|