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/>.
29 /* These are the Modbus RTU registers for the DPS family of devices. */
30 enum rdtech_dps_register {
31 REG_DPS_USET = 0x00, /* Mirror of 0x50 */
32 REG_DPS_ISET = 0x01, /* Mirror of 0x51 */
38 REG_DPS_PROTECT = 0x07,
40 REG_DPS_ENABLE = 0x09,
41 REG_DPS_BACKLIGHT = 0x0A, /* Mirror of 0x55 */
43 REG_DPS_VERSION = 0x0C,
45 REG_DPS_PRESET = 0x23, /* Loads a preset into preset 0. */
48 * Add (preset * 0x10) to each of the following, for preset 1-9.
49 * Preset 0 regs below are the active output settings.
53 PRE_DPS_OVPSET = 0x52,
54 PRE_DPS_OCPSET = 0x53,
55 PRE_DPS_OPPSET = 0x54,
56 PRE_DPS_BACKLIGHT = 0x55,
57 PRE_DPS_DISABLE = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
58 PRE_DPS_BOOT = 0x57, /* Enable output at boot if 1. */
60 #define PRE_DPS_STRIDE 0x10
62 enum rdtech_dps_protect_state {
69 enum rdtech_dps_regulation_mode {
75 * These are the Modbus RTU registers for the RD family of devices.
76 * Some registers are device specific, like REG_RD_RANGE of RD6012P
77 * which could be battery related in other devices.
79 enum rdtech_rd_register {
80 REG_RD_MODEL = 0, /* u16 */
81 REG_RD_SERIAL = 1, /* u32 */
82 REG_RD_FIRMWARE = 3, /* u16 */
83 REG_RD_TEMP_INT = 4, /* 2x u16 */
84 REG_RD_TEMP_INT_F = 6, /* 2x u16 */
85 REG_RD_VOLT_TGT = 8, /* u16 */
86 REG_RD_CURR_LIM = 9, /* u16 */
87 REG_RD_VOLTAGE = 10, /* u16 */
88 REG_RD_CURRENT = 11, /* u16 */
89 REG_RD_ENERGY = 12, /* u16 */
90 REG_RD_POWER = 13, /* u16 */
91 REG_RD_VOLT_IN = 14, /* u16 */
92 REG_RD_PROTECT = 16, /* u16 */
93 REG_RD_REGULATION = 17, /* u16 */
94 REG_RD_ENABLE = 18, /* u16 */
95 REG_RD_PRESET = 19, /* u16 */
96 REG_RD_RANGE = 20, /* u16 */
98 * Battery at 32 == 0x20 pp:
99 * Mode, voltage, temperature, capacity, energy.
102 * Date/time at 48 == 0x30 pp:
103 * Year, month, day, hour, minute, second.
105 /* Backlight at 72 == 0x48. */
106 REG_RD_OVP_THR = 82, /* 0x52 */
107 REG_RD_OCP_THR = 83, /* 0x53 */
108 /* One "live" slot and 9 "memory" positions. */
109 REG_RD_START_MEM = 84, /* 0x54 */
112 /* Retries failed modbus read attempts for improved reliability. */
113 static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
114 int address, int nb_registers, uint16_t *registers)
121 ret = sr_modbus_read_holding_registers(modbus,
122 address, nb_registers, registers);
130 /* Set one 16bit register. LE format for DPS devices. */
131 static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
132 uint16_t address, uint16_t value)
134 struct dev_context *devc;
135 struct sr_modbus_dev_inst *modbus;
136 uint16_t registers[1];
143 wrptr = (void *)registers;
144 write_u16be(wrptr, value);
146 g_mutex_lock(&devc->rw_mutex);
147 ret = sr_modbus_write_multiple_registers(modbus, address,
148 ARRAY_SIZE(registers), registers);
149 g_mutex_unlock(&devc->rw_mutex);
154 /* Set one 16bit register. BE format for RD devices. */
155 static int rdtech_rd_set_reg(const struct sr_dev_inst *sdi,
156 uint16_t address, uint16_t value)
158 struct dev_context *devc;
159 struct sr_modbus_dev_inst *modbus;
160 uint16_t registers[1];
167 wrptr = (void *)registers;
168 write_u16be(wrptr, value);
170 g_mutex_lock(&devc->rw_mutex);
171 ret = sr_modbus_write_multiple_registers(modbus, address,
172 ARRAY_SIZE(registers), registers);
173 g_mutex_unlock(&devc->rw_mutex);
178 /* Get DPS model number and firmware version from a connected device. */
179 SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
180 enum rdtech_dps_model_type model_type,
181 uint16_t *model, uint16_t *version, uint32_t *serno)
183 uint16_t registers[4];
185 const uint8_t *rdptr;
188 * No mutex here because when the routine executes then the
189 * device instance was not created yet (probe phase).
191 switch (model_type) {
193 /* Get the MODEL and VERSION registers. */
194 ret = rdtech_dps_read_holding_registers(modbus,
195 REG_DPS_MODEL, 2, registers);
198 rdptr = (void *)registers;
199 *model = read_u16be_inc(&rdptr);
200 *version = read_u16be_inc(&rdptr);
202 sr_info("RDTech DPS/DPH model: %u version: %u",
206 /* Get the MODEL, SERIAL, and FIRMWARE registers. */
207 ret = rdtech_dps_read_holding_registers(modbus,
208 REG_RD_MODEL, 4, registers);
211 rdptr = (void *)registers;
212 *model = read_u16be_inc(&rdptr);
213 *serno = read_u32be_inc(&rdptr);
214 *version = read_u16be_inc(&rdptr);
215 sr_info("RDTech RD model: %u version: %u, serno %u",
216 *model, *version, *serno);
219 sr_err("Unexpected RDTech PSU device type. Programming error?");
225 SR_PRIV void rdtech_dps_update_multipliers(const struct sr_dev_inst *sdi)
227 struct dev_context *devc;
228 const struct rdtech_dps_range *range;
231 range = &devc->model->ranges[devc->curr_range];
232 devc->current_multiplier = pow(10.0, range->current_digits);
233 devc->voltage_multiplier = pow(10.0, range->voltage_digits);
237 * Determine range of connected device. Don't do anything once
238 * acquisition has started (since the range will then be tracked).
240 SR_PRIV int rdtech_dps_update_range(const struct sr_dev_inst *sdi)
242 struct dev_context *devc;
249 * Only update range if there are multiple ranges and data
250 * acquisition hasn't started.
252 if (devc->model->n_ranges == 1 || devc->acquisition_started)
254 if (devc->model->model_type != MODEL_RD)
257 ret = rdtech_dps_read_holding_registers(sdi->conn,
258 REG_RD_RANGE, 1, &range);
261 devc->curr_range = range ? 1 : 0;
262 rdtech_dps_update_multipliers(sdi);
267 /* Send a measured value to the session feed. */
268 static int send_value(const struct sr_dev_inst *sdi,
269 struct sr_channel *ch, float value,
270 enum sr_mq mq, enum sr_mqflag mqflags,
271 enum sr_unit unit, int digits)
273 struct sr_datafeed_packet packet;
274 struct sr_datafeed_analog analog;
275 struct sr_analog_encoding encoding;
276 struct sr_analog_meaning meaning;
277 struct sr_analog_spec spec;
280 sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
281 analog.meaning->channels = g_slist_append(NULL, ch);
282 analog.num_samples = 1;
283 analog.data = &value;
284 analog.meaning->mq = mq;
285 analog.meaning->mqflags = mqflags;
286 analog.meaning->unit = unit;
288 packet.type = SR_DF_ANALOG;
289 packet.payload = &analog;
290 ret = sr_session_send(sdi, &packet);
292 g_slist_free(analog.meaning->channels);
298 * Get the device's current state. Exhaustively, relentlessly.
299 * Concentrate all details of communication in the physical transport,
300 * register layout interpretation, and potential model dependency in
301 * this central spot, to simplify maintenance.
303 * TODO Optionally limit the transfer volume depending on caller's spec
304 * which detail level is desired? Is 10 registers each 16bits an issue
305 * when the UART bitrate is only 9600bps?
307 SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
308 struct rdtech_dps_state *state, enum rdtech_dps_state_context reason)
310 struct dev_context *devc;
311 struct sr_modbus_dev_inst *modbus;
312 gboolean get_config, get_init_state, get_curr_meas;
313 uint16_t registers[14];
315 const uint8_t *rdptr;
316 uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
317 uint16_t reg_val, reg_state, out_state, ovpset_raw, ocpset_raw;
318 gboolean is_lock, is_out_enabled, is_reg_cc;
319 gboolean uses_ovp, uses_ocp;
321 float volt_target, curr_limit;
322 float ovp_threshold, ocp_threshold;
323 float curr_voltage, curr_current, curr_power;
325 if (!sdi || !sdi->priv || !sdi->conn)
332 /* Determine the requested level of response detail. */
334 get_init_state = FALSE;
335 get_curr_meas = FALSE;
339 get_init_state = TRUE;
340 get_curr_meas = TRUE;
343 get_init_state = TRUE;
344 get_curr_meas = TRUE;
347 get_curr_meas = TRUE;
354 * TODO Make use of this information to reduce the transfer
355 * volume, especially on low bitrate serial connections. Though
356 * the device firmware's samplerate is probably more limiting
357 * than communication bandwidth is.
360 (void)get_init_state;
364 * The model RD6012P has two voltage/current ranges. We set a
365 * default value here such that the compiler doesn't generate
366 * an uninitialized variable warning.
370 switch (devc->model->model_type) {
373 * Transfer a chunk of registers in a single call. It's
374 * unfortunate that the model dependency and the sparse
375 * register map force us to open code addresses, sizes,
376 * and the sequence of the registers and how to interpret
377 * their bit fields. But then this is not too unusual for
378 * a hardware specific device driver ...
380 g_mutex_lock(&devc->rw_mutex);
381 ret = rdtech_dps_read_holding_registers(modbus,
382 REG_DPS_USET, REG_DPS_ENABLE - REG_DPS_USET + 1,
384 g_mutex_unlock(&devc->rw_mutex);
388 /* Interpret the registers' values. */
389 rdptr = (const void *)registers;
390 uset_raw = read_u16be_inc(&rdptr);
391 volt_target = uset_raw / devc->voltage_multiplier;
392 iset_raw = read_u16be_inc(&rdptr);
393 curr_limit = iset_raw / devc->current_multiplier;
394 uout_raw = read_u16be_inc(&rdptr);
395 curr_voltage = uout_raw / devc->voltage_multiplier;
396 iout_raw = read_u16be_inc(&rdptr);
397 curr_current = iout_raw / devc->current_multiplier;
398 power_raw = read_u16be_inc(&rdptr);
399 curr_power = power_raw / 100.0f;
400 (void)read_u16be_inc(&rdptr); /* UIN */
401 reg_val = read_u16be_inc(&rdptr); /* LOCK */
402 is_lock = reg_val != 0;
403 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
404 uses_ovp = reg_val == STATE_OVP;
405 uses_ocp = reg_val == STATE_OCP;
406 reg_state = read_u16be_inc(&rdptr); /* CV_CC */
407 is_reg_cc = reg_state == MODE_CC;
408 out_state = read_u16be_inc(&rdptr); /* ENABLE */
409 is_out_enabled = out_state != 0;
411 /* Transfer another chunk of registers in a single call. */
412 g_mutex_lock(&devc->rw_mutex);
413 ret = rdtech_dps_read_holding_registers(modbus,
414 PRE_DPS_OVPSET, 2, registers);
415 g_mutex_unlock(&devc->rw_mutex);
419 /* Interpret the second registers chunk's values. */
420 rdptr = (const void *)registers;
421 ovpset_raw = read_u16be_inc(&rdptr); /* PRE OVPSET */
422 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
423 ocpset_raw = read_u16be_inc(&rdptr); /* PRE OCPSET */
424 ocp_threshold = ocpset_raw * devc->current_multiplier;
429 /* Retrieve a set of adjacent registers. */
430 g_mutex_lock(&devc->rw_mutex);
431 ret = rdtech_dps_read_holding_registers(modbus,
433 devc->model->n_ranges > 1
434 ? REG_RD_RANGE - REG_RD_VOLT_TGT + 1
435 : REG_RD_ENABLE - REG_RD_VOLT_TGT + 1,
437 g_mutex_unlock(&devc->rw_mutex);
441 /* Interpret the registers' raw content. */
442 rdptr = (const void *)registers;
443 uset_raw = read_u16be_inc(&rdptr); /* USET */
444 volt_target = uset_raw / devc->voltage_multiplier;
445 iset_raw = read_u16be_inc(&rdptr); /* ISET */
446 curr_limit = iset_raw / devc->current_multiplier;
447 uout_raw = read_u16be_inc(&rdptr); /* UOUT */
448 curr_voltage = uout_raw / devc->voltage_multiplier;
449 iout_raw = read_u16be_inc(&rdptr); /* IOUT */
450 curr_current = iout_raw / devc->current_multiplier;
451 (void)read_u16be_inc(&rdptr); /* ENERGY */
452 power_raw = read_u16be_inc(&rdptr); /* POWER */
453 curr_power = power_raw / 100.0f;
454 (void)read_u16be_inc(&rdptr); /* VOLT_IN */
455 (void)read_u16be_inc(&rdptr);
456 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
457 uses_ovp = reg_val == STATE_OVP;
458 uses_ocp = reg_val == STATE_OCP;
459 reg_state = read_u16be_inc(&rdptr); /* REGULATION */
460 is_reg_cc = reg_state == MODE_CC;
461 out_state = read_u16be_inc(&rdptr); /* ENABLE */
462 is_out_enabled = out_state != 0;
463 if (devc->model->n_ranges > 1) {
464 (void)read_u16be_inc(&rdptr); /* PRESET */
465 range = read_u16be_inc(&rdptr) ? 1 : 0; /* RANGE */
468 /* Retrieve a set of adjacent registers. */
469 g_mutex_lock(&devc->rw_mutex);
470 ret = rdtech_dps_read_holding_registers(modbus,
471 REG_RD_OVP_THR, 2, registers);
472 g_mutex_unlock(&devc->rw_mutex);
476 /* Interpret the registers' raw content. */
477 rdptr = (const void *)registers;
478 ovpset_raw = read_u16be_inc(&rdptr); /* OVP THR */
479 ovp_threshold = ovpset_raw / devc->voltage_multiplier;
480 ocpset_raw = read_u16be_inc(&rdptr); /* OCP THR */
481 ocp_threshold = ocpset_raw / devc->current_multiplier;
483 /* Details which we cannot query from the device. */
489 /* ShouldNotHappen(TM). Probe should have failed. */
494 * Store gathered details in the high level container.
496 * TODO Make use of the caller's context. The register access
497 * code path above need not have gathered every detail in every
500 memset(state, 0, sizeof(*state));
501 state->lock = is_lock;
502 state->mask |= STATE_LOCK;
503 state->output_enabled = is_out_enabled;
504 state->mask |= STATE_OUTPUT_ENABLED;
505 state->regulation_cc = is_reg_cc;
506 state->mask |= STATE_REGULATION_CC;
507 state->protect_ovp = uses_ovp;
508 state->mask |= STATE_PROTECT_OVP;
509 state->protect_ocp = uses_ocp;
510 state->mask |= STATE_PROTECT_OCP;
511 state->protect_enabled = TRUE;
512 state->mask |= STATE_PROTECT_ENABLED;
513 state->voltage_target = volt_target;
514 state->mask |= STATE_VOLTAGE_TARGET;
515 state->current_limit = curr_limit;
516 state->mask |= STATE_CURRENT_LIMIT;
517 state->ovp_threshold = ovp_threshold;
518 state->mask |= STATE_OVP_THRESHOLD;
519 state->ocp_threshold = ocp_threshold;
520 state->mask |= STATE_OCP_THRESHOLD;
521 state->voltage = curr_voltage;
522 state->mask |= STATE_VOLTAGE;
523 state->current = curr_current;
524 state->mask |= STATE_CURRENT;
525 state->power = curr_power;
526 state->mask |= STATE_POWER;
527 if (devc->model->n_ranges > 1) {
528 state->range = range;
529 state->mask |= STATE_RANGE;
535 /* Setup device's parameters. Selectively, from caller specs. */
536 SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
537 struct rdtech_dps_state *state)
539 struct dev_context *devc;
543 if (!sdi || !sdi->priv || !sdi->conn)
549 /* Only a subset of known values is settable. */
550 if (state->mask & STATE_OUTPUT_ENABLED) {
551 reg_value = state->output_enabled ? 1 : 0;
552 switch (devc->model->model_type) {
554 ret = rdtech_dps_set_reg(sdi, REG_DPS_ENABLE, reg_value);
559 ret = rdtech_rd_set_reg(sdi, REG_RD_ENABLE, reg_value);
567 if (state->mask & STATE_VOLTAGE_TARGET) {
568 reg_value = state->voltage_target * devc->voltage_multiplier;
569 switch (devc->model->model_type) {
571 ret = rdtech_dps_set_reg(sdi, REG_DPS_USET, reg_value);
576 ret = rdtech_rd_set_reg(sdi, REG_RD_VOLT_TGT, reg_value);
584 if (state->mask & STATE_CURRENT_LIMIT) {
585 reg_value = state->current_limit * devc->current_multiplier;
586 switch (devc->model->model_type) {
588 ret = rdtech_dps_set_reg(sdi, REG_DPS_ISET, reg_value);
593 ret = rdtech_rd_set_reg(sdi, REG_RD_CURR_LIM, reg_value);
601 if (state->mask & STATE_OVP_THRESHOLD) {
602 reg_value = state->ovp_threshold * devc->voltage_multiplier;
603 switch (devc->model->model_type) {
605 ret = rdtech_dps_set_reg(sdi, PRE_DPS_OVPSET, reg_value);
610 ret = rdtech_rd_set_reg(sdi, REG_RD_OVP_THR, reg_value);
618 if (state->mask & STATE_OCP_THRESHOLD) {
619 reg_value = state->ocp_threshold * devc->current_multiplier;
620 switch (devc->model->model_type) {
622 ret = rdtech_dps_set_reg(sdi, PRE_DPS_OCPSET, reg_value);
627 ret = rdtech_rd_set_reg(sdi, REG_RD_OCP_THR, reg_value);
635 if (state->mask & STATE_LOCK) {
636 switch (devc->model->model_type) {
638 reg_value = state->lock ? 1 : 0;
639 ret = rdtech_dps_set_reg(sdi, REG_DPS_LOCK, reg_value);
644 /* Do nothing, _and_ silently succeed. */
650 if (state->mask & STATE_RANGE) {
651 reg_value = state->range;
652 switch (devc->model->model_type) {
654 /* DPS models don't support current ranges at all. */
660 * Need not set the range when the device only
661 * supports a single fixed range.
663 if (devc->model->n_ranges == 1)
665 ret = rdtech_rd_set_reg(sdi, REG_RD_RANGE, reg_value);
669 * Immediately update internal state outside of
670 * an acquisition. Assume that in-acquisition
671 * activity will update internal state. This is
672 * essential for meta package emission.
674 if (!devc->acquisition_started) {
675 devc->curr_range = reg_value ? 1 : 0;
676 rdtech_dps_update_multipliers(sdi);
687 /* Get the current state when acquisition starts. */
688 SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
690 struct dev_context *devc;
691 struct rdtech_dps_state state;
694 if (!sdi || !sdi->priv)
697 devc->acquisition_started = TRUE;
699 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_PRE_ACQ);
703 if (state.mask & STATE_PROTECT_OVP)
704 devc->curr_ovp_state = state.protect_ovp;
705 if (state.mask & STATE_PROTECT_OCP)
706 devc->curr_ocp_state = state.protect_ocp;
707 if (state.mask & STATE_REGULATION_CC)
708 devc->curr_cc_state = state.regulation_cc;
709 if (state.mask & STATE_OUTPUT_ENABLED)
710 devc->curr_out_state = state.output_enabled;
711 if (state.mask & STATE_RANGE) {
712 devc->curr_range = state.range;
713 rdtech_dps_update_multipliers(sdi);
719 /* Get measurements, track state changes during acquisition. */
720 SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
722 struct sr_dev_inst *sdi;
723 struct dev_context *devc;
724 struct rdtech_dps_state state;
726 struct sr_channel *ch;
727 const char *regulation_text, *range_text;
737 /* Get the device's current state. */
738 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_IN_ACQ);
743 /* Submit measurement data to the session feed. */
744 std_session_send_df_frame_begin(sdi);
745 ch = g_slist_nth_data(sdi->channels, 0);
746 send_value(sdi, ch, state.voltage,
747 SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
748 devc->model->ranges[devc->curr_range].voltage_digits);
749 ch = g_slist_nth_data(sdi->channels, 1);
750 send_value(sdi, ch, state.current,
751 SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
752 devc->model->ranges[devc->curr_range].current_digits);
753 ch = g_slist_nth_data(sdi->channels, 2);
754 send_value(sdi, ch, state.power,
755 SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
756 std_session_send_df_frame_end(sdi);
758 /* Check for state changes. */
759 if (devc->curr_ovp_state != state.protect_ovp) {
760 (void)sr_session_send_meta(sdi,
761 SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
762 g_variant_new_boolean(state.protect_ovp));
763 devc->curr_ovp_state = state.protect_ovp;
765 if (devc->curr_ocp_state != state.protect_ocp) {
766 (void)sr_session_send_meta(sdi,
767 SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
768 g_variant_new_boolean(state.protect_ocp));
769 devc->curr_ocp_state = state.protect_ocp;
771 if (devc->curr_cc_state != state.regulation_cc) {
772 regulation_text = state.regulation_cc ? "CC" : "CV";
773 (void)sr_session_send_meta(sdi, SR_CONF_REGULATION,
774 g_variant_new_string(regulation_text));
775 devc->curr_cc_state = state.regulation_cc;
777 if (devc->curr_out_state != state.output_enabled) {
778 (void)sr_session_send_meta(sdi, SR_CONF_ENABLED,
779 g_variant_new_boolean(state.output_enabled));
780 devc->curr_out_state = state.output_enabled;
782 if (devc->curr_range != state.range) {
783 range_text = devc->model->ranges[state.range].range_str;
784 (void)sr_session_send_meta(sdi, SR_CONF_RANGE,
785 g_variant_new_string(range_text));
786 devc->curr_range = state.range;
787 rdtech_dps_update_multipliers(sdi);
790 /* Check optional acquisition limits. */
791 sr_sw_limits_update_samples_read(&devc->limits, 1);
792 if (sr_sw_limits_check(&devc->limits)) {
793 sr_dev_acquisition_stop(sdi);