2 * This file is part of the libsigrok project.
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>
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.
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.
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/>.
28 enum rdtech_dps_register {
29 REG_DPS_USET = 0x00, /* Mirror of 0x50 */
30 REG_DPS_ISET = 0x01, /* Mirror of 0x51 */
36 REG_DPS_PROTECT = 0x07,
38 REG_DPS_ENABLE = 0x09,
39 REG_DPS_BACKLIGHT = 0x0A, /* Mirror of 0x55 */
41 REG_DPS_VERSION = 0x0C,
43 REG_DPS_PRESET = 0x23, /* Loads a preset into preset 0. */
46 * Add (preset * 0x10) to each of the following, for preset 1-9.
47 * Preset 0 regs below are the active output settings.
51 PRE_DPS_OVPSET = 0x52,
52 PRE_DPS_OCPSET = 0x53,
53 PRE_DPS_OPPSET = 0x54,
54 PRE_DPS_BACKLIGHT = 0x55,
55 PRE_DPS_DISABLE = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
56 PRE_DPS_BOOT = 0x57, /* Enable output at boot if 1. */
58 #define PRE_DPS_STRIDE 0x10
60 enum rdtech_dps_protect_state {
67 enum rdtech_dps_regulation_mode {
72 enum rdtech_rd_register {
73 REG_RD_MODEL = 0, /* u16 */
74 REG_RD_SERIAL = 1, /* u32 */
75 REG_RD_FIRMWARE = 3, /* u16 */
76 REG_RD_TEMP_INT = 4, /* 2x u16 */
77 REG_RD_TEMP_INT_F = 6, /* 2x u16 */
78 REG_RD_VOLT_TGT = 8, /* u16 */
79 REG_RD_CURR_LIM = 9, /* u16 */
80 REG_RD_VOLTAGE = 10, /* u16 */
81 REG_RD_CURRENT = 11, /* u16 */
82 REG_RD_ENERGY = 12, /* u16 */
83 REG_RD_POWER = 13, /* u16 */
84 REG_RD_VOLT_IN = 14, /* u16 */
85 REG_RD_PROTECT = 16, /* u16 */
86 REG_RD_REGULATION = 17, /* u16 */
87 REG_RD_ENABLE = 18, /* u16 */
89 * Battery at 32 == 0x20 pp:
90 * Mode, voltage, temperature, capacity, energy.
93 * Date/time at 48 == 0x30 pp:
94 * Year, month, day, hour, minute, second.
96 /* Backlight at 72 == 0x48. */
97 REG_RD_OVP_THR = 82, /* 0x52 */
98 REG_RD_OCP_THR = 83, /* 0x53 */
99 /* One "live" slot and 9 "memory" positions. */
100 REG_RD_START_MEM = 84, /* 0x54 */
103 /* Retries failed modbus read attempts for improved reliability. */
104 static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
105 int address, int nb_registers, uint16_t *registers)
112 ret = sr_modbus_read_holding_registers(modbus,
113 address, nb_registers, registers);
121 /* Set one 16bit register. LE format for DPS devices. */
122 static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
123 uint16_t address, uint16_t value)
125 struct dev_context *devc;
126 struct sr_modbus_dev_inst *modbus;
127 uint16_t registers[1];
134 wrptr = (void *)registers;
135 write_u16le(wrptr, value);
137 g_mutex_lock(&devc->rw_mutex);
138 ret = sr_modbus_write_multiple_registers(modbus, address,
139 ARRAY_SIZE(registers), registers);
140 g_mutex_unlock(&devc->rw_mutex);
145 /* Set one 16bit register. BE format for RD devices. */
146 static int rdtech_rd_set_reg(const struct sr_dev_inst *sdi,
147 uint16_t address, uint16_t value)
149 struct dev_context *devc;
150 struct sr_modbus_dev_inst *modbus;
151 uint16_t registers[1];
158 wrptr = (void *)registers;
159 write_u16be(wrptr, value);
161 g_mutex_lock(&devc->rw_mutex);
162 ret = sr_modbus_write_multiple_registers(modbus, address,
163 ARRAY_SIZE(registers), registers);
164 g_mutex_unlock(&devc->rw_mutex);
169 /* Get DPS model number and firmware version from a connected device. */
170 SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
171 enum rdtech_dps_model_type model_type,
172 uint16_t *model, uint16_t *version, uint32_t *serno)
174 uint16_t registers[4];
176 const uint8_t *rdptr;
179 * No mutex here because when the routine executes then the
180 * device instance was not created yet (probe phase).
182 switch (model_type) {
184 /* Get the MODEL and VERSION registers. */
185 ret = rdtech_dps_read_holding_registers(modbus,
186 REG_DPS_MODEL, 2, registers);
189 rdptr = (void *)registers;
190 *model = read_u16le_inc(&rdptr);
191 *version = read_u16le_inc(&rdptr);
193 sr_info("RDTech DPS/DPH model: %u version: %u",
197 /* Get the MODEL, SERIAL, and FIRMWARE registers. */
198 ret = rdtech_dps_read_holding_registers(modbus,
199 REG_RD_MODEL, 4, registers);
202 rdptr = (void *)registers;
203 *model = read_u16be_inc(&rdptr) / 10;
204 *serno = read_u32be_inc(&rdptr);
205 *version = read_u16be_inc(&rdptr);
206 sr_info("RDTech RD model: %u version: %u, serno %u",
207 *model, *version, *serno);
210 sr_err("Unexpected RDTech PSU device type. Programming error?");
216 /* Send a measured value to the session feed. */
217 static int send_value(const struct sr_dev_inst *sdi,
218 struct sr_channel *ch, float value,
219 enum sr_mq mq, enum sr_mqflag mqflags,
220 enum sr_unit unit, int digits)
222 struct sr_datafeed_packet packet;
223 struct sr_datafeed_analog analog;
224 struct sr_analog_encoding encoding;
225 struct sr_analog_meaning meaning;
226 struct sr_analog_spec spec;
229 sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
230 analog.meaning->channels = g_slist_append(NULL, ch);
231 analog.num_samples = 1;
232 analog.data = &value;
233 analog.meaning->mq = mq;
234 analog.meaning->mqflags = mqflags;
235 analog.meaning->unit = unit;
237 packet.type = SR_DF_ANALOG;
238 packet.payload = &analog;
239 ret = sr_session_send(sdi, &packet);
241 g_slist_free(analog.meaning->channels);
247 * Get the device's current state. Exhaustively, relentlessly.
248 * Concentrate all details of communication in the physical transport,
249 * register layout interpretation, and potential model dependency in
250 * this central spot, to simplify maintenance.
252 * TODO Optionally limit the transfer volume depending on caller's spec
253 * which detail level is desired? Is 10 registers each 16bits an issue
254 * when the UART bitrate is only 9600bps?
256 SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
257 struct rdtech_dps_state *state)
259 struct dev_context *devc;
260 struct sr_modbus_dev_inst *modbus;
261 uint16_t registers[12];
263 const uint8_t *rdptr;
264 uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
265 uint16_t reg_val, reg_state, out_state, ovpset_raw, ocpset_raw;
266 gboolean is_lock, is_out_enabled, is_reg_cc;
267 gboolean uses_ovp, uses_ocp;
268 float volt_target, curr_limit;
269 float ovp_threshold, ocp_threshold;
270 float curr_voltage, curr_current, curr_power;
272 if (!sdi || !sdi->priv || !sdi->conn)
279 switch (devc->model->model_type) {
282 * Transfer a chunk of registers in a single call. It's
283 * unfortunate that the model dependency and the sparse
284 * register map force us to open code addresses, sizes,
285 * and the sequence of the registers and how to interpret
286 * their bit fields. But then this is not too unusual for
287 * a hardware specific device driver ...
289 * TODO Optionally reduce the transfer volume depending
290 * on the caller specified state query context.
292 g_mutex_lock(&devc->rw_mutex);
293 ret = rdtech_dps_read_holding_registers(modbus,
294 REG_DPS_USET, 10, registers);
295 g_mutex_unlock(&devc->rw_mutex);
299 /* Interpret the registers' values. */
300 rdptr = (const void *)registers;
301 uset_raw = read_u16le_inc(&rdptr);
302 volt_target = uset_raw / devc->voltage_multiplier;
303 iset_raw = read_u16le_inc(&rdptr);
304 curr_limit = iset_raw / devc->current_multiplier;
305 uout_raw = read_u16le_inc(&rdptr);
306 curr_voltage = uout_raw / devc->voltage_multiplier;
307 iout_raw = read_u16le_inc(&rdptr);
308 curr_current = iout_raw / devc->current_multiplier;
309 power_raw = read_u16le_inc(&rdptr);
310 curr_power = power_raw / 100.0f;
311 (void)read_u16le_inc(&rdptr); /* UIN */
312 reg_val = read_u16le_inc(&rdptr); /* LOCK */
313 is_lock = reg_val != 0;
314 reg_val = read_u16le_inc(&rdptr); /* PROTECT */
315 uses_ovp = reg_val == STATE_OVP;
316 uses_ocp = reg_val == STATE_OCP;
317 reg_state = read_u16le_inc(&rdptr); /* CV_CC */
318 is_reg_cc = reg_state == MODE_CC;
319 out_state = read_u16le_inc(&rdptr); /* ENABLE */
320 is_out_enabled = out_state != 0;
322 /* Transfer another chunk of registers in a single call. */
323 g_mutex_lock(&devc->rw_mutex);
324 ret = rdtech_dps_read_holding_registers(modbus,
325 PRE_DPS_OVPSET, 2, registers);
326 g_mutex_unlock(&devc->rw_mutex);
330 /* Interpret the second registers chunk's values. */
331 rdptr = (const void *)registers;
332 ovpset_raw = read_u16le_inc(&rdptr); /* PRE OVPSET */
333 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
334 ocpset_raw = read_u16le_inc(&rdptr); /* PRE OCPSET */
335 ocp_threshold = ocpset_raw * devc->current_multiplier;
340 /* Retrieve a set of adjacent registers. */
341 g_mutex_lock(&devc->rw_mutex);
342 ret = rdtech_dps_read_holding_registers(modbus,
343 REG_RD_VOLT_TGT, 11, registers);
344 g_mutex_unlock(&devc->rw_mutex);
348 /* Interpret the registers' raw content. */
349 rdptr = (const void *)registers;
350 uset_raw = read_u16be_inc(&rdptr); /* USET */
351 volt_target = uset_raw / devc->voltage_multiplier;
352 iset_raw = read_u16be_inc(&rdptr); /* ISET */
353 curr_limit = iset_raw / devc->current_multiplier;
354 uout_raw = read_u16be_inc(&rdptr); /* UOUT */
355 curr_voltage = uout_raw / devc->voltage_multiplier;
356 iout_raw = read_u16be_inc(&rdptr); /* IOUT */
357 curr_current = iout_raw / devc->current_multiplier;
358 (void)read_u16be_inc(&rdptr); /* ENERGY */
359 power_raw = read_u16be_inc(&rdptr); /* POWER */
360 curr_power = power_raw / 100.0f;
361 (void)read_u16be_inc(&rdptr); /* VOLT_IN */
362 (void)read_u16be_inc(&rdptr);
363 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
364 uses_ovp = reg_val == STATE_OVP;
365 uses_ocp = reg_val == STATE_OCP;
366 reg_state = read_u16be_inc(&rdptr); /* REGULATION */
367 is_reg_cc = reg_state == MODE_CC;
368 out_state = read_u16be_inc(&rdptr); /* ENABLE */
369 is_out_enabled = out_state != 0;
371 /* Retrieve a set of adjacent registers. */
372 g_mutex_lock(&devc->rw_mutex);
373 ret = rdtech_dps_read_holding_registers(modbus,
374 REG_RD_OVP_THR, 2, registers);
375 g_mutex_unlock(&devc->rw_mutex);
379 /* Interpret the registers' raw content. */
380 rdptr = (const void *)registers;
381 ovpset_raw = read_u16be_inc(&rdptr); /* OVP THR */
382 ovp_threshold = ovpset_raw / devc->voltage_multiplier;
383 ocpset_raw = read_u16be_inc(&rdptr); /* OCP THR */
384 ocp_threshold = ocpset_raw / devc->current_multiplier;
386 /* Details which we cannot query from the device. */
392 /* ShouldNotHappen(TM). Probe should have failed. */
396 /* Store gathered details in the high level container. */
397 memset(state, 0, sizeof(*state));
398 state->lock = is_lock;
399 state->mask |= STATE_LOCK;
400 state->output_enabled = is_out_enabled;
401 state->mask |= STATE_OUTPUT_ENABLED;
402 state->regulation_cc = is_reg_cc;
403 state->mask |= STATE_REGULATION_CC;
404 state->protect_ovp = uses_ovp;
405 state->mask |= STATE_PROTECT_OVP;
406 state->protect_ocp = uses_ocp;
407 state->mask |= STATE_PROTECT_OCP;
408 state->protect_enabled = TRUE;
409 state->mask |= STATE_PROTECT_ENABLED;
410 state->voltage_target = volt_target;
411 state->mask |= STATE_VOLTAGE_TARGET;
412 state->current_limit = curr_limit;
413 state->mask |= STATE_CURRENT_LIMIT;
414 state->ovp_threshold = ovp_threshold;
415 state->mask |= STATE_OVP_THRESHOLD;
416 state->ocp_threshold = ocp_threshold;
417 state->mask |= STATE_OCP_THRESHOLD;
418 state->voltage = curr_voltage;
419 state->mask |= STATE_VOLTAGE;
420 state->current = curr_current;
421 state->mask |= STATE_CURRENT;
422 state->power = curr_power;
423 state->mask |= STATE_POWER;
428 /* Setup device's parameters. Selectively, from caller specs. */
429 SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
430 struct rdtech_dps_state *state)
432 struct dev_context *devc;
436 if (!sdi || !sdi->priv || !sdi->conn)
442 /* Only a subset of known values is settable. */
443 if (state->mask & STATE_OUTPUT_ENABLED) {
444 reg_value = state->output_enabled ? 1 : 0;
445 switch (devc->model->model_type) {
447 ret = rdtech_dps_set_reg(sdi, REG_DPS_ENABLE, reg_value);
452 ret = rdtech_rd_set_reg(sdi, REG_RD_ENABLE, reg_value);
460 if (state->mask & STATE_VOLTAGE_TARGET) {
461 reg_value = state->voltage_target * devc->voltage_multiplier;
462 switch (devc->model->model_type) {
464 ret = rdtech_dps_set_reg(sdi, REG_DPS_USET, reg_value);
469 ret = rdtech_rd_set_reg(sdi, REG_RD_VOLT_TGT, reg_value);
477 if (state->mask & STATE_CURRENT_LIMIT) {
478 reg_value = state->current_limit * devc->current_multiplier;
479 switch (devc->model->model_type) {
481 ret = rdtech_dps_set_reg(sdi, REG_DPS_ISET, reg_value);
486 ret = rdtech_rd_set_reg(sdi, REG_RD_CURR_LIM, reg_value);
494 if (state->mask & STATE_OVP_THRESHOLD) {
495 reg_value = state->ovp_threshold * devc->voltage_multiplier;
496 switch (devc->model->model_type) {
498 ret = rdtech_dps_set_reg(sdi, PRE_DPS_OVPSET, reg_value);
503 ret = rdtech_rd_set_reg(sdi, REG_RD_OVP_THR, reg_value);
511 if (state->mask & STATE_OCP_THRESHOLD) {
512 reg_value = state->ocp_threshold * devc->current_multiplier;
513 switch (devc->model->model_type) {
515 ret = rdtech_dps_set_reg(sdi, PRE_DPS_OCPSET, reg_value);
520 ret = rdtech_rd_set_reg(sdi, REG_RD_OCP_THR, reg_value);
528 if (state->mask & STATE_LOCK) {
529 switch (devc->model->model_type) {
531 reg_value = state->lock ? 1 : 0;
532 ret = rdtech_dps_set_reg(sdi, REG_DPS_LOCK, reg_value);
537 /* Do nothing, _and_ silently succeed. */
547 /* Get the current state when acquisition starts. */
548 SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
550 struct dev_context *devc;
551 struct rdtech_dps_state state;
554 ret = rdtech_dps_get_state(sdi, &state);
558 if (state.mask & STATE_PROTECT_OVP)
559 devc->curr_ovp_state = state.protect_ovp;
560 if (state.mask & STATE_PROTECT_OCP)
561 devc->curr_ocp_state = state.protect_ocp;
562 if (state.mask & STATE_REGULATION_CC)
563 devc->curr_cc_state = state.regulation_cc;
564 if (state.mask & STATE_OUTPUT_ENABLED)
565 devc->curr_out_state = state.output_enabled;
570 /* Get measurements, track state changes during acquisition. */
571 SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
573 struct sr_dev_inst *sdi;
574 struct dev_context *devc;
575 struct rdtech_dps_state state;
577 struct sr_channel *ch;
578 const char *regulation_text;
588 /* Get the device's current state. */
589 ret = rdtech_dps_get_state(sdi, &state);
594 /* Submit measurement data to the session feed. */
595 std_session_send_df_frame_begin(sdi);
596 ch = g_slist_nth_data(sdi->channels, 0);
597 send_value(sdi, ch, state.voltage,
598 SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
599 devc->model->voltage_digits);
600 ch = g_slist_nth_data(sdi->channels, 1);
601 send_value(sdi, ch, state.current,
602 SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
603 devc->model->current_digits);
604 ch = g_slist_nth_data(sdi->channels, 2);
605 send_value(sdi, ch, state.power,
606 SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
607 std_session_send_df_frame_end(sdi);
609 /* Check for state changes. */
610 if (devc->curr_ovp_state != state.protect_ovp) {
611 (void)sr_session_send_meta(sdi,
612 SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
613 g_variant_new_boolean(state.protect_ovp));
614 devc->curr_ovp_state = state.protect_ovp;
616 if (devc->curr_ocp_state != state.protect_ocp) {
617 (void)sr_session_send_meta(sdi,
618 SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
619 g_variant_new_boolean(state.protect_ocp));
620 devc->curr_ocp_state = state.protect_ocp;
622 if (devc->curr_cc_state != state.regulation_cc) {
623 regulation_text = state.regulation_cc ? "CC" : "CV";
624 (void)sr_session_send_meta(sdi, SR_CONF_REGULATION,
625 g_variant_new_string(regulation_text));
626 devc->curr_cc_state = state.regulation_cc;
628 if (devc->curr_out_state != state.output_enabled) {
629 (void)sr_session_send_meta(sdi, SR_CONF_ENABLED,
630 g_variant_new_boolean(state.output_enabled));
631 devc->curr_out_state = state.output_enabled;
634 /* Check optional acquisition limits. */
635 sr_sw_limits_update_samples_read(&devc->limits, 1);
636 if (sr_sw_limits_check(&devc->limits)) {
637 sr_dev_acquisition_stop(sdi);