#include <config.h>
-#include <math.h>
#include <string.h>
#include "protocol.h"
SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
};
-/* Model ID, model name, max current/voltage/power, current/voltage digits. */
+static const uint32_t devopts_w_range[] = {
+ SR_CONF_CONTINUOUS,
+ SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_VOLTAGE | SR_CONF_GET,
+ SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_CURRENT | SR_CONF_GET,
+ SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+ SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_REGULATION | SR_CONF_GET,
+ SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+ SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+ SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+ SR_CONF_RANGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
+
+/* range name, max current/voltage/power, current/voltage digits */
+static struct rdtech_dps_range ranges_dps3005[] = {
+ { "5A", 5, 30, 160, 3, 2 }
+};
+
+static struct rdtech_dps_range ranges_dps5005[] = {
+ { "5A", 5, 50, 250, 3, 2 }
+};
+
+static struct rdtech_dps_range ranges_dps5015[] = {
+ { "15A", 15, 50, 750, 2, 2 }
+};
+
+static struct rdtech_dps_range ranges_dps5020[] = {
+ { "20A", 20, 50, 1000, 2, 2 }
+};
+
+static struct rdtech_dps_range ranges_dps8005[] = {
+ { "5A", 5, 80, 408, 3, 2 }
+};
+
+static struct rdtech_dps_range ranges_rd6006[] = {
+ { "6A", 6, 60, 360, 3, 2 }
+};
+
+static struct rdtech_dps_range ranges_rd6006p[] = {
+ { "6A", 6, 60, 360, 4, 3 }
+};
+
+static struct rdtech_dps_range ranges_rd6012[] = {
+ { "12A", 12, 60, 720, 2, 2 }
+};
+
+/*
+ * Current digits for RD6012P is 4 for the 6A range (RTU reg 20 = 0) and
+ * 3 for the 12A range (RTU reg 20 = 1)
+ */
+static struct rdtech_dps_range ranges_rd6012p[] = {
+ { "6A", 6, 60, 360, 4, 3 },
+ { "12A", 12, 60, 720, 3, 3 }
+};
+
+static struct rdtech_dps_range ranges_rd6018[] = {
+ { "18A", 18, 60, 1080, 2, 2 }
+};
+
+static struct rdtech_dps_range ranges_rd6024[] = {
+ { "24A", 24, 60, 1440, 2, 2 }
+};
+
+/* model ID, model name, range */
static const struct rdtech_dps_model supported_models[] = {
- { MODEL_DPS, 3005, "DPS3005", 5, 30, 160, 3, 2 },
- { MODEL_DPS, 5005, "DPS5005", 5, 50, 250, 3, 2 },
- { MODEL_DPS, 5205, "DPH5005", 5, 50, 250, 3, 2 },
- { MODEL_DPS, 5015, "DPS5015", 15, 50, 750, 2, 2 },
- { MODEL_DPS, 5020, "DPS5020", 20, 50, 1000, 2, 2 },
- { MODEL_DPS, 8005, "DPS8005", 5, 80, 408, 3, 2 },
- /* All RD specs taken from the 2020.12.2 instruction manual. */
- { MODEL_RD , 6006, "RD6006" , 6, 60, 360, 3, 2 },
- { MODEL_RD , 6012, "RD6012" , 12, 60, 720, 2, 2 },
- { MODEL_RD , 6018, "RD6018" , 18, 60, 1080, 2, 2 },
+ { MODEL_DPS, 3005, "DPS3005", ARRAY_AND_SIZE(ranges_dps3005) },
+ { MODEL_DPS, 5005, "DPS5005", ARRAY_AND_SIZE(ranges_dps5005) },
+ { MODEL_DPS, 5205, "DPH5005", ARRAY_AND_SIZE(ranges_dps5005) },
+ { MODEL_DPS, 5015, "DPS5015", ARRAY_AND_SIZE(ranges_dps5015) },
+ { MODEL_DPS, 5020, "DPS5020", ARRAY_AND_SIZE(ranges_dps5020) },
+ { MODEL_DPS, 8005, "DPS8005", ARRAY_AND_SIZE(ranges_dps8005) },
+ /*
+ * Specs for models RD60nn taken from the 2020.12.2 instruction manual,
+ * specs for RD6006P from the 2021.2.26 (english) manual,
+ * specs for RD6012P from the 2021.10.26 (english) manual,
+ * and specs for RD6024P from the 2021.1.7 (english) manual.
+ */
+ { MODEL_RD, 60061, "RD6006" , ARRAY_AND_SIZE(ranges_rd6006) },
+ { MODEL_RD, 60062, "RD6006" , ARRAY_AND_SIZE(ranges_rd6006) },
+ { MODEL_RD, 60065, "RD6006P", ARRAY_AND_SIZE(ranges_rd6006p) },
+ { MODEL_RD, 60121, "RD6012" , ARRAY_AND_SIZE(ranges_rd6012) },
+ { MODEL_RD, 60125, "RD6012P", ARRAY_AND_SIZE(ranges_rd6012p) },
+ { MODEL_RD, 60181, "RD6018" , ARRAY_AND_SIZE(ranges_rd6018) },
+ { MODEL_RD, 60241, "RD6024" , ARRAY_AND_SIZE(ranges_rd6024) },
};
static struct sr_dev_driver rdtech_dps_driver_info;
devc = g_malloc0(sizeof(*devc));
sr_sw_limits_init(&devc->limits);
devc->model = model;
- devc->current_multiplier = pow(10.0, model->current_digits);
- devc->voltage_multiplier = pow(10.0, model->voltage_digits);
-
sdi->priv = devc;
+ ret = rdtech_dps_update_range(sdi);
+ if (ret != SR_OK)
+ return NULL;
return sdi;
}
return SR_ERR_DATA;
*data = g_variant_new_double(state.ocp_threshold);
break;
+ case SR_CONF_RANGE:
+ ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
+ if (ret != SR_OK)
+ return ret;
+ if (!(state.mask & STATE_RANGE))
+ return SR_ERR_DATA;
+ *data = g_variant_new_string(
+ devc->model->ranges[state.range].range_str);
+ break;
default:
return SR_ERR_NA;
}
{
struct dev_context *devc;
struct rdtech_dps_state state;
+ const char *range_str;
+ size_t i;
(void)cg;
state.ocp_threshold = g_variant_get_double(data);
state.mask |= STATE_OCP_THRESHOLD;
return rdtech_dps_set_state(sdi, &state);
+ case SR_CONF_RANGE:
+ range_str = g_variant_get_string(data, NULL);
+ for (i = 0; i < devc->model->n_ranges; ++i) {
+ if (g_strcmp0(devc->model->ranges[i].range_str, range_str)
+ == 0) {
+ state.range = i;
+ state.mask |= STATE_RANGE;
+ return rdtech_dps_set_state(sdi, &state);
+ }
+ }
+ return SR_ERR_NA;
default:
return SR_ERR_NA;
}
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
+ struct rdtech_dps_range *range;
+ GVariantBuilder gvb;
+ size_t i;
devc = (sdi) ? sdi->priv : NULL;
switch (key) {
case SR_CONF_SCAN_OPTIONS:
case SR_CONF_DEVICE_OPTIONS:
- return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+ if (devc->model->n_ranges > 1)
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
+ drvopts, devopts_w_range);
+ else
+ return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
+ drvopts, devopts);
case SR_CONF_VOLTAGE_TARGET:
- *data = std_gvar_min_max_step(0.0, devc->model->max_voltage,
+ rdtech_dps_update_range(sdi);
+ range = &devc->model->ranges[devc->curr_range];
+ *data = std_gvar_min_max_step(0.0, range->max_voltage,
1 / devc->voltage_multiplier);
break;
case SR_CONF_CURRENT_LIMIT:
- *data = std_gvar_min_max_step(0.0, devc->model->max_current,
+ rdtech_dps_update_range(sdi);
+ range = &devc->model->ranges[devc->curr_range];
+ *data = std_gvar_min_max_step(0.0, range->max_current,
1 / devc->current_multiplier);
break;
+ case SR_CONF_RANGE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < devc->model->n_ranges; ++i)
+ g_variant_builder_add(&gvb, "s",
+ devc->model->ranges[i].range_str);
+ *data = g_variant_builder_end(&gvb);
+ break;
default:
return SR_ERR_NA;
}
#include <config.h>
+#include <math.h>
#include <string.h>
#include "protocol.h"
+/* These are the Modbus RTU registers for the family of rdtech-dps devices. */
enum rdtech_dps_register {
REG_DPS_USET = 0x00, /* Mirror of 0x50 */
REG_DPS_ISET = 0x01, /* Mirror of 0x51 */
MODE_CC = 1,
};
+/*
+ * Some registers are specific to a certain device. For example,
+ * REG_RD_RANGE is specific to RD6012P.
+ */
enum rdtech_rd_register {
REG_RD_MODEL = 0, /* u16 */
REG_RD_SERIAL = 1, /* u32 */
REG_RD_PROTECT = 16, /* u16 */
REG_RD_REGULATION = 17, /* u16 */
REG_RD_ENABLE = 18, /* u16 */
+ REG_RD_PRESET = 19, /* u16 */
+ REG_RD_RANGE = 20, /* u16 */
/*
* Battery at 32 == 0x20 pp:
* Mode, voltage, temperature, capacity, energy.
int ret;
retries = 3;
- while (retries--) {
+ do {
ret = sr_modbus_read_holding_registers(modbus,
address, nb_registers, registers);
if (ret == SR_OK)
return ret;
- }
+ } while (--retries);
return ret;
}
if (ret != SR_OK)
return ret;
rdptr = (void *)registers;
- *model = read_u16be_inc(&rdptr) / 10;
+ *model = read_u16be_inc(&rdptr);
*serno = read_u32be_inc(&rdptr);
*version = read_u16be_inc(&rdptr);
sr_info("RDTech RD model: %u version: %u, serno %u",
/* UNREACH */
}
+SR_PRIV void rdtech_dps_update_multipliers(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ struct rdtech_dps_range *range;
+
+ devc = sdi->priv;
+ range = &devc->model->ranges[devc->curr_range];
+ devc->current_multiplier = pow(10.0, range->current_digits);
+ devc->voltage_multiplier = pow(10.0, range->voltage_digits);
+}
+
+/*
+ * Determine range of connected device. Don't do anything once
+ * acquisition has started (since the range will then be tracked).
+ */
+SR_PRIV int rdtech_dps_update_range(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ uint16_t range;
+ int ret;
+
+ devc = sdi->priv;
+
+ /*
+ * Only update range if there are multiple ranges and data
+ * acquisition hasn't started.
+ */
+ if (devc->model->n_ranges == 1 || devc->acquisition_started)
+ return SR_OK;
+ if (devc->model->model_type != MODEL_RD)
+ return SR_ERR;
+
+ ret = rdtech_dps_read_holding_registers(sdi->conn,
+ REG_RD_RANGE, 1, &range);
+ if (ret != SR_OK)
+ return ret;
+ devc->curr_range = range ? 1 : 0;
+ rdtech_dps_update_multipliers(sdi);
+
+ return SR_OK;
+}
+
/* Send a measured value to the session feed. */
static int send_value(const struct sr_dev_inst *sdi,
struct sr_channel *ch, float value,
struct dev_context *devc;
struct sr_modbus_dev_inst *modbus;
gboolean get_config, get_init_state, get_curr_meas;
- uint16_t registers[12];
+ uint16_t registers[14];
int ret;
const uint8_t *rdptr;
uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
uint16_t reg_val, reg_state, out_state, ovpset_raw, ocpset_raw;
gboolean is_lock, is_out_enabled, is_reg_cc;
gboolean uses_ovp, uses_ocp;
+ uint16_t range;
float volt_target, curr_limit;
float ovp_threshold, ocp_threshold;
float curr_voltage, curr_current, curr_power;
(void)get_init_state;
(void)get_curr_meas;
+ /*
+ * The model RD6012P has two voltage/current ranges. We set a
+ * default value here such that the compiler doesn't generate
+ * an uninitialized variable warning.
+ */
+ range = 0;
+
switch (devc->model->model_type) {
case MODEL_DPS:
/*
* a hardware specific device driver ...
*/
g_mutex_lock(&devc->rw_mutex);
- ret = rdtech_dps_read_holding_registers(modbus,
- REG_DPS_USET, 10, registers);
+ ret = rdtech_dps_read_holding_registers(modbus, REG_DPS_USET,
+ REG_DPS_ENABLE - REG_DPS_USET + 1, registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
/* Retrieve a set of adjacent registers. */
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
- REG_RD_VOLT_TGT, 11, registers);
+ REG_RD_VOLT_TGT,
+ devc->model->n_ranges > 1
+ ? REG_RD_RANGE - REG_RD_VOLT_TGT + 1
+ : REG_RD_ENABLE - REG_RD_VOLT_TGT + 1,
+ registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
is_reg_cc = reg_state == MODE_CC;
out_state = read_u16be_inc(&rdptr); /* ENABLE */
is_out_enabled = out_state != 0;
+ if (devc->model->n_ranges > 1) {
+ rdptr += sizeof (uint16_t); /* PRESET */
+ range = read_u16be_inc(&rdptr) ? 1 : 0; /* RANGE */
+ }
/* Retrieve a set of adjacent registers. */
g_mutex_lock(&devc->rw_mutex);
state->mask |= STATE_CURRENT;
state->power = curr_power;
state->mask |= STATE_POWER;
+ if (devc->model->n_ranges > 1) {
+ state->range = range;
+ state->mask |= STATE_RANGE;
+ }
return SR_OK;
}
return SR_ERR_ARG;
}
}
+ if (state->mask & STATE_RANGE) {
+ reg_value = state->range;
+ switch (devc->model->model_type) {
+ case MODEL_DPS:
+ if (reg_value > 0)
+ return SR_ERR_ARG;
+ break;
+ case MODEL_RD:
+ if (devc->model->n_ranges == 1)
+ /* No need to set. */
+ return SR_OK;
+ ret = rdtech_rd_set_reg(sdi, REG_RD_RANGE, reg_value);
+ if (ret != SR_OK)
+ return ret;
+ if (!devc->acquisition_started) {
+ devc->curr_range = reg_value ? 1 : 0;
+ rdtech_dps_update_multipliers(sdi);
+ }
+ /*
+ * We rely on the data acquisition to update
+ * devc->curr_range. If we do it here, there
+ * will be no range meta package.
+ */
+ break;
+ default:
+ return SR_ERR_ARG;
+ }
+ }
return SR_OK;
}
if (!sdi || !sdi->priv)
return SR_ERR_ARG;
devc = sdi->priv;
+ devc->acquisition_started = TRUE;
ret = rdtech_dps_get_state(sdi, &state, ST_CTX_PRE_ACQ);
if (ret != SR_OK)
devc->curr_cc_state = state.regulation_cc;
if (state.mask & STATE_OUTPUT_ENABLED)
devc->curr_out_state = state.output_enabled;
+ if (state.mask & STATE_RANGE) {
+ devc->curr_range = state.range;
+ rdtech_dps_update_multipliers(sdi);
+ }
return SR_OK;
}
ch = g_slist_nth_data(sdi->channels, 0);
send_value(sdi, ch, state.voltage,
SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
- devc->model->voltage_digits);
+ devc->model->ranges[devc->curr_range].voltage_digits);
ch = g_slist_nth_data(sdi->channels, 1);
send_value(sdi, ch, state.current,
SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
- devc->model->current_digits);
+ devc->model->ranges[devc->curr_range].current_digits);
ch = g_slist_nth_data(sdi->channels, 2);
send_value(sdi, ch, state.power,
SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
g_variant_new_boolean(state.output_enabled));
devc->curr_out_state = state.output_enabled;
}
+ if (devc->curr_range != state.range) {
+ (void)sr_session_send_meta(sdi, SR_CONF_RANGE,
+ g_variant_new_string(
+ devc->model->ranges[state.range].range_str));
+ devc->curr_range = state.range;
+ rdtech_dps_update_multipliers(sdi);
+ }
/* Check optional acquisition limits. */
sr_sw_limits_update_samples_read(&devc->limits, 1);