]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/rdtech-dps/protocol.c
rdtech-dps: add support for RD6006P, RD6012P and RD6024
[libsigrok.git] / src / hardware / rdtech-dps / protocol.c
index 83090d86ac7c2253c6662a305b7beab10015df67..08623325b90c19786cbf673baad7973c26286dd0 100644 (file)
 
 #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 */
@@ -69,6 +71,10 @@ enum rdtech_dps_regulation_mode {
        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 */
@@ -85,6 +91,8 @@ enum rdtech_rd_register {
        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.
@@ -108,12 +116,12 @@ static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
        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;
 }
@@ -200,7 +208,7 @@ SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
                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",
@@ -213,6 +221,48 @@ SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
        /* 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,
@@ -259,13 +309,14 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
        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;
@@ -308,6 +359,13 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
        (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:
                /*
@@ -319,8 +377,8 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
                 * 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;
@@ -369,7 +427,11 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
                /* 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;
@@ -396,6 +458,10 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
                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);
@@ -456,6 +522,10 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
        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;
 }
@@ -575,6 +645,34 @@ SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
                        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;
 }
@@ -589,6 +687,7 @@ SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
        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)
@@ -602,6 +701,10 @@ SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
                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;
 }
@@ -635,11 +738,11 @@ SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
        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);
@@ -669,6 +772,13 @@ SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
                        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);