]>
Commit | Line | Data |
---|---|---|
0549416e JC |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2018 James Churchill <pelrun@gmail.com> | |
7c0891b0 | 5 | * Copyright (C) 2019 Frank Stettner <frank-stettner@gmx.net> |
d7a4dad8 | 6 | * Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net> |
0549416e JC |
7 | * |
8 | * This program is free software: you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation, either version 3 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <config.h> | |
d7a4dad8 | 23 | |
cce6a8a1 | 24 | #include <math.h> |
d7a4dad8 GS |
25 | #include <string.h> |
26 | ||
0549416e JC |
27 | #include "protocol.h" |
28 | ||
69b05583 JC |
29 | static const uint32_t scanopts[] = { |
30 | SR_CONF_CONN, | |
31 | SR_CONF_SERIALCOMM, | |
32 | SR_CONF_MODBUSADDR, | |
33 | }; | |
34 | ||
35 | static const uint32_t drvopts[] = { | |
36 | SR_CONF_POWER_SUPPLY, | |
37 | }; | |
38 | ||
39 | static const uint32_t devopts[] = { | |
40 | SR_CONF_CONTINUOUS, | |
41 | SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, | |
42 | SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, | |
43 | SR_CONF_VOLTAGE | SR_CONF_GET, | |
44 | SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, | |
45 | SR_CONF_CURRENT | SR_CONF_GET, | |
46 | SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, | |
47 | SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, | |
48 | SR_CONF_REGULATION | SR_CONF_GET, | |
49 | SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET, | |
50 | SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, | |
51 | SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET, | |
52 | SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, | |
53 | }; | |
54 | ||
d7a4dad8 | 55 | /* Model ID, model name, max current/voltage/power, current/voltage digits. */ |
69b05583 | 56 | static const struct rdtech_dps_model supported_models[] = { |
cce6a8a1 FS |
57 | { 3005, "DPS3005", 5, 30, 160, 3, 2 }, |
58 | { 5005, "DPS5005", 5, 50, 250, 3, 2 }, | |
59 | { 5205, "DPH5005", 5, 50, 250, 3, 2 }, | |
60 | { 5015, "DPS5015", 15, 50, 750, 2, 2 }, | |
61 | { 5020, "DPS5020", 20, 50, 1000, 2, 2 }, | |
62 | { 8005, "DPS8005", 5, 80, 408, 3, 2 }, | |
69b05583 JC |
63 | }; |
64 | ||
65 | static struct sr_dev_driver rdtech_dps_driver_info; | |
66 | ||
67 | static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus) | |
0549416e | 68 | { |
69b05583 | 69 | uint16_t id, version; |
d7a4dad8 GS |
70 | int ret; |
71 | const struct rdtech_dps_model *model; | |
72 | size_t i; | |
73 | struct sr_dev_inst *sdi; | |
74 | struct dev_context *devc; | |
69b05583 | 75 | |
d7a4dad8 | 76 | ret = rdtech_dps_get_model_version(modbus, &id, &version); |
69b05583 JC |
77 | if (ret != SR_OK) |
78 | return NULL; | |
d7a4dad8 GS |
79 | model = NULL; |
80 | for (i = 0; i < ARRAY_SIZE(supported_models); i++) { | |
69b05583 JC |
81 | if (id == supported_models[i].id) { |
82 | model = &supported_models[i]; | |
83 | break; | |
84 | } | |
d7a4dad8 GS |
85 | } |
86 | if (!model) { | |
87 | sr_err("Unknown model: %u.", id); | |
69b05583 JC |
88 | return NULL; |
89 | } | |
90 | ||
d7a4dad8 | 91 | sdi = g_malloc0(sizeof(*sdi)); |
69b05583 JC |
92 | sdi->status = SR_ST_INACTIVE; |
93 | sdi->vendor = g_strdup("RDTech"); | |
94 | sdi->model = g_strdup(model->name); | |
d7a4dad8 | 95 | sdi->version = g_strdup_printf("v%u", version); |
69b05583 JC |
96 | sdi->conn = modbus; |
97 | sdi->driver = &rdtech_dps_driver_info; | |
98 | sdi->inst_type = SR_INST_MODBUS; | |
0549416e | 99 | |
69b05583 | 100 | sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V"); |
d7a4dad8 GS |
101 | sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I"); |
102 | sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, TRUE, "P"); | |
0549416e | 103 | |
d7a4dad8 | 104 | devc = g_malloc0(sizeof(*devc)); |
69b05583 JC |
105 | sr_sw_limits_init(&devc->limits); |
106 | devc->model = model; | |
cce6a8a1 FS |
107 | devc->current_multiplier = pow(10.0, model->current_digits); |
108 | devc->voltage_multiplier = pow(10.0, model->voltage_digits); | |
0549416e | 109 | |
69b05583 JC |
110 | sdi->priv = devc; |
111 | ||
112 | return sdi; | |
113 | } | |
114 | ||
115 | static int config_compare(gconstpointer a, gconstpointer b) | |
116 | { | |
117 | const struct sr_config *ac = a, *bc = b; | |
118 | return ac->key != bc->key; | |
119 | } | |
120 | ||
121 | static GSList *scan(struct sr_dev_driver *di, GSList *options) | |
122 | { | |
123 | struct sr_config default_serialcomm = { | |
124 | .key = SR_CONF_SERIALCOMM, | |
125 | .data = g_variant_new_string("9600/8n1"), | |
126 | }; | |
127 | struct sr_config default_modbusaddr = { | |
128 | .key = SR_CONF_MODBUSADDR, | |
129 | .data = g_variant_new_uint64(1), | |
130 | }; | |
d7a4dad8 | 131 | GSList *opts, *devices; |
69b05583 | 132 | |
d7a4dad8 | 133 | opts = options; |
69b05583 JC |
134 | if (!g_slist_find_custom(options, &default_serialcomm, config_compare)) |
135 | opts = g_slist_prepend(opts, &default_serialcomm); | |
136 | if (!g_slist_find_custom(options, &default_modbusaddr, config_compare)) | |
137 | opts = g_slist_prepend(opts, &default_modbusaddr); | |
138 | ||
139 | devices = sr_modbus_scan(di->context, opts, probe_device); | |
140 | ||
141 | while (opts != options) | |
142 | opts = g_slist_delete_link(opts, opts); | |
143 | g_variant_unref(default_serialcomm.data); | |
144 | g_variant_unref(default_modbusaddr.data); | |
0549416e JC |
145 | |
146 | return devices; | |
147 | } | |
148 | ||
149 | static int dev_open(struct sr_dev_inst *sdi) | |
150 | { | |
d7a4dad8 GS |
151 | struct sr_modbus_dev_inst *modbus; |
152 | struct rdtech_dps_state state; | |
153 | int ret; | |
69b05583 | 154 | |
d7a4dad8 | 155 | modbus = sdi->conn; |
69b05583 JC |
156 | if (sr_modbus_open(modbus) < 0) |
157 | return SR_ERR; | |
0549416e | 158 | |
d7a4dad8 GS |
159 | memset(&state, 0, sizeof(state)); |
160 | state.lock = TRUE; | |
161 | state.mask |= STATE_LOCK; | |
162 | ret = rdtech_dps_set_state(sdi, &state); | |
163 | if (ret != SR_OK) | |
164 | return ret; | |
0549416e JC |
165 | |
166 | return SR_OK; | |
167 | } | |
168 | ||
169 | static int dev_close(struct sr_dev_inst *sdi) | |
170 | { | |
69b05583 | 171 | struct sr_modbus_dev_inst *modbus; |
d7a4dad8 | 172 | struct rdtech_dps_state state; |
0549416e | 173 | |
69b05583 | 174 | modbus = sdi->conn; |
69b05583 JC |
175 | if (!modbus) |
176 | return SR_ERR_BUG; | |
177 | ||
d7a4dad8 GS |
178 | memset(&state, 0, sizeof(state)); |
179 | state.lock = FALSE; | |
180 | state.mask |= STATE_LOCK; | |
181 | (void)rdtech_dps_set_state(sdi, &state); | |
69b05583 JC |
182 | |
183 | return sr_modbus_close(modbus); | |
0549416e JC |
184 | } |
185 | ||
186 | static int config_get(uint32_t key, GVariant **data, | |
187 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
188 | { | |
69b05583 | 189 | struct dev_context *devc; |
d7a4dad8 | 190 | struct rdtech_dps_state state; |
0549416e | 191 | int ret; |
d7a4dad8 | 192 | const char *cc_text; |
0549416e | 193 | |
0549416e JC |
194 | (void)cg; |
195 | ||
69b05583 JC |
196 | devc = sdi->priv; |
197 | ||
0549416e JC |
198 | ret = SR_OK; |
199 | switch (key) { | |
69b05583 JC |
200 | case SR_CONF_LIMIT_SAMPLES: |
201 | case SR_CONF_LIMIT_MSEC: | |
202 | ret = sr_sw_limits_config_get(&devc->limits, key, data); | |
203 | break; | |
204 | case SR_CONF_ENABLED: | |
d7a4dad8 GS |
205 | ret = rdtech_dps_get_state(sdi, &state); |
206 | if (ret != SR_OK) | |
207 | return ret; | |
208 | if (!(state.mask & STATE_OUTPUT_ENABLED)) | |
209 | return SR_ERR_DATA; | |
210 | *data = g_variant_new_boolean(state.output_enabled); | |
69b05583 JC |
211 | break; |
212 | case SR_CONF_REGULATION: | |
d7a4dad8 GS |
213 | ret = rdtech_dps_get_state(sdi, &state); |
214 | if (ret != SR_OK) | |
215 | return ret; | |
216 | if (!(state.mask & STATE_REGULATION_CC)) | |
217 | return SR_ERR_DATA; | |
218 | cc_text = state.regulation_cc ? "CC" : "CV"; | |
219 | *data = g_variant_new_string(cc_text); | |
69b05583 JC |
220 | break; |
221 | case SR_CONF_VOLTAGE: | |
d7a4dad8 GS |
222 | ret = rdtech_dps_get_state(sdi, &state); |
223 | if (ret != SR_OK) | |
224 | return ret; | |
225 | if (!(state.mask & STATE_VOLTAGE)) | |
226 | return SR_ERR_DATA; | |
227 | *data = g_variant_new_double(state.voltage); | |
69b05583 JC |
228 | break; |
229 | case SR_CONF_VOLTAGE_TARGET: | |
d7a4dad8 GS |
230 | ret = rdtech_dps_get_state(sdi, &state); |
231 | if (ret != SR_OK) | |
232 | return ret; | |
233 | if (!(state.mask & STATE_VOLTAGE_TARGET)) | |
234 | return SR_ERR_DATA; | |
235 | *data = g_variant_new_double(state.voltage_target); | |
69b05583 JC |
236 | break; |
237 | case SR_CONF_CURRENT: | |
d7a4dad8 GS |
238 | ret = rdtech_dps_get_state(sdi, &state); |
239 | if (ret != SR_OK) | |
240 | return ret; | |
241 | if (!(state.mask & STATE_CURRENT)) | |
242 | return SR_ERR_DATA; | |
243 | *data = g_variant_new_double(state.current); | |
69b05583 JC |
244 | break; |
245 | case SR_CONF_CURRENT_LIMIT: | |
d7a4dad8 GS |
246 | ret = rdtech_dps_get_state(sdi, &state); |
247 | if (ret != SR_OK) | |
248 | return ret; | |
249 | if (!(state.mask & STATE_CURRENT_LIMIT)) | |
250 | return SR_ERR_DATA; | |
251 | *data = g_variant_new_double(state.current_limit); | |
69b05583 JC |
252 | break; |
253 | case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: | |
d7a4dad8 GS |
254 | ret = rdtech_dps_get_state(sdi, &state); |
255 | if (ret != SR_OK) | |
256 | return ret; | |
257 | if (!(state.mask & STATE_PROTECT_ENABLED)) | |
258 | return SR_ERR_DATA; | |
259 | *data = g_variant_new_boolean(state.protect_enabled); | |
69b05583 JC |
260 | break; |
261 | case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE: | |
d7a4dad8 GS |
262 | ret = rdtech_dps_get_state(sdi, &state); |
263 | if (ret != SR_OK) | |
264 | return ret; | |
265 | if (!(state.mask & STATE_PROTECT_OVP)) | |
266 | return SR_ERR_DATA; | |
267 | *data = g_variant_new_boolean(state.protect_ovp); | |
69b05583 JC |
268 | break; |
269 | case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD: | |
d7a4dad8 GS |
270 | ret = rdtech_dps_get_state(sdi, &state); |
271 | if (ret != SR_OK) | |
272 | return ret; | |
273 | if (!(state.mask & STATE_OUTPUT_ENABLED)) | |
274 | return SR_ERR_DATA; | |
275 | *data = g_variant_new_double(state.ovp_threshold); | |
69b05583 JC |
276 | break; |
277 | case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: | |
d7a4dad8 GS |
278 | ret = rdtech_dps_get_state(sdi, &state); |
279 | if (ret != SR_OK) | |
280 | return ret; | |
281 | if (!(state.mask & STATE_PROTECT_ENABLED)) | |
282 | return SR_ERR_DATA; | |
283 | *data = g_variant_new_boolean(state.protect_enabled); | |
69b05583 JC |
284 | break; |
285 | case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE: | |
d7a4dad8 GS |
286 | ret = rdtech_dps_get_state(sdi, &state); |
287 | if (ret != SR_OK) | |
288 | return ret; | |
289 | if (!(state.mask & STATE_PROTECT_OCP)) | |
290 | return SR_ERR_DATA; | |
291 | *data = g_variant_new_boolean(state.protect_ocp); | |
69b05583 JC |
292 | break; |
293 | case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD: | |
d7a4dad8 GS |
294 | ret = rdtech_dps_get_state(sdi, &state); |
295 | if (ret != SR_OK) | |
296 | return ret; | |
297 | if (!(state.mask & STATE_OCP_THRESHOLD)) | |
298 | return SR_ERR_DATA; | |
299 | *data = g_variant_new_double(state.ocp_threshold); | |
69b05583 | 300 | break; |
0549416e JC |
301 | default: |
302 | return SR_ERR_NA; | |
303 | } | |
304 | ||
305 | return ret; | |
306 | } | |
307 | ||
308 | static int config_set(uint32_t key, GVariant *data, | |
309 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
310 | { | |
69b05583 | 311 | struct dev_context *devc; |
d7a4dad8 | 312 | struct rdtech_dps_state state; |
0549416e | 313 | |
0549416e JC |
314 | (void)cg; |
315 | ||
69b05583 | 316 | devc = sdi->priv; |
d7a4dad8 | 317 | memset(&state, 0, sizeof(state)); |
69b05583 | 318 | |
0549416e | 319 | switch (key) { |
69b05583 JC |
320 | case SR_CONF_LIMIT_SAMPLES: |
321 | case SR_CONF_LIMIT_MSEC: | |
322 | return sr_sw_limits_config_set(&devc->limits, key, data); | |
323 | case SR_CONF_ENABLED: | |
d7a4dad8 GS |
324 | state.output_enabled = g_variant_get_boolean(data); |
325 | state.mask |= STATE_OUTPUT_ENABLED; | |
326 | return rdtech_dps_set_state(sdi, &state); | |
69b05583 | 327 | case SR_CONF_VOLTAGE_TARGET: |
d7a4dad8 GS |
328 | state.voltage_target = g_variant_get_double(data); |
329 | state.mask |= STATE_VOLTAGE_TARGET; | |
330 | return rdtech_dps_set_state(sdi, &state); | |
69b05583 | 331 | case SR_CONF_CURRENT_LIMIT: |
d7a4dad8 GS |
332 | state.current_limit = g_variant_get_double(data); |
333 | state.mask |= STATE_CURRENT_LIMIT; | |
334 | return rdtech_dps_set_state(sdi, &state); | |
69b05583 | 335 | case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD: |
d7a4dad8 GS |
336 | state.ovp_threshold = g_variant_get_double(data); |
337 | state.mask |= STATE_OVP_THRESHOLD; | |
338 | return rdtech_dps_set_state(sdi, &state); | |
69b05583 | 339 | case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD: |
d7a4dad8 GS |
340 | state.ocp_threshold = g_variant_get_double(data); |
341 | state.mask |= STATE_OCP_THRESHOLD; | |
342 | return rdtech_dps_set_state(sdi, &state); | |
0549416e | 343 | default: |
69b05583 | 344 | return SR_ERR_NA; |
0549416e JC |
345 | } |
346 | ||
69b05583 | 347 | return SR_OK; |
0549416e JC |
348 | } |
349 | ||
350 | static int config_list(uint32_t key, GVariant **data, | |
351 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
352 | { | |
69b05583 | 353 | struct dev_context *devc; |
0549416e | 354 | |
69b05583 | 355 | devc = (sdi) ? sdi->priv : NULL; |
0549416e | 356 | |
0549416e | 357 | switch (key) { |
69b05583 JC |
358 | case SR_CONF_SCAN_OPTIONS: |
359 | case SR_CONF_DEVICE_OPTIONS: | |
360 | return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); | |
361 | case SR_CONF_VOLTAGE_TARGET: | |
cce6a8a1 FS |
362 | *data = std_gvar_min_max_step(0.0, devc->model->max_voltage, |
363 | 1 / devc->voltage_multiplier); | |
69b05583 JC |
364 | break; |
365 | case SR_CONF_CURRENT_LIMIT: | |
cce6a8a1 FS |
366 | *data = std_gvar_min_max_step(0.0, devc->model->max_current, |
367 | 1 / devc->current_multiplier); | |
69b05583 | 368 | break; |
0549416e JC |
369 | default: |
370 | return SR_ERR_NA; | |
371 | } | |
372 | ||
69b05583 | 373 | return SR_OK; |
0549416e JC |
374 | } |
375 | ||
376 | static int dev_acquisition_start(const struct sr_dev_inst *sdi) | |
377 | { | |
69b05583 JC |
378 | struct dev_context *devc; |
379 | struct sr_modbus_dev_inst *modbus; | |
380 | int ret; | |
0549416e | 381 | |
69b05583 JC |
382 | modbus = sdi->conn; |
383 | devc = sdi->priv; | |
0549416e | 384 | |
d7a4dad8 GS |
385 | /* Seed internal state from current data. */ |
386 | ret = rdtech_dps_seed_receive(sdi); | |
dfdf4c83 FS |
387 | if (ret != SR_OK) |
388 | return ret; | |
dfdf4c83 | 389 | |
d7a4dad8 GS |
390 | /* Register the periodic data reception callback. */ |
391 | ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10, | |
392 | rdtech_dps_receive_data, (void *)sdi); | |
393 | if (ret != SR_OK) | |
69b05583 JC |
394 | return ret; |
395 | ||
396 | sr_sw_limits_acquisition_start(&devc->limits); | |
397 | std_session_send_df_header(sdi); | |
398 | ||
7c0891b0 | 399 | return SR_OK; |
0549416e JC |
400 | } |
401 | ||
402 | static int dev_acquisition_stop(struct sr_dev_inst *sdi) | |
403 | { | |
69b05583 | 404 | struct sr_modbus_dev_inst *modbus; |
0549416e | 405 | |
69b05583 JC |
406 | std_session_send_df_end(sdi); |
407 | ||
408 | modbus = sdi->conn; | |
409 | sr_modbus_source_remove(sdi->session, modbus); | |
0549416e JC |
410 | |
411 | return SR_OK; | |
412 | } | |
413 | ||
69b05583 | 414 | static struct sr_dev_driver rdtech_dps_driver_info = { |
0549416e | 415 | .name = "rdtech-dps", |
69b05583 | 416 | .longname = "RDTech DPS/DPH series power supply", |
0549416e JC |
417 | .api_version = 1, |
418 | .init = std_init, | |
419 | .cleanup = std_cleanup, | |
420 | .scan = scan, | |
421 | .dev_list = std_dev_list, | |
422 | .dev_clear = std_dev_clear, | |
423 | .config_get = config_get, | |
424 | .config_set = config_set, | |
425 | .config_list = config_list, | |
426 | .dev_open = dev_open, | |
427 | .dev_close = dev_close, | |
428 | .dev_acquisition_start = dev_acquisition_start, | |
429 | .dev_acquisition_stop = dev_acquisition_stop, | |
430 | .context = NULL, | |
431 | }; | |
0549416e | 432 | SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info); |