]> sigrok.org Git - libsigrok.git/blob - src/hardware/rdtech-dps/api.c
rdtech-dps: research to reduce serial comm transfer volume again
[libsigrok.git] / src / hardware / rdtech-dps / api.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2018 James Churchill <pelrun@gmail.com>
5  * Copyright (C) 2019 Frank Stettner <frank-stettner@gmx.net>
6  * Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
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>
23
24 #include <math.h>
25 #include <string.h>
26
27 #include "protocol.h"
28
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
55 /* Model ID, model name, max current/voltage/power, current/voltage digits. */
56 static const struct rdtech_dps_model supported_models[] = {
57         { MODEL_DPS, 3005, "DPS3005",  5, 30,  160, 3, 2 },
58         { MODEL_DPS, 5005, "DPS5005",  5, 50,  250, 3, 2 },
59         { MODEL_DPS, 5205, "DPH5005",  5, 50,  250, 3, 2 },
60         { MODEL_DPS, 5015, "DPS5015", 15, 50,  750, 2, 2 },
61         { MODEL_DPS, 5020, "DPS5020", 20, 50, 1000, 2, 2 },
62         { MODEL_DPS, 8005, "DPS8005",  5, 80,  408, 3, 2 },
63         /* All RD specs taken from the 2020.12.2 instruction manual. */
64         { MODEL_RD , 6006, "RD6006" ,  6, 60,  360, 3, 2 },
65         { MODEL_RD , 6012, "RD6012" , 12, 60,  720, 2, 2 },
66         { MODEL_RD , 6018, "RD6018" , 18, 60, 1080, 2, 2 },
67 };
68
69 static struct sr_dev_driver rdtech_dps_driver_info;
70 static struct sr_dev_driver rdtech_rd_driver_info;
71
72 static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus,
73         enum rdtech_dps_model_type model_type)
74 {
75         static const char *type_prefix[] = {
76                 [MODEL_DPS] = "DPS",
77                 [MODEL_RD]  = "RD",
78         };
79
80         uint16_t id, version;
81         uint32_t serno;
82         int ret;
83         const struct rdtech_dps_model *model, *supported;
84         size_t i;
85         struct sr_dev_inst *sdi;
86         struct dev_context *devc;
87
88         ret = rdtech_dps_get_model_version(modbus,
89                 model_type, &id, &version, &serno);
90         sr_dbg("probe: ret %d, type %s, model %u, vers %u, snr %u.",
91                 ret, type_prefix[model_type], id, version, serno);
92         if (ret != SR_OK)
93                 return NULL;
94         model = NULL;
95         for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
96                 supported = &supported_models[i];
97                 if (model_type != supported->model_type)
98                         continue;
99                 if (id != supported->id)
100                         continue;
101                 model = supported;
102                 break;
103         }
104         if (!model) {
105                 sr_err("Unknown model: %s%u.", type_prefix[model_type], id);
106                 return NULL;
107         }
108
109         sdi = g_malloc0(sizeof(*sdi));
110         sdi->status = SR_ST_INACTIVE;
111         sdi->vendor = g_strdup("RDTech");
112         switch (model_type) {
113         case MODEL_DPS:
114                 sdi->model = g_strdup(model->name);
115                 sdi->version = g_strdup_printf("v%u", version);
116                 sdi->driver = &rdtech_dps_driver_info;
117                 break;
118         case MODEL_RD:
119                 sdi->model = g_strdup(model->name);
120                 sdi->version = g_strdup_printf("v%u.%u",
121                         version / 100, version % 100);
122                 if (serno)
123                         sdi->serial_num = g_strdup_printf("%u", serno);
124                 sdi->driver = &rdtech_rd_driver_info;
125                 break;
126         default:
127                 sr_err("Programming error, unhandled DPS/DPH/RD device type.");
128                 g_free(sdi->vendor);
129                 g_free(sdi);
130                 return NULL;
131         }
132         sdi->conn = modbus;
133         sdi->inst_type = SR_INST_MODBUS;
134
135         sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
136         sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
137         sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, TRUE, "P");
138
139         devc = g_malloc0(sizeof(*devc));
140         sr_sw_limits_init(&devc->limits);
141         devc->model = model;
142         devc->current_multiplier = pow(10.0, model->current_digits);
143         devc->voltage_multiplier = pow(10.0, model->voltage_digits);
144
145         sdi->priv = devc;
146
147         return sdi;
148 }
149
150 static struct sr_dev_inst *probe_device_dps(struct sr_modbus_dev_inst *modbus)
151 {
152         return probe_device(modbus, MODEL_DPS);
153 }
154
155 static struct sr_dev_inst *probe_device_rd(struct sr_modbus_dev_inst *modbus)
156 {
157         return probe_device(modbus, MODEL_RD);
158 }
159
160 static int config_compare(gconstpointer a, gconstpointer b)
161 {
162         const struct sr_config *ac = a, *bc = b;
163         return ac->key != bc->key;
164 }
165
166 static GSList *scan(struct sr_dev_driver *di, GSList *options,
167         enum rdtech_dps_model_type model_type)
168 {
169         static const char *default_serialcomm_dps = "9600/8n1";
170         static const char *default_serialcomm_rd = "115200/8n1";
171
172         struct sr_config default_serialcomm = {
173                 .key = SR_CONF_SERIALCOMM,
174                 .data = NULL,
175         };
176         struct sr_config default_modbusaddr = {
177                 .key = SR_CONF_MODBUSADDR,
178                 .data = g_variant_new_uint64(1),
179         };
180         GSList *opts, *devices;
181         const char *serialcomm;
182         struct sr_dev_inst *(*probe_func)(struct sr_modbus_dev_inst *modbus);
183
184         /* TODO See why di->context isn't available yet at this time. */
185         serialcomm = NULL;
186         probe_func = NULL;
187         if (di->context == &rdtech_dps_driver_info || model_type == MODEL_DPS) {
188                 serialcomm = default_serialcomm_dps;
189                 probe_func = probe_device_dps;
190         }
191         if (di->context == &rdtech_rd_driver_info || model_type == MODEL_RD) {
192                 serialcomm = default_serialcomm_rd;
193                 probe_func = probe_device_rd;
194         }
195         if (!probe_func)
196                 return NULL;
197         if (serialcomm && *serialcomm)
198                 default_serialcomm.data = g_variant_new_string(serialcomm);
199
200         opts = options;
201         if (!g_slist_find_custom(options, &default_serialcomm, config_compare))
202                 opts = g_slist_prepend(opts, &default_serialcomm);
203         if (!g_slist_find_custom(options, &default_modbusaddr, config_compare))
204                 opts = g_slist_prepend(opts, &default_modbusaddr);
205
206         devices = sr_modbus_scan(di->context, opts, probe_func);
207
208         while (opts != options)
209                 opts = g_slist_delete_link(opts, opts);
210         g_variant_unref(default_serialcomm.data);
211         g_variant_unref(default_modbusaddr.data);
212
213         return devices;
214 }
215
216 static GSList *scan_dps(struct sr_dev_driver *di, GSList *options)
217 {
218         return scan(di, options, MODEL_DPS);
219 }
220
221 static GSList *scan_rd(struct sr_dev_driver *di, GSList *options)
222 {
223         return scan(di, options, MODEL_RD);
224 }
225
226 static int dev_open(struct sr_dev_inst *sdi)
227 {
228         struct sr_modbus_dev_inst *modbus;
229         struct rdtech_dps_state state;
230         int ret;
231
232         modbus = sdi->conn;
233         if (sr_modbus_open(modbus) < 0)
234                 return SR_ERR;
235
236         memset(&state, 0, sizeof(state));
237         state.lock = TRUE;
238         state.mask |= STATE_LOCK;
239         ret = rdtech_dps_set_state(sdi, &state);
240         if (ret != SR_OK)
241                 return ret;
242
243         return SR_OK;
244 }
245
246 static int dev_close(struct sr_dev_inst *sdi)
247 {
248         struct sr_modbus_dev_inst *modbus;
249         struct rdtech_dps_state state;
250
251         modbus = sdi->conn;
252         if (!modbus)
253                 return SR_ERR_BUG;
254
255         memset(&state, 0, sizeof(state));
256         state.lock = FALSE;
257         state.mask |= STATE_LOCK;
258         (void)rdtech_dps_set_state(sdi, &state);
259
260         return sr_modbus_close(modbus);
261 }
262
263 static int config_get(uint32_t key, GVariant **data,
264         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
265 {
266         struct dev_context *devc;
267         struct rdtech_dps_state state;
268         int ret;
269         const char *cc_text;
270
271         (void)cg;
272
273         devc = sdi->priv;
274
275         ret = SR_OK;
276         switch (key) {
277         case SR_CONF_LIMIT_SAMPLES:
278         case SR_CONF_LIMIT_MSEC:
279                 ret = sr_sw_limits_config_get(&devc->limits, key, data);
280                 break;
281         case SR_CONF_ENABLED:
282                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
283                 if (ret != SR_OK)
284                         return ret;
285                 if (!(state.mask & STATE_OUTPUT_ENABLED))
286                         return SR_ERR_DATA;
287                 *data = g_variant_new_boolean(state.output_enabled);
288                 break;
289         case SR_CONF_REGULATION:
290                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
291                 if (ret != SR_OK)
292                         return ret;
293                 if (!(state.mask & STATE_REGULATION_CC))
294                         return SR_ERR_DATA;
295                 cc_text = state.regulation_cc ? "CC" : "CV";
296                 *data = g_variant_new_string(cc_text);
297                 break;
298         case SR_CONF_VOLTAGE:
299                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
300                 if (ret != SR_OK)
301                         return ret;
302                 if (!(state.mask & STATE_VOLTAGE))
303                         return SR_ERR_DATA;
304                 *data = g_variant_new_double(state.voltage);
305                 break;
306         case SR_CONF_VOLTAGE_TARGET:
307                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
308                 if (ret != SR_OK)
309                         return ret;
310                 if (!(state.mask & STATE_VOLTAGE_TARGET))
311                         return SR_ERR_DATA;
312                 *data = g_variant_new_double(state.voltage_target);
313                 break;
314         case SR_CONF_CURRENT:
315                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
316                 if (ret != SR_OK)
317                         return ret;
318                 if (!(state.mask & STATE_CURRENT))
319                         return SR_ERR_DATA;
320                 *data = g_variant_new_double(state.current);
321                 break;
322         case SR_CONF_CURRENT_LIMIT:
323                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
324                 if (ret != SR_OK)
325                         return ret;
326                 if (!(state.mask & STATE_CURRENT_LIMIT))
327                         return SR_ERR_DATA;
328                 *data = g_variant_new_double(state.current_limit);
329                 break;
330         case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
331                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
332                 if (ret != SR_OK)
333                         return ret;
334                 if (!(state.mask & STATE_PROTECT_ENABLED))
335                         return SR_ERR_DATA;
336                 *data = g_variant_new_boolean(state.protect_enabled);
337                 break;
338         case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
339                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
340                 if (ret != SR_OK)
341                         return ret;
342                 if (!(state.mask & STATE_PROTECT_OVP))
343                         return SR_ERR_DATA;
344                 *data = g_variant_new_boolean(state.protect_ovp);
345                 break;
346         case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
347                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
348                 if (ret != SR_OK)
349                         return ret;
350                 if (!(state.mask & STATE_OUTPUT_ENABLED))
351                         return SR_ERR_DATA;
352                 *data = g_variant_new_double(state.ovp_threshold);
353                 break;
354         case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
355                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
356                 if (ret != SR_OK)
357                         return ret;
358                 if (!(state.mask & STATE_PROTECT_ENABLED))
359                         return SR_ERR_DATA;
360                 *data = g_variant_new_boolean(state.protect_enabled);
361                 break;
362         case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
363                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
364                 if (ret != SR_OK)
365                         return ret;
366                 if (!(state.mask & STATE_PROTECT_OCP))
367                         return SR_ERR_DATA;
368                 *data = g_variant_new_boolean(state.protect_ocp);
369                 break;
370         case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
371                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
372                 if (ret != SR_OK)
373                         return ret;
374                 if (!(state.mask & STATE_OCP_THRESHOLD))
375                         return SR_ERR_DATA;
376                 *data = g_variant_new_double(state.ocp_threshold);
377                 break;
378         default:
379                 return SR_ERR_NA;
380         }
381
382         return ret;
383 }
384
385 static int config_set(uint32_t key, GVariant *data,
386         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
387 {
388         struct dev_context *devc;
389         struct rdtech_dps_state state;
390
391         (void)cg;
392
393         devc = sdi->priv;
394         memset(&state, 0, sizeof(state));
395
396         switch (key) {
397         case SR_CONF_LIMIT_SAMPLES:
398         case SR_CONF_LIMIT_MSEC:
399                 return sr_sw_limits_config_set(&devc->limits, key, data);
400         case SR_CONF_ENABLED:
401                 state.output_enabled = g_variant_get_boolean(data);
402                 state.mask |= STATE_OUTPUT_ENABLED;
403                 return rdtech_dps_set_state(sdi, &state);
404         case SR_CONF_VOLTAGE_TARGET:
405                 state.voltage_target = g_variant_get_double(data);
406                 state.mask |= STATE_VOLTAGE_TARGET;
407                 return rdtech_dps_set_state(sdi, &state);
408         case SR_CONF_CURRENT_LIMIT:
409                 state.current_limit = g_variant_get_double(data);
410                 state.mask |= STATE_CURRENT_LIMIT;
411                 return rdtech_dps_set_state(sdi, &state);
412         case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
413                 state.ovp_threshold = g_variant_get_double(data);
414                 state.mask |= STATE_OVP_THRESHOLD;
415                 return rdtech_dps_set_state(sdi, &state);
416         case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
417                 state.ocp_threshold = g_variant_get_double(data);
418                 state.mask |= STATE_OCP_THRESHOLD;
419                 return rdtech_dps_set_state(sdi, &state);
420         default:
421                 return SR_ERR_NA;
422         }
423
424         return SR_OK;
425 }
426
427 static int config_list(uint32_t key, GVariant **data,
428         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
429 {
430         struct dev_context *devc;
431
432         devc = (sdi) ? sdi->priv : NULL;
433
434         switch (key) {
435         case SR_CONF_SCAN_OPTIONS:
436         case SR_CONF_DEVICE_OPTIONS:
437                 return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
438         case SR_CONF_VOLTAGE_TARGET:
439                 *data = std_gvar_min_max_step(0.0, devc->model->max_voltage,
440                         1 / devc->voltage_multiplier);
441                 break;
442         case SR_CONF_CURRENT_LIMIT:
443                 *data = std_gvar_min_max_step(0.0, devc->model->max_current,
444                         1 / devc->current_multiplier);
445                 break;
446         default:
447                 return SR_ERR_NA;
448         }
449
450         return SR_OK;
451 }
452
453 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
454 {
455         struct dev_context *devc;
456         struct sr_modbus_dev_inst *modbus;
457         int ret;
458
459         modbus = sdi->conn;
460         devc = sdi->priv;
461
462         /* Seed internal state from current data. */
463         ret = rdtech_dps_seed_receive(sdi);
464         if (ret != SR_OK)
465                 return ret;
466
467         /* Register the periodic data reception callback. */
468         ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10,
469                         rdtech_dps_receive_data, (void *)sdi);
470         if (ret != SR_OK)
471                 return ret;
472
473         sr_sw_limits_acquisition_start(&devc->limits);
474         std_session_send_df_header(sdi);
475
476         return SR_OK;
477 }
478
479 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
480 {
481         struct sr_modbus_dev_inst *modbus;
482
483         std_session_send_df_end(sdi);
484
485         modbus = sdi->conn;
486         sr_modbus_source_remove(sdi->session, modbus);
487
488         return SR_OK;
489 }
490
491 static struct sr_dev_driver rdtech_dps_driver_info = {
492         .name = "rdtech-dps",
493         .longname = "RDTech DPS/DPH series power supply",
494         .api_version = 1,
495         .init = std_init,
496         .cleanup = std_cleanup,
497         .scan = scan_dps,
498         .dev_list = std_dev_list,
499         .dev_clear = std_dev_clear,
500         .config_get = config_get,
501         .config_set = config_set,
502         .config_list = config_list,
503         .dev_open = dev_open,
504         .dev_close = dev_close,
505         .dev_acquisition_start = dev_acquisition_start,
506         .dev_acquisition_stop = dev_acquisition_stop,
507         .context = NULL,
508 };
509 SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info);
510
511 static struct sr_dev_driver rdtech_rd_driver_info = {
512         .name = "rdtech-rd",
513         .longname = "RDTech RD series power supply",
514         .api_version = 1,
515         .init = std_init,
516         .cleanup = std_cleanup,
517         .scan = scan_rd,
518         .dev_list = std_dev_list,
519         .dev_clear = std_dev_clear,
520         .config_get = config_get,
521         .config_set = config_set,
522         .config_list = config_list,
523         .dev_open = dev_open,
524         .dev_close = dev_close,
525         .dev_acquisition_start = dev_acquisition_start,
526         .dev_acquisition_stop = dev_acquisition_stop,
527         .context = NULL,
528 };
529 SR_REGISTER_DEV_DRIVER(rdtech_rd_driver_info);