forked from linuxcnc-ethercat/linuxcnc-ethercat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlcec_class_dout.c
196 lines (168 loc) · 7.43 KB
/
lcec_class_dout.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//
// Copyright (C) 2024 Scott Laird <[email protected]>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
/// @file
/// @brief Library for digital output devices
#include "lcec_class_dout.h"
#include <stdio.h>
#include "../lcec.h"
static const lcec_pindesc_t slave_pins[] = {
{HAL_BIT, HAL_IN, offsetof(lcec_class_dout_channel_t, out), "%s.%s.%s.%s"},
{HAL_TYPE_UNSPECIFIED, HAL_DIR_UNSPECIFIED, -1, NULL},
};
static const lcec_pindesc_t slave_params[] = {
{HAL_BIT, HAL_RW, offsetof(lcec_class_dout_channel_t, invert), "%s.%s.%s.%s-invert"},
{HAL_TYPE_UNSPECIFIED, HAL_DIR_UNSPECIFIED, -1, NULL},
};
/// @brief Allocates a block of memory for holding the result of `count`
/// calls to `lcec_dout_register_device()`.
///
/// It is the caller's responsibility to verify that the result is not
/// NULL.
///
/// @param count The number of channels to allocate room for.
lcec_class_dout_channels_t *lcec_dout_allocate_channels(int count) {
lcec_class_dout_channels_t *channels;
channels = hal_malloc(sizeof(lcec_class_dout_channels_t));
if (channels == NULL) {
return NULL;
}
channels->count = count;
channels->channels = hal_malloc(sizeof(lcec_class_dout_channel_t *) * count);
if (channels->channels == NULL) {
return NULL;
}
return channels;
}
/// @brief Register a single digital-output channel and publishes it as a LinuxCNC HAL pin.
///
/// @param slave The slave, from `_init`.
/// @param id The pin ID. Used for naming. Should generally start at 0 and increment once per digital out pin.
/// @param idx The PDO index for the digital output.
/// @param sindx The PDO sub-index for the digital output.
///
/// See lcec_el2xxx.c for an example of use.
lcec_class_dout_channel_t *lcec_dout_register_channel(struct lcec_slave *slave, int id, uint16_t idx, uint16_t sidx) {
char name[32];
snprintf(name, 32, "dout-%d", id);
return lcec_dout_register_channel_named(slave, idx, sidx, name);
}
/// @brief Register a single digital-output channel and publishes it as a LinuxCNC HAL pin.
///
/// @param slave The slave, from `_init`.
/// @param id The pin ID. Used for naming. Should generally start at 0 and increment once per digital out pin.
/// @param idx The PDO index for the digital output.
/// @param sindx The PDO sub-index for the digital output.
/// @param name The base pin name to use, usually `dout-<ID>`.
///
/// See lcec_el2xxx.c for an example of use.
lcec_class_dout_channel_t *lcec_dout_register_channel_named(struct lcec_slave *slave, uint16_t idx, uint16_t sidx, char *name) {
lcec_class_dout_channel_t *data;
int err;
data = hal_malloc(sizeof(lcec_class_dout_channel_t));
if (data == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "hal_malloc() for slave %s.%s pin %s failed\n", slave->master->name, slave->name, name);
return NULL;
}
memset(data, 0, sizeof(lcec_class_dout_channel_t));
data->name = name;
data->pdo_bp_packed = 0xffff;
lcec_pdo_init(slave, idx, sidx, &data->pdo_os, &data->pdo_bp);
err = lcec_pin_newf_list(data, slave_pins, LCEC_MODULE_NAME, slave->master->name, slave->name, name);
if (err != 0) {
rtapi_print_msg(
RTAPI_MSG_ERR, LCEC_MSG_PFX "lcec_pin_newf_list for slave %s.%s pin %s failed\n", slave->master->name, slave->name, name);
return NULL;
}
err = lcec_param_newf_list(data, slave_params, LCEC_MODULE_NAME, slave->master->name, slave->name, name);
if (err != 0) {
rtapi_print_msg(
RTAPI_MSG_ERR, LCEC_MSG_PFX "lcec_params_newf_list for slave %s.%s pin %s failed\n", slave->master->name, slave->name, name);
return NULL;
}
return data;
}
/// @brief registers a single digital-output channel and publishes it as a LinuxCNC HAL pin.
///
/// The `_packed` variant is for devices where several logical digital
/// output channels are packed into a single PDO. For instance, the
/// RTelligent ECT60 has 2 digital outputs sharing a single U32. For
/// the ECT60, this function is intended to be called twice, with the
/// same `idx` and `sdx` values, but varying `bit` and `name` values.
///
/// @param slave the slave, from `_init`.
/// @param os The offset from `LCEC_PDO_INIT()`.
/// @param bit The bit offset for the digital out channel.
/// @param name The base name to use for the channel, `dout-<ID>` is common.
lcec_class_dout_channel_t *lcec_dout_register_channel_packed(struct lcec_slave *slave, uint16_t idx, uint16_t sidx, int bit, char *name) {
lcec_class_dout_channel_t *data;
int err;
data = hal_malloc(sizeof(lcec_class_dout_channel_t));
if (data == NULL) {
rtapi_print_msg(RTAPI_MSG_ERR, LCEC_MSG_PFX "hal_malloc() for slave %s.%s pin %s failed\n", slave->master->name, slave->name, name);
return NULL;
}
memset(data, 0, sizeof(lcec_class_dout_channel_t));
// Register the whole PDO, hopefully this does the sane thing if we register the same PDO repeatedly.
lcec_pdo_init(slave, idx, sidx, &data->pdo_os, &data->pdo_bp);
// This is kind of a terrible hack, but it should mostly work, modulo possible issues with variable-sized PDOs and endianness problems.
data->pdo_bp_packed = bit;
err = lcec_pin_newf_list(data, slave_pins, LCEC_MODULE_NAME, slave->master->name, slave->name, name);
if (err != 0) {
rtapi_print_msg(
RTAPI_MSG_ERR, LCEC_MSG_PFX "lcec_pin_newf_list for slave %s.%s pin %s failed\n", slave->master->name, slave->name, name);
return NULL;
}
return data;
}
/// @brief Write data to a digital out port.
///
/// Call this once per channel registered, from inside of your device's
/// write function.
///
/// @param slave The slave, passed from the per-device `_write`.
/// @param data A lcec_class_dout_channel_t *, as returned by lcec_dout_register_channel.
void lcec_dout_write(struct lcec_slave *slave, lcec_class_dout_channel_t *data) {
lcec_master_t *master = slave->master;
uint8_t *pd = master->process_data;
hal_bit_t s;
int os = data->pdo_os;
int bp = data->pdo_bp;
// If this is a packed input, then we need to use the packed bit
// offset, not the one that came back from LCEC_PDO_INIT(). We also
// need to adjust the offset to pick the correct byte.
if (data->pdo_bp_packed != 0xffff) {
bp = data->pdo_bp_packed & 7;
os += data->pdo_bp_packed >> 3; // the data in pd[] is always little-endian.
}
s = *(data->out);
if (data->invert) {
s = !s;
}
EC_WRITE_BIT(&pd[os], bp, s);
}
/// @brief Write data to all digital out channels attached to this device.
///
/// @param slave The slave, passed from the per-device `_write`.
/// @param channels A `lcec_class_dout_channels_t *`, as returned by
/// `lcec_dout_register_channel`.
void lcec_dout_write_all(struct lcec_slave *slave, lcec_class_dout_channels_t *channels) {
for (int i = 0; i < channels->count; i++) {
lcec_class_dout_channel_t *channel = channels->channels[i];
lcec_dout_write(slave, channel);
}
}