Skip to content

Commit 11eda9f

Browse files
committed
enum: Add a HAL component to handle enumerated types
Some devices (especially ModBus VFDs) use enumerated register values (rather than bitmaps) to send/receive commands or status. This component encodes or decodes these into/from HAL bit pins. Signed-off-by: andypugh <andy@bodgesoc.org>
1 parent 63d7f6a commit 11eda9f

3 files changed

Lines changed: 313 additions & 0 deletions

File tree

docs/man/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ man9/deadzone.9
9797
man9/demux.9
9898
man9/differential.9
9999
man9/edge.9
100+
man9/enum.9
100101
man9/eoffset_per_angle.9
101102
man9/estop_latch.9
102103
man9/feedcomp.9

docs/src/man/man9/enum.9.adoc

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
= enum(9)
2+
3+
== NAME
4+
5+
enum - enumerate integer values into bits
6+
7+
== SYNOPSIS
8+
9+
*loadrt enum enums=E;enum1pin1;enum1pin2;;;enum1pin3,D;;;enum2pin1;enum2pin2 [names=name1,name2]*
10+
11+
== DESCRIPTION
12+
13+
*enum* converts integer values into bits and vice versa.
14+
15+
The component is especially suitable for encoding and decoding register
16+
values for modbus devices, where control commands and status are frequently
17+
encoded as enumerations rather than bits. For example 0 = stop, 1 =
18+
forwards, 2 = backwards, 3 = jog-forwards etc.
19+
20+
The pins created and the behaviour of the component are controlled by
21+
the load-time modparams "enums=" and "names="
22+
23+
The *enums=* parameter should be a comma-separated list of semicolon-
24+
separated pin labels. The enumerated values will increase in sequence
25+
starting at zero. To skip a value use a zero-length label, ie two
26+
consecutive semicolons, as shown in the examples.
27+
28+
There should be no spaces in the "enums=" list.
29+
30+
"names=" is an optional list of component instance names. If "names=" is
31+
omitted the functions and pins will be named "enum-decode...." or
32+
"enum-encode...."
33+
34+
Taking the example configuration above, if *enum-decode.01.enum2pin1-in*
35+
is set to *TRUE* then the output pin *enum-decode.01.output* will be set
36+
to the value 2. If *enum-decode.01.enum2pin2-in* is set to true then the
37+
output would be 3.
38+
39+
Conversely, if *enum-encode.00.input* is set to 4 then the pin
40+
*enum-encode.00.enum1pin3-out* will be set to *TRUE*.
41+
42+
== OPTIONS
43+
44+
Preceding the list of labels should be the control-codes "D" for decode
45+
or "E" for encode. A D-type enum will set the value of HAL bit pins in
46+
response to changes to the enum-decode.NN.input value, whereas an E-type
47+
enum will set the value of the enum-encode.NN.output integer depending
48+
on which enum-encode.NN.label-bit value is set.
49+
50+
If more than one label-bit input pin is set the output value will
51+
correspond to the pin label later in the list.
52+
53+
E and D-type enumerations may be freely mixed in separate instances.
54+
55+
== FUNCTIONS
56+
57+
*enum-decode._NN_.decode* - if instance type = "D"
58+
59+
*enum-encode._NN_.encode* - if instance type = "E"
60+
61+
== PINS
62+
63+
*enum-decode._NN_.input* - The integer value to be decoded
64+
65+
*enum-decode._NN_.label-out* - output bits of a decode instance
66+
67+
*enum-decode._NN_.label-val* - The enumeration value corresponding to
68+
each specific bit output. These are
69+
populated in sequence during loading
70+
but may be over-ridden in HAL if
71+
convenient.
72+
73+
*enum-encode._NN_.label-in* - input bits of a decode instance
74+
75+
*enum-encode._NN_.label-val* - The enumeration value corresponding to
76+
each specified bit input. These are
77+
populated in sequence during loading
78+
but may be over-ridden in HAL if
79+
convenient.
80+
81+
*enum-decode._NN_.output* - The integer value corresponding to the
82+
set bit input.
83+
84+
== BUGS
85+
86+
If no bits are set the output value will be zero even if zero is a
87+
defined enumeration.
88+
89+
== AUTHOR
90+
91+
Andy Pugh
92+
93+
== REPORTING BUGS
94+
Report bugs to at the LinuxCNC github issues list:
95+
https://github.com/LinuxCNC/linuxcnc/issues
96+
97+
== COPYRIGHT
98+
Copyright © 2023 Andy Pugh. This is free software; see the
99+
source for copying conditions. There is NO warranty; not even for
100+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
101+

src/hal/components/enum.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright (C) 2023 Andy Pugh
2+
3+
// This program is free software; you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation; either version 2 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with this program; if not, write to the Free Software
15+
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16+
//
17+
18+
/* A configurable component to use Mesa PktUART for modbus control */
19+
20+
21+
#include "rtapi.h"
22+
#include "rtapi_slab.h"
23+
#include "rtapi_app.h"
24+
#include "rtapi_string.h"
25+
#include "hal.h"
26+
27+
#if !defined(__KERNEL__)
28+
#include <stdio.h>
29+
#include <stdlib.h>
30+
#endif
31+
32+
/* module information */
33+
MODULE_AUTHOR("Andy Pugh");
34+
MODULE_DESCRIPTION("convert enumerated types to HAL_BIT pins");
35+
MODULE_LICENSE("GPL");
36+
37+
#define MAX_CHAN 256
38+
39+
typedef struct {
40+
hal_bit_t *bit;
41+
hal_u32_t *en; // note use index 0 differently
42+
} enum_hal_t;
43+
44+
typedef struct{
45+
int dir;
46+
int num_pins;
47+
enum_hal_t *hal;
48+
} enum_inst_t;
49+
50+
typedef struct {
51+
int num_insts;
52+
enum_inst_t *insts;
53+
} enum_t;
54+
55+
static int comp_id;
56+
57+
static enum_t e;
58+
59+
static char *enums[MAX_CHAN] = {0,};
60+
RTAPI_MP_ARRAY_STRING(enums, MAX_CHAN, "states, ; delimited");
61+
static char *names[MAX_CHAN] = {0,};
62+
RTAPI_MP_ARRAY_STRING(names, MAX_CHAN, "component names (optional)");
63+
64+
static void decode(void *inst, long period);
65+
static void encode(void *inst, long period);
66+
67+
int rtapi_app_main(void){
68+
int num_chans;
69+
int i, j, v;
70+
int retval;
71+
char *token;
72+
73+
if (!enums[0]) {
74+
rtapi_print_msg(RTAPI_MSG_ERR, "The enum_decode component requires at least"
75+
" one enumeration list\n");
76+
return -EINVAL;
77+
}
78+
79+
// count instances
80+
e.num_insts = MAX_CHAN;
81+
for (i = 0; i < MAX_CHAN; i++){
82+
if (! enums[i] && ! names[i]){
83+
e.num_insts = i;
84+
rtapi_print_msg(RTAPI_MSG_ERR, "making %i instances\n", e.num_insts);
85+
break;
86+
}
87+
if ((! enums[i] && names[i]) || ( ! names[i] && names[0] && enums[i])){
88+
rtapi_print_msg(RTAPI_MSG_ERR, "Inconsistent number of names and enums\n");
89+
return -EINVAL;
90+
}
91+
}
92+
93+
comp_id = hal_init("enum");
94+
95+
if (comp_id < 0) {
96+
rtapi_print_msg(RTAPI_MSG_ERR, "ERROR: hal_init() failed\n");
97+
return -EINVAL;
98+
}
99+
// allocate memory for the base struct
100+
e.insts = (enum_inst_t *)rtapi_kmalloc(e.num_insts * sizeof(enum_inst_t), RTAPI_GFP_KERNEL);
101+
for (i = 0; i < e.num_insts; i++){
102+
enum_inst_t *inst = &(e.insts[i]);
103+
char this[HAL_NAME_LEN];
104+
char func[HAL_NAME_LEN];
105+
106+
// Count the pins
107+
inst->num_pins = 0;
108+
inst->dir = HAL_OUT; // direction of bit pin, out for decode
109+
for (j = strlen(enums[i]); j > 0; j--){
110+
if (enums[i][j] == ';'){
111+
if (enums[i][j-1] != ';' ) inst->num_pins++;
112+
// insert a string terminator
113+
enums[i][j] = 0;
114+
}
115+
}
116+
inst->hal = (enum_hal_t *)hal_malloc((inst->num_pins + 1) * sizeof(enum_hal_t));
117+
token = enums[i];
118+
switch (*token){
119+
case 'E':
120+
case 'e': // encode
121+
inst->dir = HAL_IN;
122+
break;
123+
case 'D':
124+
case 'd':
125+
inst->dir = HAL_OUT;
126+
break;
127+
default:
128+
rtapi_print_msg(RTAPI_MSG_ERR, "Each enum string must start"
129+
"with either E; or D; to define the mode\n");
130+
goto fail0;
131+
}
132+
133+
if (names[i]) {
134+
rtapi_snprintf(this, HAL_NAME_LEN, "%s", names[i]);
135+
} else if (inst->dir == HAL_IN) {
136+
rtapi_snprintf(this, HAL_NAME_LEN, "enum-encode.%02i", i);
137+
} else {
138+
rtapi_snprintf(this, HAL_NAME_LEN, "enum-decode.%02i", i);
139+
}
140+
141+
// create single per-instance int pin in index 0
142+
if (inst->dir == HAL_OUT) {
143+
retval = hal_pin_u32_newf(HAL_IN, &(inst->hal[0].en), comp_id,
144+
"%s.input", this);
145+
} else {
146+
retval = hal_pin_u32_newf(HAL_OUT, &(inst->hal[0].en), comp_id,
147+
"%s.output", this);
148+
}
149+
v = 0;
150+
for (j = 1; j <= inst->num_pins; j++){ // 1-based indexing
151+
// skip to the next pin name
152+
while (*(++token) != 0){}
153+
//increment for skipped enumerations
154+
while (*(++token) == 0) v++;
155+
156+
retval = hal_pin_bit_newf(inst->dir, &(inst->hal[j].bit),
157+
comp_id, "%s.%s-%s",this, token,
158+
(inst->dir == HAL_IN)?"in":"out");
159+
retval += hal_pin_u32_newf(HAL_IN, &(inst->hal[j].en),
160+
comp_id, "%s.%s-val",this, token);
161+
*(inst->hal[j].en) = v++;
162+
163+
if (retval < 0){
164+
rtapi_print_msg(RTAPI_MSG_ERR, "Failed to create HAL pins\n");
165+
goto fail0;
166+
}
167+
}
168+
if (inst->dir == HAL_OUT){
169+
retval = rtapi_snprintf(func, HAL_NAME_LEN, "%s.decode", this);
170+
hal_export_funct(func, decode, inst, 0, 0, comp_id);
171+
} else {
172+
retval = rtapi_snprintf(func, HAL_NAME_LEN, "%s.encode", this);
173+
hal_export_funct(func, encode, inst, 0, 0, comp_id);
174+
}
175+
if (retval < 0){
176+
rtapi_print_msg(RTAPI_MSG_ERR, "Failed to export functions\n");
177+
goto fail0;
178+
}
179+
}
180+
181+
182+
hal_ready(comp_id);
183+
return 0;
184+
185+
fail0:
186+
free(e.insts);
187+
hal_exit(comp_id);
188+
return -1;
189+
190+
}
191+
192+
static void decode(void *v_inst, long period){;
193+
enum_inst_t *inst = v_inst;
194+
static int once = 0;
195+
for (int i = 1; i <= inst->num_pins; i++){
196+
if (*(inst->hal[0].en) == *(inst->hal[i].en)){
197+
*(inst->hal[i].bit) = 1;
198+
} else {
199+
*(inst->hal[i].bit) = 0;
200+
}
201+
}
202+
}
203+
static void encode(void *v_inst, long period){;
204+
enum_inst_t *inst = v_inst;
205+
*(inst->hal[0].en) = 0;
206+
for (int i = 1; i <= inst->num_pins; i++){
207+
if (*(inst->hal[i].bit)){
208+
*(inst->hal[0].en) = *(inst->hal[i].en);
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)