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_USET = 0x00, /* Mirror of 0x50 */
30 REG_ISET = 0x01, /* Mirror of 0x51 */
39 REG_BACKLIGHT = 0x0A, /* Mirror of 0x55 */
43 REG_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.
55 PRE_DISABLE = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
56 PRE_BOOT = 0x57, /* Enable output at boot if 1. */
58 #define REG_PRESET_STRIDE 0x10
60 enum rdtech_dps_protect_state {
67 enum rdtech_dps_regulation_mode {
72 /* Retries failed modbus read attempts for improved reliability. */
73 static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
74 int address, int nb_registers, uint16_t *registers)
81 ret = sr_modbus_read_holding_registers(modbus,
82 address, nb_registers, registers);
90 /* Get one 16bit register. */
91 static int rdtech_dps_get_reg(const struct sr_dev_inst *sdi,
92 uint16_t address, uint16_t *value)
94 struct dev_context *devc;
95 struct sr_modbus_dev_inst *modbus;
96 uint16_t registers[1];
103 g_mutex_lock(&devc->rw_mutex);
104 ret = rdtech_dps_read_holding_registers(modbus,
105 address, ARRAY_SIZE(registers), registers);
106 g_mutex_unlock(&devc->rw_mutex);
108 rdptr = (void *)registers;
109 *value = read_u16le(rdptr);
114 /* Set one 16bit register. */
115 static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
116 uint16_t address, uint16_t value)
118 struct dev_context *devc;
119 struct sr_modbus_dev_inst *modbus;
120 uint16_t registers[1];
127 wrptr = (void *)registers;
128 write_u16le(wrptr, value);
130 g_mutex_lock(&devc->rw_mutex);
131 ret = sr_modbus_write_multiple_registers(modbus, address,
132 ARRAY_SIZE(registers), registers);
133 g_mutex_unlock(&devc->rw_mutex);
138 /* Get DPS model number and firmware version from a connected device. */
139 SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
140 uint16_t *model, uint16_t *version)
142 uint16_t registers[REG_VERSION + 1 - REG_MODEL];
144 const uint8_t *rdptr;
146 /* Silence a compiler warning about an unused routine. */
147 (void)rdtech_dps_get_reg;
150 * Get the MODEL and VERSION registers. No mutex here, because
151 * there is no sr_dev_inst when this function is called.
153 ret = rdtech_dps_read_holding_registers(modbus,
154 REG_MODEL, ARRAY_SIZE(registers), registers);
158 rdptr = (void *)registers;
159 *model = read_u16le_inc(&rdptr);
160 *version = read_u16le_inc(&rdptr);
161 sr_info("RDTech DPS PSU model: %u version: %u", *model, *version);
166 /* Send a measured value to the session feed. */
167 static int send_value(const struct sr_dev_inst *sdi,
168 struct sr_channel *ch, float value,
169 enum sr_mq mq, enum sr_mqflag mqflags,
170 enum sr_unit unit, int digits)
172 struct sr_datafeed_packet packet;
173 struct sr_datafeed_analog analog;
174 struct sr_analog_encoding encoding;
175 struct sr_analog_meaning meaning;
176 struct sr_analog_spec spec;
179 sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
180 analog.meaning->channels = g_slist_append(NULL, ch);
181 analog.num_samples = 1;
182 analog.data = &value;
183 analog.meaning->mq = mq;
184 analog.meaning->mqflags = mqflags;
185 analog.meaning->unit = unit;
187 packet.type = SR_DF_ANALOG;
188 packet.payload = &analog;
189 ret = sr_session_send(sdi, &packet);
191 g_slist_free(analog.meaning->channels);
197 * Get the device's current state. Exhaustively, relentlessly.
198 * Concentrate all details of communication in the physical transport,
199 * register layout interpretation, and potential model dependency in
200 * this central spot, to simplify maintenance.
202 * TODO Optionally limit the transfer volume depending on caller's spec
203 * which detail level is desired? Is 10 registers each 16bits an issue
204 * when the UART bitrate is only 9600bps?
206 SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
207 struct rdtech_dps_state *state)
209 struct dev_context *devc;
210 struct sr_modbus_dev_inst *modbus;
211 uint16_t registers[REG_ENABLE + 1 - REG_USET];
213 const uint8_t *rdptr;
214 uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
215 uint16_t reg_val, reg_state, out_state, ovpset_raw, ocpset_raw;
216 gboolean is_lock, is_out_enabled, is_reg_cc;
217 gboolean uses_ovp, uses_ocp;
218 float volt_target, curr_limit;
219 float ovp_threshold, ocp_threshold;
220 float curr_voltage, curr_current, curr_power;
222 if (!sdi || !sdi->priv || !sdi->conn)
229 /* Transfer a chunk of registers in a single call. */
230 g_mutex_lock(&devc->rw_mutex);
231 ret = rdtech_dps_read_holding_registers(modbus,
232 REG_USET, ARRAY_SIZE(registers), registers);
233 g_mutex_unlock(&devc->rw_mutex);
237 /* Interpret the registers' values. */
238 rdptr = (const void *)registers;
239 uset_raw = read_u16le_inc(&rdptr);
240 volt_target = uset_raw / devc->voltage_multiplier;
241 iset_raw = read_u16le_inc(&rdptr);
242 curr_limit = iset_raw / devc->current_multiplier;
243 uout_raw = read_u16le_inc(&rdptr);
244 curr_voltage = uout_raw / devc->voltage_multiplier;
245 iout_raw = read_u16le_inc(&rdptr);
246 curr_current = iout_raw / devc->current_multiplier;
247 power_raw = read_u16le_inc(&rdptr);
248 curr_power = power_raw / 100.0f;
249 (void)read_u16le_inc(&rdptr); /* UIN */
250 reg_val = read_u16le_inc(&rdptr); /* LOCK */
251 is_lock = reg_val != 0;
252 reg_val = read_u16le_inc(&rdptr); /* PROTECT */
253 uses_ovp = reg_val == STATE_OVP;
254 uses_ocp = reg_val == STATE_OCP;
255 reg_state = read_u16le_inc(&rdptr); /* CV_CC */
256 is_reg_cc = reg_state == MODE_CC;
257 out_state = read_u16le_inc(&rdptr); /* ENABLE */
258 is_out_enabled = out_state != 0;
261 * Transfer another chunk of registers in a single call.
262 * TODO Unfortunately this call site open codes a fixed number
263 * of registers to read. But there is already some leakage of
264 * the register layout in this routine, and adding more device
265 * types in the future will make things "worse". So accept it.
267 g_mutex_lock(&devc->rw_mutex);
268 ret = rdtech_dps_read_holding_registers(modbus,
269 PRE_OVPSET, 2, registers);
270 g_mutex_unlock(&devc->rw_mutex);
274 /* Interpret the registers' values. */
275 rdptr = (const void *)registers;
276 ovpset_raw = read_u16le_inc(&rdptr); /* PRE OVPSET */
277 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
278 ocpset_raw = read_u16le_inc(&rdptr); /* PRE OCPSET */
279 ocp_threshold = ocpset_raw * devc->current_multiplier;
281 /* Store gathered details in the high level container. */
282 memset(state, 0, sizeof(*state));
283 state->lock = is_lock;
284 state->mask |= STATE_LOCK;
285 state->output_enabled = is_out_enabled;
286 state->mask |= STATE_OUTPUT_ENABLED;
287 state->regulation_cc = is_reg_cc;
288 state->mask |= STATE_REGULATION_CC;
289 state->protect_ovp = uses_ovp;
290 state->mask |= STATE_PROTECT_OVP;
291 state->protect_ocp = uses_ocp;
292 state->mask |= STATE_PROTECT_OCP;
293 state->protect_enabled = TRUE;
294 state->mask |= STATE_PROTECT_ENABLED;
295 state->voltage_target = volt_target;
296 state->mask |= STATE_VOLTAGE_TARGET;
297 state->current_limit = curr_limit;
298 state->mask |= STATE_CURRENT_LIMIT;
299 state->ovp_threshold = ovp_threshold;
300 state->mask |= STATE_OVP_THRESHOLD;
301 state->ocp_threshold = ocp_threshold;
302 state->mask |= STATE_OCP_THRESHOLD;
303 state->voltage = curr_voltage;
304 state->mask |= STATE_VOLTAGE;
305 state->current = curr_current;
306 state->mask |= STATE_CURRENT;
307 state->power = curr_power;
308 state->mask |= STATE_POWER;
313 /* Setup device's parameters. Selectively, from caller specs. */
314 SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
315 struct rdtech_dps_state *state)
317 struct dev_context *devc;
321 if (!sdi || !sdi->priv || !sdi->conn)
327 /* Only a subset of known values is settable. */
328 if (state->mask & STATE_OUTPUT_ENABLED) {
329 reg_value = state->output_enabled ? 1 : 0;
330 ret = rdtech_dps_set_reg(sdi, REG_ENABLE, reg_value);
334 if (state->mask & STATE_VOLTAGE_TARGET) {
335 reg_value = state->voltage_target * devc->voltage_multiplier;
336 ret = rdtech_dps_set_reg(sdi, REG_USET, reg_value);
340 if (state->mask & STATE_CURRENT_LIMIT) {
341 reg_value = state->current_limit * devc->current_multiplier;
342 ret = rdtech_dps_set_reg(sdi, REG_ISET, reg_value);
346 if (state->mask & STATE_OVP_THRESHOLD) {
347 reg_value = state->ovp_threshold * devc->voltage_multiplier;
348 ret = rdtech_dps_set_reg(sdi, PRE_OVPSET, reg_value);
352 if (state->mask & STATE_OCP_THRESHOLD) {
353 reg_value = state->ocp_threshold * devc->current_multiplier;
354 ret = rdtech_dps_set_reg(sdi, PRE_OCPSET, reg_value);
358 if (state->mask & STATE_LOCK) {
359 reg_value = state->lock ? 1 : 0;
360 ret = rdtech_dps_set_reg(sdi, REG_LOCK, reg_value);
368 /* Get the current state when acquisition starts. */
369 SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
371 struct dev_context *devc;
372 struct rdtech_dps_state state;
375 ret = rdtech_dps_get_state(sdi, &state);
379 if (state.mask & STATE_PROTECT_OVP)
380 devc->curr_ovp_state = state.protect_ovp;
381 if (state.mask & STATE_PROTECT_OCP)
382 devc->curr_ocp_state = state.protect_ocp;
383 if (state.mask & STATE_REGULATION_CC)
384 devc->curr_cc_state = state.regulation_cc;
385 if (state.mask & STATE_OUTPUT_ENABLED)
386 devc->curr_out_state = state.output_enabled;
391 /* Get measurements, track state changes during acquisition. */
392 SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
394 struct sr_dev_inst *sdi;
395 struct dev_context *devc;
396 struct rdtech_dps_state state;
398 struct sr_channel *ch;
399 const char *regulation_text;
409 /* Get the device's current state. */
410 ret = rdtech_dps_get_state(sdi, &state);
415 /* Submit measurement data to the session feed. */
416 std_session_send_df_frame_begin(sdi);
417 ch = g_slist_nth_data(sdi->channels, 0);
418 send_value(sdi, ch, state.voltage,
419 SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
420 devc->model->voltage_digits);
421 ch = g_slist_nth_data(sdi->channels, 1);
422 send_value(sdi, ch, state.current,
423 SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
424 devc->model->current_digits);
425 ch = g_slist_nth_data(sdi->channels, 2);
426 send_value(sdi, ch, state.power,
427 SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
428 std_session_send_df_frame_end(sdi);
430 /* Check for state changes. */
431 if (devc->curr_ovp_state != state.protect_ovp) {
432 (void)sr_session_send_meta(sdi,
433 SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
434 g_variant_new_boolean(state.protect_ovp));
435 devc->curr_ovp_state = state.protect_ovp;
437 if (devc->curr_ocp_state != state.protect_ocp) {
438 (void)sr_session_send_meta(sdi,
439 SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
440 g_variant_new_boolean(state.protect_ocp));
441 devc->curr_ocp_state = state.protect_ocp;
443 if (devc->curr_cc_state != state.regulation_cc) {
444 regulation_text = state.regulation_cc ? "CC" : "CV";
445 (void)sr_session_send_meta(sdi, SR_CONF_REGULATION,
446 g_variant_new_string(regulation_text));
447 devc->curr_cc_state = state.regulation_cc;
449 if (devc->curr_out_state != state.output_enabled) {
450 (void)sr_session_send_meta(sdi, SR_CONF_ENABLED,
451 g_variant_new_boolean(state.output_enabled));
452 devc->curr_out_state = state.output_enabled;
455 /* Check optional acquisition limits. */
456 sr_sw_limits_update_samples_read(&devc->limits, 1);
457 if (sr_sw_limits_check(&devc->limits)) {
458 sr_dev_acquisition_stop(sdi);