Skip to content

Commit bf1fe9b

Browse files
committed
mesa_modbus: Modbus driver framework for Mesa PktUART
This framework builds custom HAL components to drive user-defined Modbus devices in realtime using the Mesa PktUART component. Signed-off-by: andypugh <andy@bodgesoc.org>
1 parent e7cb456 commit bf1fe9b

7 files changed

Lines changed: 1371 additions & 0 deletions

File tree

docs/src/drivers/mesa_modbus.adoc

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
:lang: en
2+
3+
[[cha:mesa_modbusl]]
4+
5+
= Mesa Modbus =
6+
7+
A framework to create custom, realtime, modbus drivers using the Mesa
8+
PktUART component.
9+
10+
There are several existing ways to control Modbus devices with LinuxCNC,
11+
and those should also be considered.
12+
They include, but are not limited to:
13+
14+
<<cha:mb2hal>>
15+
16+
https://forum.linuxcnc.org/24-hal-components/38733-vfdmod-an-easy-vfd-control-over-modbus-rtu?start=0#162435[vfdmod]
17+
18+
<<cha:cl-programming>>
19+
20+
There are also several drivers for specific VFDs, for example
21+
22+
<<cha:mitsub>>
23+
24+
<<hy_gt_vfd>>
25+
26+
<<hy_vfd>>
27+
28+
<<gs2_vfd>>
29+
30+
<<vfdb_vfd>>
31+
32+
<<vfs11_vfd>>
33+
34+
<<wj200_vfd>>
35+
36+
For most applications (especially the "hy" (HuangYang) VFDs which do not
37+
actually conform to true modbus) it is likely to be less work to use
38+
these.
39+
40+
Reasons to use this framework:
41+
42+
1) It is realtime, commands and reads happen deterministically. This
43+
does not mean that they are fast, but that they happen at the same rate
44+
all of the time, independent of other activity on the PC. At 9600bps a
45+
typical transaction takes 18 servo-thread cycles, and the channels are
46+
serviced consecutively. At higher bit-rates things are better, but each
47+
read or write will always take at least three cycles, per register. To
48+
ameliorate this a little, writes to registers are only performed when
49+
necessary. Reads happen in turn, taking as long as they take.
50+
51+
2) It uses ports on the Mesa card. If you already have a Mesa card then
52+
this means that no extra hardware is needed. A typical application would
53+
be to re-purpose the smart-serial port on the 7i96 as a modbus port to
54+
control a VFD. If you do not have a Mesa card then this is obviously no
55+
advantage at all.
56+
57+
== Quick Start ==
58+
59+
Install the linuxcnc-dev package. (FIXME: modcompile and mesa_uart.c are
60+
not yet included in linuxcnc-dev)
61+
62+
Create a new definition file similar to analogue.mod and relayboard.mod
63+
(the annotated mesa_uart.mod.sample file should help here)
64+
65+
Run the command:
66+
67+
----
68+
./modcompile my_file.mod
69+
----
70+
71+
and the "modcompile" script will compile and install the module. As
72+
some realtime implementations of LinuxCNC realtime require that modules
73+
be kernel modules, and kernel modules have no access to files, the
74+
mesa_modbus framework compiles-in the modbus register structure and
75+
HAL pins into an immutable, loadable, module. If you need to change
76+
the register assigments or add/remove pins/registers then the module
77+
must be recompiled.
78+
79+
Load and activate the module with HAL commands such as
80+
81+
----
82+
loadrt my_file ports=hm2_7i69.0.pktuart.0
83+
addf my_file servo-thread
84+
----
85+
86+
== Modbus ==
87+
88+
Rather than document modbus here, the main details can be found at:
89+
https://en.wikipedia.org/wiki/Modbus
90+
91+
The mesa_modbus system supports modbus commands 1,2,3,5,6,15,16.
92+
93+
== HAL Interface ==
94+
95+
Modbus *write* commands will create HAL *input* pins, that take values
96+
from HAL and pass them to the Modbus device.
97+
98+
Modbus *read* commands will read data from the device and pass that to
99+
HAL *output* pins for use by other components.
100+
101+
HAL_BIT, HAL_U32 and HAL_S32 pins will present the (16 bit) Modbus
102+
registers exactly as the bits are delivered from the device and will
103+
send them to the device as 16 bit values according to standard C type
104+
conversion standards. HAL_FLOAT values are treated differently. When a
105+
float pin is specified then two extra HAL pins are created with the
106+
same name but suffixed with -scale and offset. The value from the
107+
modbus register will be interpreted as a sighed-16 bit number. It will
108+
then be multiplied by the scale value and then the offset will be
109+
added (ie the offset is in engineering units, and the scale converts 16
110+
bit register values to engineering units)
111+
112+
The reverse transformation is performed when writing to the device.
113+
114+
All Modbus registers are 16 bits. It is possible that in some applications
115+
this might be used to represent encoder counts or some other number that
116+
will wrap-around. To circumvent this problem in the case of the HAL_S32
117+
pin type the registers will be promoted to signed 64-bit internally then
118+
multiplied by the "scale" pin value and presented as a floating point
119+
value on a HAL pin with the suffix "scaled".
120+
For example mymodule.0.counts-0-scaled.
121+
122+
123+
In addition to the pins configured in the definition file, each
124+
module will create the following pins for each instance of the driver:
125+
126+
*modname.address* (default 0x01)
127+
128+
*modname.baudrate* (default 9600)
129+
130+
*modname.parity* (default 0 (no parity) options are 1 for odd and 2 for even.)
131+
132+
*modname.txdelay* (default 20 bit lengths. Generally should be larger than Rx Delay)
133+
134+
*modname.rxdelay* (default 15)
135+
136+
*modname.drive_delay* (default 0)
137+
138+
These can be redefined at any time and will take effect the next time
139+
that a modbus packet is assembled.
140+
141+
Each module exports a single HAL function to be attached to a realtime
142+
thread. The function name is just the module name, with no distinction
143+
made between read and write cycles.
144+
145+
All modules created by the framework require a hostmot2 pktuart instance
146+
to be given to the "ports" modparam on the "loadrt" file. See the
147+
example in the [Quick Start] section.
148+
149+
150+
== Configuration File ==
151+
152+
A Mesa_Modbus configuration file is actually a C header file and must
153+
conform to C syntax rules. An example file is included here:
154+
155+
[source,C]
156+
----
157+
/*
158+
The format of the channel descriptors is:
159+
160+
{TYPE, FUNC, ADDR, COUNT, pin_name}
161+
162+
TYPE is one of HAL_BIT, HAL_FLOAT, HAL_S32, HAL_U32
163+
FUNC = 1, 2, 3, 4, 5, 6, 15, 16 - Modbus commands
164+
COUNT = number of coils/registers to read
165+
*/
166+
167+
#define MAX_MSG_LEN 16 // may be increased if necessary to max 251
168+
169+
static const hm2_modbus_chan_descriptor_t channels[] = {
170+
/* {TYPE, FUNC, ADDR, COUNT, pin_name} */
171+
// Create 8 HAL bit pins coil-00 .. -07 supplying the values of coils at 0x0000
172+
{HAL_BIT, 1, 0x0000, 8, "coil"},
173+
// Create 8 HAL bit pins input-00 .. -07 supplying the values of inputs at 0x0000
174+
{HAL_BIT, 2, 0x0000, 8, "input"},
175+
// Create a HAL pin to set the coil at address 0x0010
176+
{HAL_BIT, 5, 0x0010, 1, "coil-0"},
177+
// Create 8 HAL pins to set the coils at 0x0020
178+
{HAL_BIT, 15, 0x0020, 8, "more_coils"},
179+
// Create a scaled floating point pin calculated from input register 0x0100
180+
{HAL_FLOAT, 4, 0x0100, 1, "float"},
181+
// Create 4 unsigned integer HAL pins from the holding registers at 0x0200-0x203
182+
{HAL_S32, 3, 0x0003, 4, "holding"},
183+
// Create a single signed int HAL pin to control the register at 0x0300
184+
{HAL_S32, 6, 0x0300, 1, "relay-3"},
185+
// Create 7 scaled FP HAL pins to control holfing registers at 0x400-0x406
186+
{HAL_FLOAT, 16, 0x0300, 1, "more_floats"},
187+
};
188+
----
189+
190+
Typically the comments would not be included in a config file.
191+
192+
MAX_MSG_LEN can be included as a #define if required, but will default
193+
to 16 bytes if this is omitted. The Modbus protocol forces a hard max
194+
limit of 251 bytes, but that would imply setting thousands of bits or
195+
hundreds of registers in a single transaction.
196+
197+
An optional DEBUG parameter may be defined. This will default to
198+
RTAPI_MSG_ERR (1) which means that only error messages will be shown.
199+
include the line
200+
----
201+
#define DEBUG 3
202+
----
203+
To see verbose data from the driver which can be useful for debugging.
204+
Be aware that this is a lot of data, and it should be turned back to
205+
1 when the driver is working.
206+
207+
The text `static const hm2_modbus_chan_descriptor_t channels[] = {`
208+
must be left unchanged, and the concluding `};` is also very
209+
important.
210+
211+
Between the start and end delimiters defined above there should be as
212+
many descriptors as necessary for the device being controlled. For a
213+
simple device (such as a single channel ADC) there might be only one
214+
line. For such a simple device the following minimal description file
215+
would suffice
216+
217+
[source,C]
218+
----
219+
static const hm2_modbus_chan_descriptor_t channels[] = {
220+
/* {TYPE, FUNC, ADDR, COUNT, pin_name} */
221+
{HAL_FLOAT, 3, 0x0000, 1, "volts"},
222+
};
223+
----
224+
225+
The valid HAL pin types supported are HAL_BIT, HAL_FLOAT, HAL_U32 and
226+
HAL_S32.
227+
228+
The supported Modbus command types are:
229+
230+
[cols=description, code]
231+
|===
232+
|Read Coils|1|
233+
|Read Discrete Inputs|2|
234+
|Read Multiple Holding Registers|3|
235+
|Read Input Registers|4|
236+
|Write Single Coil|5|
237+
|Write Single Holding Register|6|
238+
|Write Multiple Coils|15|
239+
|Write Multiple Holding Registers|16|
240+
|===
241+
242+
The Modbus address can be given in Hexadecimal, decimal (or even octal)
243+
as can the modbus command. Typically the modbus commands are given in
244+
decimal and the addresses in hex.
245+
246+
If the number in the "count" column is >1 _and_ if the command given
247+
supports multiple reads/writes then a numbered sequence of HAL pins will
248+
be created using the root name from the definition with an appended 2
249+
digit suffix, eg `volts-03`. For commands that do not support multiple
250+
values (5, 6) the count column is silently ignored (but must be numeric
251+
and not omitted)
252+
253+
== Compiling ==
254+
255+
A simple script 'modcompile' is provided that will compile and install
256+
a new HAL module based on the mesa_modbus.c file and the pin definition
257+
file. The sample definition files use the .mod prefix but this is not
258+
necessary except in the special case of the 'modcompile all' command,
259+
which will compile and install all .mod files in the current directory.
260+
261+
----
262+
./modcompile my_file.mod
263+
----
264+
or
265+
----
266+
./modcompile all
267+
----
268+
269+
"modcompile" is provided by the "linuxcnc-dev" package.
270+
271+
----
272+
sudo apt-get install linuxcnc-uspace-dev
273+
----
274+
or
275+
----
276+
sudo apt-get install linuxcnc-dev
277+
----
278+
if using RTAI kernel realtime.
279+
280+
Alternatively the package should be installable with the Synaptic
281+
package manager.
282+
283+
== Hardware Connection ==
284+
285+
The Mesa serial ports have separate pins for Tx and Tx pairs. For RS422
286+
Modbus RTU communications these should be connected at the Mesa card
287+
Tx+ to Rx+ and Tx- to Rx-.
288+
289+
Nore that there are differing naming standards for Modbus pins.
290+
Typically Rx+ and TX+ will connect to the B- pin on the modbus device
291+
and Rx- and Tx- will connect to the A+ pin. (ie, +/- will appear
292+
reversed.
293+
294+
295+
=== Ad-hoc Modbus device access ===
296+
297+
For experimentation and one-off configuration it is possible to send /
298+
recieve data through the FPGA serial port using the mesaflash utility
299+
in a script. A sample script follows.
300+
301+
[source,bash]
302+
----
303+
#! /bin/bash
304+
305+
# First setup the DDR and Alt Source regs for the 7I96
306+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x1100=0x1F800
307+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x1104=0x1C3FF
308+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x1200=0x1F800
309+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x1204=0x1C3FF
310+
# Next set the baud rate DDS's for 9600 baud
311+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6300=0x65
312+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6700=0x65
313+
# setup the TX and RX mode registers
314+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6400=0x00000A20
315+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6800=0x3FC0140C
316+
# Reset the TX and RX UARTS
317+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6400=0x80010000
318+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6800=0x80010000
319+
# load two 8-byte modbus commands:
320+
# 01 05 00 00 5A 00 F7 6A and 01 01 00 00 00 01 FD CA
321+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6100=0x00000501
322+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6100=0x6AF7005A
323+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6100=0x00000101
324+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6100=0xCAFD0100
325+
326+
# Command the TX UART to send the two 8 byte packets
327+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6200=0x08
328+
mesaflash --device 7i96 --addr 10.10.10.10 --wpo 0x6200=0x08
329+
sleep 1
330+
# display TX Mode
331+
mesaflash --device 7i96 --addr 10.10.10.10 --rpo 0x6400
332+
# display the RX mode reg, RX count, and the data
333+
mesaflash --device 7i96 --addr 10.10.10.10 --rpo 0x6800
334+
mesaflash --device 7i96 --addr 10.10.10.10 --rpo 0x6600
335+
mesaflash --device 7i96 --addr 10.10.10.10 --rpo 0x6500
336+
mesaflash --device 7i96 --addr 10.10.10.10 --rpo 0x6500
337+
----
338+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Do not edit mesa_modbus.c (Unless you are sure that you want to, of
2+
course).
3+
Create a new xxxxx.mod file that describes your particular modbus device
4+
and run
5+
----
6+
modcompile xxxxx.mod
7+
----
8+
to compile, and install, the component on your system.
9+
10+
See the main mesa_modbus docs (in the "Hardware Drivers" section for
11+
further information.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* Modbus definition file for a single-channel analogue input module
2+
found on eBay */
3+
4+
/*
5+
The format of the channel descriptors is:
6+
7+
{TYPE, FUNC, ADDR, COUNT, pin_name}
8+
9+
TYPE is one of HAL_BIT, HAL_FLOAT, HAL_S32, HAL_U32, ENCODER
10+
FUNC = 1, 2, 3, 4, 5, 6, 15, 16 - Modbus commands
11+
COUNT = number of coils/registers to read
12+
*/
13+
14+
#define MAX_MSG_LEN 16 // may be increased if necessary to max 251
15+
16+
static const hm2_modbus_chan_descriptor_t channels[] = {
17+
/* {TYPE, FUNC, ADDR, COUNT, pin_name} */
18+
{HAL_FLOAT, 3, 0x0000, 1, "volts"},
19+
};
20+
21+
/* Optionally #define DEBUG to aid with troubleshooting.
22+
Use 3 to see a lot of information about the modbus
23+
internal workings. 1 is default (errors only) */
24+
25+
#define DEBUG 1

0 commit comments

Comments
 (0)