CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

(Help Me!) MODBUS RTU RS-485 Master-Slave
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

(Help Me!) MODBUS RTU RS-485 Master-Slave
PostPosted: Mon Jun 03, 2019 6:40 pm     Reply with quote

Hi! I'm developing a project in which I have two devices:

1. PIC18F4550 (Master);
2. WEG CFW500 Variable Speed Drive (Slave).

I need the PIC18F4550 to read some Variable Speed Drive holding registers.

I'm programming in C language and using the PIC C Compiler (CCS).

I'll attach a diagram of how the physical part will look.

I will use a MAX485 transceiver (although the model of the diagram is the MAX487) for the communication medium.

The crystal of the microcontroller will be 20MHz.

I'm using a 20x4 LM044L LCD display.

I need to display the value of (a):

Motor Current;
Motor Frequency;
Motor Acceleration Time;
Motor Deceleration Time.

I've already started programming in CCS, I'm using just two libraries, the standard CCS LCD library and the MODBUS library.

The CFW500 uses parameters, each parameter corresponds to a register that stores some data, for example:

P0003 = motor current;
P0005 = motor frequency;
P0100 = motor acceleration time;
P0101 = motor deceleration time.

I know to read some register I should use the command from function 3:

modbus_read_holding_registers (address of the slave, address of the initial registrar, number of registers to read);

Function 3 is used to read the retention registers.

I set the slave address to 1.

So, if I just want to read the frequency value for example, I convert 0005 (frequency parameter) to hexadecimal and it stays:

modbus_read_holding_registers (0x01,0x05,0x01); //read the slave retention registers at address 1; read register 5 and only 1 register

But I'm not sure how to display the reading result on the display, could anyone please help me? Any missing detail I answer. Thank you! Smile

Update: I created a variable type int and said that it would equal the read value of the register, then that variable would be displayed on the LCD display, but I always get the value 12, does anyone know what I am doing wrong?

CCS Code:

Code:

//Master

#include <18f4550.h>
#device *=16
#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=20M)

#define MODBUS_BUS SERIAL
#define MODBUS_TYPE MODBUS_TYPE_MASTER
#define MODBUS_SERIAL_TYPE MODBUS_RTU
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_EXT
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 9600
#define MODBUS_REMOTE_ADDRESS 1
#define MODBUS_SERIAL_RX_ENABLE PIN_C6                                      //pino RE RS-485
#define MODBUS_SERIAL_ENABLE_PIN PIN_C7                                     //pino DE RS-485

#include "modbus.c"
#include "LCD.C"

#define MODBUS_SLAVE_ADDRESS 0x01                                           //define o endereço do escravo

int ler;

void main() {
     
     modbus_init();
     lcd_init();
     
     while(TRUE) {
          ler = modbus_read_holding_registers(0x01,0x05,0x01);              //lendo os registradores de retenção
          printf(lcd_putc,"%u",ler);
          delay_ms(3000);
     }
}


Diagram: https://www.flickr.com/photos/166432267@N08/47997163758/in/dateposted-public/


Last edited by José Aparecido on Tue Jun 04, 2019 10:36 am; edited 3 times in total
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Mon Jun 03, 2019 7:29 pm     Reply with quote

EDIT!!!: Before you do any of this, I just noticed that you have wired your UART pins to the MAX485's receiver enable and data enable pins... That is incorrect! They need to be wired to the RO and DI pins (I believe it should be TX -> DI and RX -> RO).
Then, you need to wire two of your GPIO pins to the DE and RE pins, and specify that. Check ex_modbus_master.c to learn how to configure the driver to use RS485.

I don't have the CCS compiler on this computer, but ex_modbus_master.c has some wrapper functions with examples on how to use the modbus functions included in the library.

The first thing you have to do is make sure you have the right baud rate. You should write some code to blink LED at 1 Hz to confirm that you have done your clock setups correctly. Then, you need to initialize the modbus driver using modbus_init().

When you open ex_modbus_master.c, do a search for read_all_holding().

I modified that function so that I can input as parameters:
slave address
register to start reading
number of registers to read
pointer to array where I can store the data

Instead of printing the data, I modified that line to place each element in the modbus_rx.data[] array.

Keep in mind that this modbus_rx.data is an array of int8's, and each register is 16-bits so each pair of bytes that you read is one element of data. I used the union data structure to conveniently access this. But ALSO, keep in mind that while it prints fine if you are doing it like the CCS example, the bytes are swapped when you store them in array.

So in your function, you will need to swap element 0 with element 1, element 2 with element 3, and so on and so forth...

However, the best way to figure it out is to play with the code, starting with printing your values as shown by the CCS example code, and then moving on to storing it in your own data structures and so on.
Ttelmah



Joined: 11 Mar 2010
Posts: 19549

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 1:38 am     Reply with quote

You are also not telling the modbus driver what protocol to support

#define MODBUS_SERIAL_TYPE MODBUS_ASCII

or RTU.....

Then you need to call 'modbus_init' before trying to use the modbus
functions.

Then after you have called _one_ read_holding_registers operation,
you need to look at/use the reply, which is in the modbus_rx.data array.
How much data, is defined by modbus_rx.len. Dong 4 sequential reads will
have overwritten the values from the earlier reads.
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 10:07 am     Reply with quote

dluu13 wrote:
EDIT!!!: Before you do any of this, I just noticed that you have wired your UART pins to the MAX485's receiver enable and data enable pins... That is incorrect! They need to be wired to the RO and DI pins (I believe it should be TX -> DI and RX -> RO).
Then, you need to wire two of your GPIO pins to the DE and RE pins, and specify that. Check ex_modbus_master.c to learn how to configure the driver to use RS485.

I don't have the CCS compiler on this computer, but ex_modbus_master.c has some wrapper functions with examples on how to use the modbus functions included in the library.

The first thing you have to do is make sure you have the right baud rate. You should write some code to blink LED at 1 Hz to confirm that you have done your clock setups correctly. Then, you need to initialize the modbus driver using modbus_init().

When you open ex_modbus_master.c, do a search for read_all_holding().

I modified that function so that I can input as parameters:
slave address
register to start reading
number of registers to read
pointer to array where I can store the data

Instead of printing the data, I modified that line to place each element in the modbus_rx.data[] array.

Keep in mind that this modbus_rx.data is an array of int8's, and each register is 16-bits so each pair of bytes that you read is one element of data. I used the union data structure to conveniently access this. But ALSO, keep in mind that while it prints fine if you are doing it like the CCS example, the bytes are swapped when you store them in array.

So in your function, you will need to swap element 0 with element 1, element 2 with element 3, and so on and so forth...

However, the best way to figure it out is to play with the code, starting with printing your values as shown by the CCS example code, and then moving on to storing it in your own data structures and so on.


What is GPIO? Thanks!
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 10:08 am     Reply with quote

Ttelmah wrote:
You are also not telling the modbus driver what protocol to support

#define MODBUS_SERIAL_TYPE MODBUS_ASCII

or RTU.....

Then you need to call 'modbus_init' before trying to use the modbus
functions.

Then after you have called _one_ read_holding_registers operation,
you need to look at/use the reply, which is in the modbus_rx.data array.
How much data, is defined by modbus_rx.len. Dong 4 sequential reads will
have overwritten the values from the earlier reads.


I update the code, but what is wrong?
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jun 04, 2019 10:34 am     Reply with quote

GPIO = general purpose input/output

You need to fix your hardware first. Going on without the proper hookups guarantees that you have no chance of getting it working.

So before you start playing any more with the code, you should update your wiring schematic and confirm that it is correct.

It should go like this:
PIC-----------------MAX485
PIN_C7 (RX)-------RO
free IO pin---------RE
free IO pin---------DE
PIN_C6 (TX)-------DI

Here's a picture of a hookup on MSP430. It's not exactly what you need to do, but it gives the general idea.


Last edited by dluu13 on Tue Jun 04, 2019 10:45 am; edited 1 time in total
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 10:44 am     Reply with quote

dluu13 wrote:
GPIO = general purpose input/output

You need to fix your hardware first. Going on without the proper hookups guarantees that you have no chance of getting it working.

So before you start playing any more with the code, you should update your wiring schematic and confirm that it is correct.

It should go like this:
PIC_________MAX485
PIN_C7-------RO
free IO pin---RE
free IO pin---DE
PIN_C6-------DI


Okay, I already fixed a pinout as you told me!
I created a variable type int and said that it would equal the read value of the register, then that variable would be displayed on the LCD display, but I always get the value 12, do you know what I am doing wrong?
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jun 04, 2019 10:49 am     Reply with quote

Can you show your code? You need to show the full setup, where you are trying to read the Modbus register, and where you try and store the incoming data. Try and write the most minimal code you can to demonstrate the problem.
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 10:51 am     Reply with quote

dluu13 wrote:
Can you show your code? You need to show the full setup, where you are trying to read the Modbus register, and where you try and store the incoming data. Try and write the most minimal code you can to demonstrate the problem.


Here:

Quote:

//Master

#include <18f4550.h>
#device *=16
#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=20M)

#define MODBUS_BUS SERIAL
#define MODBUS_TYPE MODBUS_TYPE_MASTER
#define MODBUS_SERIAL_TYPE MODBUS_RTU
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_EXT
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 9600
#define MODBUS_REMOTE_ADDRESS 1
#define MODBUS_SERIAL_RX_ENABLE PIN_C5 //pino RE RS-485
#define MODBUS_SERIAL_ENABLE_PIN PIN_C4 //pino DE RS-485

#include "modbus.c"
#include "LCD.C"

#define MODBUS_SLAVE_ADDRESS 0x01 //define o endereço do escravo

int ler;

void main() {

modbus_init();
lcd_init();

while(TRUE) {
ler = modbus_read_holding_registers(0x01,0x05,0x01); //lendo os registradores de retenção
printf(lcd_putc,"%u",ler);
delay_ms(3000);
}
}


Confused
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jun 04, 2019 11:07 am     Reply with quote

I notice a few things:
1. your DE and RE pins are still set to your TX and RX pins in your code. You need to set the DE and RE pins to what they are connected to, and also tell the modbus driver that you are using C7 as the RO pin and C6 as the DI pin.

2. Your interrupt source is set at INT_EXT. You should use MODBUS_INT_RDA as your interrupt source for this

3. modbus_read_holding_registers() does not return the value that it reads. It returns whether or not there was an exception in reading modbus. In your case, it is returning 12, meaning there was a time out. You can find the definition of these error codes in the driver source code.
The data it reads is stored global modbus_rx struct. In that struct, there is the .data member variable which is a pointer to an array. You need to read the modbus_rx.data array to get your data out.

Have you gone to the CCS examples folder to find ex_modbus_master.c? You need to study and understand at least line 70 to 94 in that code to do the setup properly. Notice how they specify the interrupt source. Also notice how you need to define MODBUS_SERIAL_ENABLE_PIN and MODBUS_RX_SERIAL_ENABLE if you want to use RS485.
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 11:12 am     Reply with quote

dluu13 wrote:
I notice a few things:
1. your DE and RE pins are still set to your TX and RX pins in your code. You need to set the DE and RE pins to what they are connected to, and also tell the modbus driver that you are using C7 as the RO pin and C6 as the DI pin.

2. Your interrupt source is set at INT_EXT. You should use MODBUS_INT_RDA as your interrupt source for this

3. modbus_read_holding_registers() does not return the value that it reads. It returns whether or not there was an exception in reading modbus. In your case, it is returning 12, meaning there was a time out. You can find the definition of these error codes in the driver source code.
The data it reads is stored global modbus_rx struct. In that struct, there is the .data member variable which is a pointer to an array. You need to read the modbus_rx.data array to get your data out.

Have you gone to the CCS examples folder to find ex_modbus_master.c? You need to study and understand at least line 70 to 94 in that code to do the setup properly. Notice how they specify the interrupt source. Also notice how you need to define MODBUS_SERIAL_ENABLE_PIN and MODBUS_RX_SERIAL_ENABLE if you want to use RS485.


I'm studying ex_modbus_master.c right now. Sorry for the mistakes, I'm learning and correcting, thanks!
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jun 04, 2019 11:16 am     Reply with quote

One thing: I say that you should use MODBUS_INT_RDA for this because I notice that you are using C6 and C7, which are the hardware UART pins. If you really intend to use MODBUS_INT_EXT then you should wire the incoming (RX) pin into the INT_EXT pin of your PIC. Otherwise you need to define:
MODBUS_SERIAL_TX_PIN
MODBUS_SERIAL_RX_PIN
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 11:30 am     Reply with quote

dluu13 wrote:
I notice a few things:
1. your DE and RE pins are still set to your TX and RX pins in your code. You need to set the DE and RE pins to what they are connected to, and also tell the modbus driver that you are using C7 as the RO pin and C6 as the DI pin.

2. Your interrupt source is set at INT_EXT. You should use MODBUS_INT_RDA as your interrupt source for this

3. modbus_read_holding_registers() does not return the value that it reads. It returns whether or not there was an exception in reading modbus. In your case, it is returning 12, meaning there was a time out. You can find the definition of these error codes in the driver source code.
The data it reads is stored global modbus_rx struct. In that struct, there is the .data member variable which is a pointer to an array. You need to read the modbus_rx.data array to get your data out.

Have you gone to the CCS examples folder to find ex_modbus_master.c? You need to study and understand at least line 70 to 94 in that code to do the setup properly. Notice how they specify the interrupt source. Also notice how you need to define MODBUS_SERIAL_ENABLE_PIN and MODBUS_RX_SERIAL_ENABLE if you want to use RS485.


I want to try to simulate Proteus with two PIC18F4550, one master and the other slave, but which memory will the master read with the read_holding_registers command?

Diagram: https://www.flickr.com/photos/166432267@N08/48001756668/in/dateposted-public/
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Jun 04, 2019 11:41 am     Reply with quote

You will need to provide a modbus master driver for the master and a slave driver for the slave.

read_holding_registers will put the contents of the slave's holding registers into modbus_rx.data

And I advise against simulating using Proteus. There have been a number of cases documented on this forum where a design simulated in Proteus will work there and not in real life, and vice versa.

It is more reliable to simulate in real life using real components.
José Aparecido



Joined: 29 May 2019
Posts: 21

View user's profile Send private message

PostPosted: Tue Jun 04, 2019 11:42 am     Reply with quote

dluu13 wrote:
One thing: I say that you should use MODBUS_INT_RDA for this because I notice that you are using C6 and C7, which are the hardware UART pins. If you really intend to use MODBUS_INT_EXT then you should wire the incoming (RX) pin into the INT_EXT pin of your PIC. Otherwise you need to define:
MODBUS_SERIAL_TX_PIN
MODBUS_SERIAL_RX_PIN


The display shows the value 00, what's this?
My code is:

Code:

//Master

#include <18f4550.h>
#device *=16
#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=20M)

#define MODBUS_BUS SERIAL
#define MODBUS_TYPE MODBUS_TYPE_MASTER
#define MODBUS_SERIAL_TYPE MODBUS_RTU
#define MODBUS_SERIAL_INT_SOURCE MODBUS_INT_RDA
#define MODBUS_SERIAL_RX_BUFFER_SIZE 64
#define MODBUS_SERIAL_BAUD 9600
#define MODBUS_REMOTE_ADDRESS 1
#define MODBUS_OUR_ADDRESS 1
#define MODBUS_SERIAL_RX_ENABLE PIN_C7                                      //pino RE RS-485 C7 RO
#define MODBUS_SERIAL_ENABLE_PIN PIN_C6                                     //pino DE RS-485 C6 DI

#include "modbus.c"
#include "LCD.C"

#define MODBUS_SLAVE_ADDRESS 0x01                                           //define o endereço do escravo

int nbuf;

void main() {

     modbus_init();
     lcd_init();
     
     while(TRUE) {
          modbus_read_holding_registers(0x01,0x05,0x01);              //lendo os registradores de retenção
          printf(lcd_putc,"\f%X",modbus_rx.data[nbuf]);
          delay_ms(50);;     
     }
}


Last edited by José Aparecido on Tue Jun 04, 2019 11:46 am; edited 1 time in total
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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