diff --git a/src/hardware/korad-kdxxxxp/api.c b/src/hardware/korad-kdxxxxp/api.c index ab7905d8..107bdc40 100644 --- a/src/hardware/korad-kdxxxxp/api.c +++ b/src/hardware/korad-kdxxxxp/api.c @@ -20,6 +20,38 @@ #include #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, diff --git a/src/hardware/korad-kdxxxxp/protocol.c b/src/hardware/korad-kdxxxxp/protocol.c index 66fe5e77..770131e2 100644 --- a/src/hardware/korad-kdxxxxp/protocol.c +++ b/src/hardware/korad-kdxxxxp/protocol.c @@ -20,10 +20,295 @@ #include #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; diff --git a/src/hardware/korad-kdxxxxp/protocol.h b/src/hardware/korad-kdxxxxp/protocol.h index cb535c5f..3b5c2b17 100644 --- a/src/hardware/korad-kdxxxxp/protocol.h +++ b/src/hardware/korad-kdxxxxp/protocol.h @@ -17,28 +17,97 @@ * along with this program. If not, see . */ +/** + * @file + * Korad KDxxxxP power supply driver + * @internal + */ + #ifndef LIBSIGROK_HARDWARE_KORAD_KDXXXXP_PROTOCOL_H #define LIBSIGROK_HARDWARE_KORAD_KDXXXXP_PROTOCOL_H #include +#include #include #include #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). */ + + 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