]> sigrok.org Git - libsigrok.git/commitdiff
Initial driver for Korad KDxxxxP (Velleman LABPS3005D)
authorHannu Vuolasaho <redacted>
Fri, 25 Sep 2015 14:43:17 +0000 (16:43 +0200)
committerUwe Hermann <redacted>
Tue, 13 Oct 2015 21:18:49 +0000 (23:18 +0200)
With this driver it is possible to set voltage target and current
limit. Also enabling and disabling the output is possible.

Analog output sends read back values from output. If output is
disabled analog outputs 0.00.

In protocol.c there is a g_usleep() call. This gives almost
every time enough time for PSU to parse and process input.

Multichannel devices aren't supported.

src/hardware/korad-kdxxxxp/api.c
src/hardware/korad-kdxxxxp/protocol.c
src/hardware/korad-kdxxxxp/protocol.h

index ab7905d85af7315cd1cbc3e495c4aed640d31e33..107bdc4070d258cc1cf8f986e0f01ca72f94586b 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
+static const uint32_t drvopts[] = {
+       /* Device class */
+       SR_CONF_POWER_SUPPLY,
+};
+
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+};
+
+static const uint32_t devopts[] = {
+       /* Device class */
+       SR_CONF_POWER_SUPPLY,
+       /* Acquisition modes. */
+       SR_CONF_CONTINUOUS,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+       /* Device configuration */
+       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,
+};
+
+static const struct korad_kdxxxxp_model models[] = {
+       /* Device enum, vendor, model, ID reply, channels, voltage, current */
+       {VELLEMAN_LABPS_3005D, "Velleman", "LABPS3005D",
+               "VELLEMANLABPS3005DV2.0", 1, {0, 31, 0.01}, {0, 5, 0.001}},
+       {0, NULL, NULL, NULL, 0, {0, 0, 0}, {0, 0, 0}}
+};
+
 SR_PRIV struct sr_dev_driver korad_kdxxxxp_driver_info;
 
 static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
@@ -30,18 +62,109 @@ static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
        struct drv_context *drvc;
-       GSList *devices;
-
-       (void)options;
+       struct dev_context *devc;
+       GSList *devices, *l;
+       struct sr_dev_inst *sdi;
+       struct sr_config *src;
+       const char *conn, *serialcomm;
+       struct sr_serial_dev_inst *serial;
+       char reply[50];
+       int i, model_id;
+       unsigned int len;
 
        devices = NULL;
+       conn = NULL;
+       serialcomm = NULL;
        drvc = di->context;
        drvc->instances = NULL;
 
-       /* TODO: scan for devices, either based on a SR_CONF_CONN option
-        * or on a USB scan. */
+       for (l = options; l; l = l->next) {
+               src = l->data;
+               switch (src->key) {
+               case SR_CONF_CONN:
+                       conn = g_variant_get_string(src->data, NULL);
+                       break;
+               case SR_CONF_SERIALCOMM:
+                       serialcomm = g_variant_get_string(src->data, NULL);
+                       break;
+               default:
+                       sr_err("Unknown option %d, skipping.", src->key);
+                       break;
+               }
+       }
+
+       if (!conn)
+               return NULL;
+       if (!serialcomm)
+               serialcomm = "9600/8n1";
+
+       serial = sr_serial_dev_inst_new(conn, serialcomm);
+       if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+               return NULL;
+
+       serial_flush(serial);
+
+       /* Get the device model. */
+       len = 0;
+       for (i = 0; models[i].id; i++) {
+               if (strlen(models[i].id) > len)
+                       len = strlen(models[i].id);
+       }
+       memset(&reply, 0, sizeof(reply));
+       sr_dbg("Want max %d bytes.", len);
+       if ((korad_kdxxxxp_send_cmd(serial, "*IDN?") < 0))
+               return NULL;
+
+       /* i is used here for debug purposes only. */
+       if ((i = korad_kdxxxxp_read_chars(serial, len, reply)) < 0)
+               return NULL;
+       sr_dbg("Received: %d, %s", i, reply);
+       model_id = -1;
+       for (i = 0; models[i].id; i++) {
+               if (!strcmp(models[i].id, reply))
+                       model_id = i;
+       }
+       if (model_id < 0) {
+               sr_err("Unknown model ID '%s' detected, aborting.", reply);
+               return NULL;
+       }
+       sr_dbg("Found: %s %s", models[model_id].vendor, models[model_id].name);
+
+       /* Init device instance, etc. */
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup(models[model_id].vendor);
+       sdi->model = g_strdup(models[model_id].name);
+       sdi->inst_type = SR_INST_SERIAL;
+       sdi->conn = serial;
+       sdi->driver = di;
+
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->model = &models[model_id];
+       devc->reply[5] = 0;
+       devc->req_sent_at = 0;
+       sdi->priv = devc;
+
+       /* Get current status of device. */
+       if (korad_kdxxxxp_get_all_values(serial, devc) < 0)
+               goto exit_err;
+       drvc->instances = g_slist_append(drvc->instances, sdi);
+       devices = g_slist_append(devices, sdi);
+
+       serial_close(serial);
+       if (!devices)
+               sr_serial_dev_inst_free(serial);
 
        return devices;
+
+exit_err:
+       sr_dev_inst_free(sdi);
+       g_free(devc);
+       sr_dbg("Scan failed.");
+
+       return NULL;
 }
 
 static GSList *dev_list(const struct sr_dev_driver *di)
@@ -54,120 +177,208 @@ static int dev_clear(const struct sr_dev_driver *di)
        return std_dev_clear(di, NULL);
 }
 
-static int dev_open(struct sr_dev_inst *sdi)
-{
-       (void)sdi;
-
-       /* TODO: get handle from sdi->conn and open it. */
-
-       sdi->status = SR_ST_ACTIVE;
-
-       return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
-       (void)sdi;
-
-       /* TODO: get handle from sdi->conn and close it. */
-
-       sdi->status = SR_ST_INACTIVE;
-
-       return SR_OK;
-}
-
 static int cleanup(const struct sr_dev_driver *di)
 {
        dev_clear(di);
-
-       /* TODO: free other driver resources, if any. */
-
        return SR_OK;
 }
 
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
+       struct dev_context *devc;
 
-       (void)sdi;
-       (void)data;
        (void)cg;
 
-       ret = SR_OK;
+       if (!sdi || !data)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
        switch (key) {
-       /* TODO */
+       case SR_CONF_LIMIT_SAMPLES:
+               *data = g_variant_new_uint64(devc->limit_samples);
+               break;
+       case SR_CONF_LIMIT_MSEC:
+               *data = g_variant_new_uint64(devc->limit_msec);
+               break;
+       case SR_CONF_VOLTAGE:
+               *data = g_variant_new_double(devc->voltage);
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               *data = g_variant_new_double(devc->voltage_max);
+               break;
+       case SR_CONF_CURRENT:
+               *data = g_variant_new_double(devc->current);
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               *data = g_variant_new_double(devc->current_max);
+               break;
+       case SR_CONF_ENABLED:
+               *data = g_variant_new_boolean(devc->output_enabled);
+               break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
+       struct dev_context *devc;
+       double dval;
+       gboolean bval;
 
-       (void)data;
        (void)cg;
 
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
 
-       ret = SR_OK;
+       devc = sdi->priv;
+
        switch (key) {
-       /* TODO */
+       case SR_CONF_LIMIT_MSEC:
+               if (g_variant_get_uint64(data) == 0)
+                       return SR_ERR_ARG;
+               devc->limit_msec = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               if (g_variant_get_uint64(data) == 0)
+                       return SR_ERR_ARG;
+               devc->limit_samples = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               dval = g_variant_get_double(data);
+               if (dval < devc->model->voltage[0] || dval > devc->model->voltage[1])
+                       return SR_ERR_ARG;
+               devc->voltage_max = dval;
+               devc->target = KDXXXXP_VOLTAGE_MAX;
+               if (korad_kdxxxxp_set_value(sdi->conn, devc) < 0)
+                       return SR_ERR;
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               dval = g_variant_get_double(data);
+               if (dval < devc->model->current[0] || dval > devc->model->current[1])
+                       return SR_ERR_ARG;
+               devc->current_max = dval;
+               devc->target = KDXXXXP_CURRENT_MAX;
+               if (korad_kdxxxxp_set_value(sdi->conn, devc) < 0)
+                       return SR_ERR;
+               break;
+       case SR_CONF_ENABLED:
+               bval = g_variant_get_boolean(data);
+               /* Set always so it is possible turn off with sigrok-cli. */
+               devc->output_enabled = bval;
+               devc->target = KDXXXXP_OUTPUT;
+               if (korad_kdxxxxp_set_value(sdi->conn, devc) < 0)
+                       return SR_ERR;
+               break;
        default:
-               ret = SR_ERR_NA;
+               return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
 
-       (void)sdi;
-       (void)data;
+       struct dev_context *devc;
+       GVariant *gvar;
+       GVariantBuilder gvb;
+       double dval;
+       int idx;
+
        (void)cg;
 
-       ret = SR_OK;
+       /* Always available (with or without sdi). */
+       if (key == SR_CONF_SCAN_OPTIONS) {
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+               return SR_OK;
+       }
+
+       /* Return drvopts without sdi (and devopts with sdi, see below). */
+       if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+               return SR_OK;
+       }
+
+       /* Every other key needs an sdi. */
+       if (!sdi)
+               return SR_ERR_ARG;
+
+       devc = sdi->priv;
+
        switch (key) {
-       /* TODO */
+       case SR_CONF_DEVICE_OPTIONS:
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+               /* Min, max, step. */
+               for (idx = 0; idx < 3; idx++) {
+                       dval = devc->model->voltage[idx];
+                       gvar = g_variant_new_double(dval);
+                       g_variant_builder_add_value(&gvb, gvar);
+               }
+               *data = g_variant_builder_end(&gvb);
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+               /* Min, max, step. */
+               for (idx = 0; idx < 3; idx++) {
+                       dval = devc->model->current[idx];
+                       gvar = g_variant_new_double(dval);
+                       g_variant_builder_add_value(&gvb, gvar);
+               }
+               *data = g_variant_builder_end(&gvb);
+               break;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
 {
-       (void)sdi;
-       (void)cb_data;
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
 
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
 
-       /* TODO: configure hardware, reset acquisition state, set up
-        * callbacks and send header packet. */
+       devc = sdi->priv;
+       devc->cb_data = cb_data;
+
+       /* Send header packet to the session bus. */
+       std_session_send_df_header(cb_data, LOG_PREFIX);
+
+       devc->starttime = g_get_monotonic_time();
+       devc->num_samples = 0;
+       devc->reply_pending = FALSE;
+       devc->req_sent_at = 0;
+       serial = sdi->conn;
+       serial_source_add(sdi->session, serial, G_IO_IN,
+                       KDXXXXP_POLL_INTERVAL_MS,
+                       korad_kdxxxxp_receive_data, (void *)sdi);
 
        return SR_OK;
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
-       (void)cb_data;
-
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
 
-       /* TODO: stop acquisition. */
-
-       return SR_OK;
+       return std_serial_dev_acquisition_stop(sdi, cb_data,
+               std_serial_dev_close, sdi->conn, LOG_PREFIX);
 }
 
 SR_PRIV struct sr_dev_driver korad_kdxxxxp_driver_info = {
@@ -182,8 +393,8 @@ SR_PRIV struct sr_dev_driver korad_kdxxxxp_driver_info = {
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
-       .dev_open = dev_open,
-       .dev_close = dev_close,
+       .dev_open = std_serial_dev_open,
+       .dev_close = std_serial_dev_close,
        .dev_acquisition_start = dev_acquisition_start,
        .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
index 66fe5e771d1fdbc98f22871faf7566a34ffed8e3..770131e26d8e31afe84004daf17e6f16cf154e97 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
+#define REQ_TIMEOUT_MS 500
+#define DEVICE_PROCESSING_TIME_MS 80
+
+SR_PRIV int korad_kdxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
+                               const char *cmd)
+{
+       int ret;
+
+       sr_dbg("Sending '%s'.", cmd);
+       if ((ret = serial_write_blocking(serial, cmd, strlen(cmd), 0)) < 0) {
+               sr_err("Error sending command: %d.", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+SR_PRIV int korad_kdxxxxp_read_chars(struct sr_serial_dev_inst *serial,
+                               int count, char *buf)
+{
+       int ret, received, turns;
+
+       received = 0;
+       turns = 0;
+
+       do {
+               if ((ret = serial_read_blocking(serial, buf + received,
+                               count - received,
+                               serial_timeout(serial, count))) < 0) {
+                       sr_err("Error %d reading %d bytes from device.",
+                              ret, count);
+                       return ret;
+               }
+               received += ret;
+               turns++;
+       } while ((received < count) && (turns < 100));
+
+       buf[count] = 0;
+
+       sr_spew("Received: '%s'.", buf);
+
+       return ret;
+}
+
+static void give_device_time_to_process(struct dev_context *devc)
+{
+       int64_t sleeping_time;
+
+       sleeping_time = devc->req_sent_at + (DEVICE_PROCESSING_TIME_MS * 1000);
+       sleeping_time -= g_get_monotonic_time();
+
+       if (sleeping_time > 0) {
+               g_usleep(sleeping_time);
+               sr_spew("Sleeping for processing %ld usec", sleeping_time);
+       }
+}
+
+SR_PRIV int korad_kdxxxxp_set_value(struct sr_serial_dev_inst *serial,
+                               struct dev_context *devc)
+{
+       char msg[21], *cmd;
+       float value;
+       int ret;
+
+       give_device_time_to_process(devc);
+
+       msg[20] = 0;
+       switch(devc->target){
+       case KDXXXXP_CURRENT:
+       case KDXXXXP_VOLTAGE:
+       case KDXXXXP_STATUS:
+               sr_err("Can't set measurable parameter.");
+               return SR_ERR;
+       case KDXXXXP_CURRENT_MAX:
+               cmd = "ISET1:%05.3f";
+               value = devc->current_max;
+               break;
+       case KDXXXXP_VOLTAGE_MAX:
+               cmd = "VSET1:%05.2f";
+               value = devc->voltage_max;
+               break;
+       case KDXXXXP_OUTPUT:
+               cmd = "OUT%01.0f";
+               value = (devc->output_enabled) ? 1 : 0;
+               break;
+       case KDXXXXP_BEEP:
+               cmd = "BEEP%01.0f";
+               value = (devc->beep_enabled) ? 1 : 0;
+               break;
+       case KDXXXXP_SAVE:
+               cmd = "SAV%01.0f";
+               if (devc->program < 1 || devc->program > 5) {
+                       sr_err("Only programs 1-5 supported and %d isn't "
+                              "between them.", devc->program);
+                       return SR_ERR;
+               }
+               value = devc->program;
+               break;
+       case KDXXXXP_RECALL:
+               cmd = "RCL%01.0f";
+               if (devc->program < 1 || devc->program > 5) {
+                       sr_err("Only programs 1-5 supported and %d isn't "
+                              "between them.", devc->program);
+                       return SR_ERR;
+               }
+               value = devc->program;
+               break;
+       default:
+               sr_err("Don't know how to set %d.", devc->target);
+               return SR_ERR;
+       }
+
+       if (cmd)
+               snprintf(msg, 20, cmd, value);
+
+       ret = korad_kdxxxxp_send_cmd(serial, msg);
+       devc->req_sent_at = g_get_monotonic_time();
+       devc->reply_pending = FALSE;
+
+       return ret;
+}
+
+SR_PRIV int korad_kdxxxxp_query_value(struct sr_serial_dev_inst *serial,
+                               struct dev_context *devc)
+{
+       int ret;
+
+       give_device_time_to_process(devc);
+
+       switch(devc->target){
+       case KDXXXXP_CURRENT:
+               /* Read current from device. */
+               ret = korad_kdxxxxp_send_cmd(serial, "IOUT1?");
+               break;
+       case KDXXXXP_CURRENT_MAX:
+               /* Read set current from device. */
+               ret = korad_kdxxxxp_send_cmd(serial, "ISET1?");
+               break;
+       case KDXXXXP_VOLTAGE:
+               /* Read voltage from device. */
+               ret = korad_kdxxxxp_send_cmd(serial, "VOUT1?");
+               break;
+       case KDXXXXP_VOLTAGE_MAX:
+               /* Read set voltage from device. */
+               ret = korad_kdxxxxp_send_cmd(serial, "VSET1?");
+               break;
+       case KDXXXXP_STATUS:
+       case KDXXXXP_OUTPUT:
+               /* Read status from device. */
+               ret = korad_kdxxxxp_send_cmd(serial, "STATUS?");
+               break;
+       default:
+               sr_err("Don't know how to query %d.", devc->target);
+               return SR_ERR;
+       }
+
+       devc->req_sent_at = g_get_monotonic_time();
+       devc->reply_pending = TRUE;
+
+       return ret;
+}
+
+SR_PRIV int korad_kdxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
+                               struct dev_context *devc)
+{
+       int ret;
+
+       for (devc->target = KDXXXXP_CURRENT;
+                       devc->target <= KDXXXXP_STATUS; devc->target++) {
+               if ((ret = korad_kdxxxxp_query_value(serial, devc)) < 0)
+                       return ret;
+               if ((ret = korad_kdxxxxp_get_reply(serial, devc)) < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+SR_PRIV int korad_kdxxxxp_get_reply(struct sr_serial_dev_inst *serial,
+                               struct dev_context *devc)
+{
+       double value;
+       int count, ret;
+       float *target;
+       char status_byte;
+
+       target = NULL;
+       count = 5;
+
+       switch (devc->target) {
+       case KDXXXXP_CURRENT:
+               /* Read current from device. */
+               target = &(devc->current);
+               break;
+       case KDXXXXP_CURRENT_MAX:
+               /* Read set current from device. */
+               target = &(devc->current_max);
+               break;
+       case KDXXXXP_VOLTAGE:
+               /* Read voltage from device. */
+               target = &(devc->voltage);
+               break;
+       case KDXXXXP_VOLTAGE_MAX:
+               /* Read set voltage from device. */
+               target = &(devc->voltage_max);
+               break;
+       case KDXXXXP_STATUS:
+       case KDXXXXP_OUTPUT:
+               /* Read status from device. */
+               count = 1;
+               break;
+       default:
+               sr_err("Don't know where to put repply %d.", devc->target);
+       }
+
+       if ((ret = korad_kdxxxxp_read_chars(serial, count, devc->reply)) < 0)
+               return ret;
+
+       devc->reply[count] = 0;
+
+       if (target) {
+               value = g_ascii_strtod(devc->reply, NULL);
+               *target = (float)value;
+               sr_dbg("value: %f",value);
+       } else {
+               /* We have status reply. */
+               status_byte = devc->reply[0];
+               /* Constant current */
+               devc->cc_mode[0] = !(status_byte & (1 << 0)); /* Channel one */
+               devc->cc_mode[1] = !(status_byte & (1 << 1)); /* Channel two */
+               /*
+                * Tracking
+                * status_byte & ((1 << 2) | (1 << 3))
+                * 00 independent 01 series 11 parallel
+                */
+               devc->beep_enabled = (1 << 4);
+               /* status_byte & (1 << 5) Unlocked */
+
+               devc->output_enabled = (status_byte & (1 << 6));
+               sr_dbg("Status: 0x%02x", status_byte);
+               sr_spew("Status: CH1: constant %s CH2: constant %s. Device is "
+                       "%s and %s. Buttons are %s. Output is %s ",
+                       (status_byte & (1 << 0)) ? "voltage" : "current",
+                       (status_byte & (1 << 1)) ? "voltage" : "current",
+                       (status_byte & (1 << 3)) ? "tracking" : "independent",
+                       (status_byte & (1 << 4)) ? "beeping" : "silent",
+                       (status_byte & (1 << 5)) ? "locked" : "unlocked",
+                       (status_byte & (1 << 6)) ? "enabled" : "disabled");
+       }
+
+       devc->reply_pending = FALSE;
+
+       return ret;
+}
+
+static void next_measurement(struct dev_context *devc)
+{
+       switch (devc->target) {
+       case KDXXXXP_CURRENT:
+               devc->target = KDXXXXP_VOLTAGE;
+               break;
+       case KDXXXXP_CURRENT_MAX:
+               devc->target = KDXXXXP_CURRENT;
+               break;
+       case KDXXXXP_VOLTAGE:
+               devc->target = KDXXXXP_STATUS;
+               break;
+       case KDXXXXP_VOLTAGE_MAX:
+               devc->target = KDXXXXP_CURRENT;
+               break;
+       case KDXXXXP_OUTPUT:
+               devc->target = KDXXXXP_STATUS;
+               break;
+       case KDXXXXP_STATUS:
+               devc->target = KDXXXXP_CURRENT;
+               break;
+       default:
+               devc->target = KDXXXXP_CURRENT;
+       }
+}
+
 SR_PRIV int korad_kdxxxxp_receive_data(int fd, int revents, void *cb_data)
 {
-       const struct sr_dev_inst *sdi;
+       struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+       int64_t t, elapsed_us;
 
        (void)fd;
 
@@ -33,8 +318,67 @@ SR_PRIV int korad_kdxxxxp_receive_data(int fd, int revents, void *cb_data)
        if (!(devc = sdi->priv))
                return TRUE;
 
+       serial = sdi->conn;
+
        if (revents == G_IO_IN) {
-               /* TODO */
+               /* Get the value. */
+               korad_kdxxxxp_get_reply(serial, devc);
+
+               /* Send the value forward. */
+               packet.type = SR_DF_ANALOG;
+               packet.payload = &analog;
+               analog.channels = sdi->channels;
+               analog.num_samples = 1;
+               if (devc->target == KDXXXXP_CURRENT) {
+                       analog.mq = SR_MQ_CURRENT;
+                       analog.unit = SR_UNIT_AMPERE;
+                       analog.mqflags = 0;
+                       analog.data = &devc->current;
+                       sr_session_send(sdi, &packet);
+               }
+               if (devc->target == KDXXXXP_VOLTAGE) {
+                       analog.mq = SR_MQ_VOLTAGE;
+                       analog.unit = SR_UNIT_VOLT;
+                       analog.mqflags = SR_MQFLAG_DC;
+                       analog.data = &devc->voltage;
+                       sr_session_send(sdi, &packet);
+                       devc->num_samples++;
+               }
+               next_measurement(devc);
+       } else {
+               /* Time out */
+               if (!devc->reply_pending) {
+                       if (korad_kdxxxxp_query_value(serial, devc) < 0)
+                               return TRUE;
+                       devc->req_sent_at = g_get_monotonic_time();
+                       devc->reply_pending = TRUE;
+               }
+       }
+
+       if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
+               sr_info("Requested number of samples reached.");
+               sdi->driver->dev_acquisition_stop(sdi, cb_data);
+               return TRUE;
+       }
+
+       if (devc->limit_msec) {
+               t = (g_get_monotonic_time() - devc->starttime) / 1000;
+               if (t > (int64_t)devc->limit_msec) {
+                       sr_info("Requested time limit reached.");
+                       sdi->driver->dev_acquisition_stop(sdi, cb_data);
+                       return TRUE;
+               }
+       }
+
+       /* Request next packet, if required. */
+       if (sdi->status == SR_ST_ACTIVE) {
+               if (devc->reply_pending) {
+                       elapsed_us = g_get_monotonic_time() - devc->req_sent_at;
+                       if (elapsed_us > (REQ_TIMEOUT_MS * 1000))
+                               devc->reply_pending = FALSE;
+                       return TRUE;
+               }
+
        }
 
        return TRUE;
index cb535c5fa08d4379b8028e6bc29966eaed2bd150..3b5c2b177ea77e646e08eb558861b0d7c6bb4a23 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/**
+ * @file
+ * Korad KDxxxxP power supply driver
+ * @internal
+ */
+
 #ifndef LIBSIGROK_HARDWARE_KORAD_KDXXXXP_PROTOCOL_H
 #define LIBSIGROK_HARDWARE_KORAD_KDXXXXP_PROTOCOL_H
 
 #include <stdint.h>
+#include <string.h>
 #include <glib.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "korad-kdxxxxp"
 
+#define KDXXXXP_POLL_INTERVAL_MS 80
+
+enum {
+       VELLEMAN_LABPS_3005D,
+       /* Support for future devices with this protocol. */
+};
+
+/* Information on single model */
+struct korad_kdxxxxp_model {
+       int model_id; /**< Model info */
+       char *vendor; /**< Vendor name */
+       char *name; /**< Model name */
+       char *id; /**< Model ID, as delivered by interface */
+       int channels; /**< Number of channels */
+       double voltage[3]; /**< Min, max, step */
+       double current[3]; /**< Min, max, step */
+};
+
+/* Reply targets */
+enum {
+       KDXXXXP_CURRENT,
+       KDXXXXP_CURRENT_MAX,
+       KDXXXXP_VOLTAGE,
+       KDXXXXP_VOLTAGE_MAX,
+       KDXXXXP_STATUS,
+       KDXXXXP_OUTPUT,
+       KDXXXXP_BEEP,
+       KDXXXXP_SAVE,
+       KDXXXXP_RECALL,
+};
+
 /** Private, per-device-instance driver context. */
 struct dev_context {
        /* Model-specific information */
+       const struct korad_kdxxxxp_model *model; /**< Model information. */
 
        /* Acquisition settings */
+       uint64_t limit_samples;
+       uint64_t limit_msec;
+       uint64_t num_samples;
+       int64_t starttime;
+       int64_t req_sent_at;
+       gboolean reply_pending;
+
+       void *cb_data;
 
        /* Operational state */
+       float current;          /**< Last current value [A] read from device. */
+       float current_max;      /**< Output current set. */
+       float voltage;          /**< Last voltage value [V] read from device. */
+       float voltage_max;      /**< Output voltage set. */
+       gboolean cc_mode[2];    /**< Device is in CC mode (otherwise CV). */
 
-       /* Temporary state across callbacks */
+       gboolean output_enabled; /**< Is the output enabled? */
+       gboolean beep_enabled;   /**< Enable beeper. */
 
+       /* Temporary state across callbacks */
+       int target;              /**< What reply to expect */
+       int program;             /**< Program to store or recall. */
+       char reply[6];
 };
 
+SR_PRIV int korad_kdxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
+                                       const char *cmd);
+SR_PRIV int korad_kdxxxxp_read_chars(struct sr_serial_dev_inst *serial,
+                                       int count, char *buf);
+SR_PRIV int korad_kdxxxxp_set_value(struct sr_serial_dev_inst *serial,
+                                       struct dev_context *devc);
+SR_PRIV int korad_kdxxxxp_query_value(struct sr_serial_dev_inst *serial,
+                                       struct dev_context *devc);
+SR_PRIV int korad_kdxxxxp_get_reply(struct sr_serial_dev_inst *serial,
+                                       struct dev_context *devc);
+SR_PRIV int korad_kdxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
+                                       struct dev_context *devc);
 SR_PRIV int korad_kdxxxxp_receive_data(int fd, int revents, void *cb_data);
 
 #endif