From: Uwe Hermann Date: Thu, 26 Nov 2015 23:16:05 +0000 (+0100) Subject: korad-kdxxxxp: Rename driver to korad-kaxxxxp. X-Git-Tag: libsigrok-0.4.0~104 X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=commitdiff_plain;h=16fc7ee29ffa196d29a20fddd81234792f88b12c korad-kdxxxxp: Rename driver to korad-kaxxxxp. This matches the supported / supportable devices better. --- diff --git a/Makefile.am b/Makefile.am index 4ea3b8a1..4ed41bf7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -323,11 +323,11 @@ libsigrok_la_SOURCES += \ src/hardware/kern-scale/protocol.c \ src/hardware/kern-scale/api.c endif -if HW_KORAD_KDXXXXP +if HW_KORAD_KAXXXXP libsigrok_la_SOURCES += \ - src/hardware/korad-kdxxxxp/protocol.h \ - src/hardware/korad-kdxxxxp/protocol.c \ - src/hardware/korad-kdxxxxp/api.c + src/hardware/korad-kaxxxxp/protocol.h \ + src/hardware/korad-kaxxxxp/protocol.c \ + src/hardware/korad-kaxxxxp/api.c endif if HW_LASCAR_EL_USB libsigrok_la_SOURCES += \ diff --git a/configure.ac b/configure.ac index e5d5ce8c..ac7259d3 100644 --- a/configure.ac +++ b/configure.ac @@ -239,7 +239,7 @@ SR_DRIVER([Ikalogic Scanalogic-2], [ikalogic-scanalogic2], [libusb]) SR_DRIVER([Ikalogic Scanaplus], [ikalogic-scanaplus], [libftdi]) SR_DRIVER([Kecheng KC-330B], [kecheng-kc-330b], [libusb]) SR_DRIVER([KERN scale], [kern-scale], [libserialport]) -SR_DRIVER([Korad KDxxxxP], [korad-kdxxxxp], [libserialport]) +SR_DRIVER([Korad KAxxxxP], [korad-kaxxxxp], [libserialport]) SR_DRIVER([Lascar EL-USB], [lascar-el-usb], [libusb]) SR_DRIVER([Manson HCS-3xxx], [manson-hcs-3xxx], [libserialport]) SR_DRIVER([maynuo-m97], [maynuo-m97]) diff --git a/src/drivers.c b/src/drivers.c index fd4b91fe..f4e5d1c3 100644 --- a/src/drivers.c +++ b/src/drivers.c @@ -102,8 +102,8 @@ extern SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info; #ifdef HAVE_HW_KERN_SCALE extern SR_PRIV struct sr_dev_driver *kern_scale_drivers[]; #endif -#ifdef HAVE_HW_KORAD_KDXXXXP -extern SR_PRIV struct sr_dev_driver korad_kdxxxxp_driver_info; +#ifdef HAVE_HW_KORAD_KAXXXXP +extern SR_PRIV struct sr_dev_driver korad_kaxxxxp_driver_info; #endif #ifdef HAVE_HW_LASCAR_EL_USB extern SR_PRIV struct sr_dev_driver lascar_el_usb_driver_info; @@ -263,8 +263,8 @@ SR_PRIV struct sr_dev_driver **drivers_lists[] = { #ifdef HAVE_HW_KERN_SCALE kern_scale_drivers, #endif -#ifdef HAVE_HW_KORAD_KDXXXXP - (DRVS) {&korad_kdxxxxp_driver_info, NULL}, +#ifdef HAVE_HW_KORAD_KAXXXXP + (DRVS) {&korad_kaxxxxp_driver_info, NULL}, #endif #ifdef HAVE_HW_LASCAR_EL_USB (DRVS) {&lascar_el_usb_driver_info, NULL}, diff --git a/src/hardware/korad-kaxxxxp/api.c b/src/hardware/korad-kaxxxxp/api.c new file mode 100644 index 00000000..c195f1f8 --- /dev/null +++ b/src/hardware/korad-kaxxxxp/api.c @@ -0,0 +1,428 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Hannu Vuolasaho + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#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, + SR_CONF_REGULATION | SR_CONF_GET, + SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, + SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, +}; + +static const struct korad_kaxxxxp_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_kaxxxxp_driver_info; + +static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx) +{ + return std_init(sr_ctx, di, LOG_PREFIX); +} + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct drv_context *drvc; + 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; + + 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_kaxxxxp_send_cmd(serial, "*IDN?") < 0)) + return NULL; + + /* i is used here for debug purposes only. */ + if ((i = korad_kaxxxxp_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_kaxxxxp_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) +{ + return ((struct drv_context *)(di->context))->instances; +} + +static int dev_clear(const struct sr_dev_driver *di) +{ + return std_dev_clear(di, NULL); +} + +static int cleanup(const struct sr_dev_driver *di) +{ + dev_clear(di); + return SR_OK; +} + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + (void)cg; + + if (!sdi || !data) + return SR_ERR_ARG; + + devc = sdi->priv; + + switch (key) { + 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; + case SR_CONF_REGULATION: + /* Dual channel not supported. */ + *data = g_variant_new_string((devc->cc_mode[0]) ? "CC" : "CV"); + break; + case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: + *data = g_variant_new_boolean(devc->ocp_enabled); + break; + case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: + *data = g_variant_new_boolean(devc->ovp_enabled); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + double dval; + gboolean bval; + + (void)cg; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + devc = sdi->priv; + + switch (key) { + 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 = KAXXXXP_VOLTAGE_MAX; + if (korad_kaxxxxp_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 = KAXXXXP_CURRENT_MAX; + if (korad_kaxxxxp_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 = KAXXXXP_OUTPUT; + if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0) + return SR_ERR; + break; + case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: + bval = g_variant_get_boolean(data); + devc->ocp_enabled = bval; + devc->target = KAXXXXP_OCP; + if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0) + return SR_ERR; + break; + case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: + bval = g_variant_get_boolean(data); + devc->ovp_enabled = bval; + devc->target = KAXXXXP_OVP; + if (korad_kaxxxxp_set_value(sdi->conn, devc) < 0) + return SR_ERR; + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + + struct dev_context *devc; + GVariant *gvar; + GVariantBuilder gvb; + double dval; + int idx; + + (void)cg; + + /* 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) { + 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 SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *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; + + 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, + KAXXXXP_POLL_INTERVAL_MS, + korad_kaxxxxp_receive_data, (void *)sdi); + + return SR_OK; +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) +{ + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + return std_serial_dev_acquisition_stop(sdi, cb_data, + std_serial_dev_close, sdi->conn, LOG_PREFIX); +} + +SR_PRIV struct sr_dev_driver korad_kaxxxxp_driver_info = { + .name = "korad-kaxxxxp", + .longname = "Korad KAxxxxP", + .api_version = 1, + .init = init, + .cleanup = cleanup, + .scan = scan, + .dev_list = dev_list, + .dev_clear = dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .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-kaxxxxp/protocol.c b/src/hardware/korad-kaxxxxp/protocol.c new file mode 100644 index 00000000..c01d3ca6 --- /dev/null +++ b/src/hardware/korad-kaxxxxp/protocol.c @@ -0,0 +1,413 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Hannu Vuolasaho + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "protocol.h" + +#define REQ_TIMEOUT_MS 500 +#define DEVICE_PROCESSING_TIME_MS 80 + +SR_PRIV int korad_kaxxxxp_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_kaxxxxp_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 %" PRIi64 " usec", sleeping_time); + } +} + +SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + char msg[21]; + const char *cmd; + float value; + int ret; + + give_device_time_to_process(devc); + + msg[20] = 0; + switch(devc->target){ + case KAXXXXP_CURRENT: + case KAXXXXP_VOLTAGE: + case KAXXXXP_STATUS: + sr_err("Can't set measurable parameter."); + return SR_ERR; + case KAXXXXP_CURRENT_MAX: + cmd = "ISET1:%05.3f"; + value = devc->current_max; + break; + case KAXXXXP_VOLTAGE_MAX: + cmd = "VSET1:%05.2f"; + value = devc->voltage_max; + break; + case KAXXXXP_OUTPUT: + cmd = "OUT%01.0f"; + value = (devc->output_enabled) ? 1 : 0; + break; + case KAXXXXP_BEEP: + cmd = "BEEP%01.0f"; + value = (devc->beep_enabled) ? 1 : 0; + break; + case KAXXXXP_OCP: + cmd = "OCP%01.0f"; + value = (devc->ocp_enabled) ? 1 : 0; + break; + case KAXXXXP_OVP: + cmd = "OVP%01.0f"; + value = (devc->ovp_enabled) ? 1 : 0; + break; + case KAXXXXP_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 KAXXXXP_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_kaxxxxp_send_cmd(serial, msg); + devc->req_sent_at = g_get_monotonic_time(); + devc->reply_pending = FALSE; + + return ret; +} + +SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + int ret; + + give_device_time_to_process(devc); + + switch(devc->target){ + case KAXXXXP_CURRENT: + /* Read current from device. */ + ret = korad_kaxxxxp_send_cmd(serial, "IOUT1?"); + break; + case KAXXXXP_CURRENT_MAX: + /* Read set current from device. */ + ret = korad_kaxxxxp_send_cmd(serial, "ISET1?"); + break; + case KAXXXXP_VOLTAGE: + /* Read voltage from device. */ + ret = korad_kaxxxxp_send_cmd(serial, "VOUT1?"); + break; + case KAXXXXP_VOLTAGE_MAX: + /* Read set voltage from device. */ + ret = korad_kaxxxxp_send_cmd(serial, "VSET1?"); + break; + case KAXXXXP_STATUS: + case KAXXXXP_OUTPUT: + /* Read status from device. */ + ret = korad_kaxxxxp_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_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + int ret; + + for (devc->target = KAXXXXP_CURRENT; + devc->target <= KAXXXXP_STATUS; devc->target++) { + if ((ret = korad_kaxxxxp_query_value(serial, devc)) < 0) + return ret; + if ((ret = korad_kaxxxxp_get_reply(serial, devc)) < 0) + return ret; + } + + return ret; +} + +SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + double value; + int count, ret, i; + float *target; + char status_byte; + + target = NULL; + count = 5; + + switch (devc->target) { + case KAXXXXP_CURRENT: + /* Read current from device. */ + target = &(devc->current); + break; + case KAXXXXP_CURRENT_MAX: + /* Read set current from device. */ + target = &(devc->current_max); + break; + case KAXXXXP_VOLTAGE: + /* Read voltage from device. */ + target = &(devc->voltage); + break; + case KAXXXXP_VOLTAGE_MAX: + /* Read set voltage from device. */ + target = &(devc->voltage_max); + break; + case KAXXXXP_STATUS: + case KAXXXXP_OUTPUT: + /* Read status from device. */ + count = 1; + break; + default: + sr_err("Don't know where to put repply %d.", devc->target); + } + + if ((ret = korad_kaxxxxp_read_chars(serial, count, devc->reply)) < 0) + return ret; + + devc->reply[count] = 0; + + if (target) { + /* Handle the strange 'M'. */ + if (devc->reply[0] == 'M') { + for (i = 1; i < count; i++) + devc->reply[i - 1] = devc->reply[i]; + /* Get the last character. */ + if ((i = korad_kaxxxxp_read_chars(serial, 1, + &(devc->reply[count]))) < 0) + return i; + } + 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); + devc->ocp_enabled = (status_byte & (1 << 5)); + devc->output_enabled = (status_byte & (1 << 6)); + /* Velleman LABPS3005 quirk */ + if (devc->output_enabled) + devc->ovp_enabled = (status_byte & (1 << 7)); + sr_dbg("Status: 0x%02x", status_byte); + sr_spew("Status: CH1: constant %s CH2: constant %s. " + "Tracking would be %s. Device is " + "%s and %s. Buttons are %s. Output is %s " + "and extra byte is %s.", + (status_byte & (1 << 0)) ? "voltage" : "current", + (status_byte & (1 << 1)) ? "voltage" : "current", + (status_byte & (1 << 2)) ? "parallel" : "series", + (status_byte & (1 << 3)) ? "tracking" : "independent", + (status_byte & (1 << 4)) ? "beeping" : "silent", + (status_byte & (1 << 5)) ? "locked" : "unlocked", + (status_byte & (1 << 6)) ? "enabled" : "disabled", + (status_byte & (1 << 7)) ? "true" : "false"); + } + + devc->reply_pending = FALSE; + + return ret; +} + +static void next_measurement(struct dev_context *devc) +{ + switch (devc->target) { + case KAXXXXP_CURRENT: + devc->target = KAXXXXP_VOLTAGE; + break; + case KAXXXXP_CURRENT_MAX: + devc->target = KAXXXXP_CURRENT; + break; + case KAXXXXP_VOLTAGE: + devc->target = KAXXXXP_STATUS; + break; + case KAXXXXP_VOLTAGE_MAX: + devc->target = KAXXXXP_CURRENT; + break; + /* Read back what was set. */ + case KAXXXXP_BEEP: + case KAXXXXP_OCP: + case KAXXXXP_OVP: + case KAXXXXP_OUTPUT: + devc->target = KAXXXXP_STATUS; + break; + case KAXXXXP_STATUS: + devc->target = KAXXXXP_CURRENT; + break; + default: + devc->target = KAXXXXP_CURRENT; + } +} + +SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog_old analog; + int64_t t, elapsed_us; + + (void)fd; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + serial = sdi->conn; + + if (revents == G_IO_IN) { + /* Get the value. */ + korad_kaxxxxp_get_reply(serial, devc); + + /* Send the value forward. */ + packet.type = SR_DF_ANALOG_OLD; + packet.payload = &analog; + analog.channels = sdi->channels; + analog.num_samples = 1; + if (devc->target == KAXXXXP_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 == KAXXXXP_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_kaxxxxp_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-kaxxxxp/protocol.h b/src/hardware/korad-kaxxxxp/protocol.h new file mode 100644 index 00000000..1a9dfe30 --- /dev/null +++ b/src/hardware/korad-kaxxxxp/protocol.h @@ -0,0 +1,117 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Hannu Vuolasaho + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Korad KAxxxxP power supply driver + * @internal + */ + +#ifndef LIBSIGROK_HARDWARE_KORAD_KAXXXXP_PROTOCOL_H +#define LIBSIGROK_HARDWARE_KORAD_KAXXXXP_PROTOCOL_H + +#include +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "korad-kaxxxxp" + +#define KAXXXXP_POLL_INTERVAL_MS 80 + +enum { + VELLEMAN_LABPS_3005D, + /* Support for future devices with this protocol. */ +}; + +/* Information on single model */ +struct korad_kaxxxxp_model { + int model_id; /**< Model info */ + const char *vendor; /**< Vendor name */ + const char *name; /**< Model name */ + const 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 { + KAXXXXP_CURRENT, + KAXXXXP_CURRENT_MAX, + KAXXXXP_VOLTAGE, + KAXXXXP_VOLTAGE_MAX, + KAXXXXP_STATUS, + KAXXXXP_OUTPUT, + KAXXXXP_BEEP, + KAXXXXP_OCP, + KAXXXXP_OVP, + KAXXXXP_SAVE, + KAXXXXP_RECALL, +}; + +/** Private, per-device-instance driver context. */ +struct dev_context { + /* Model-specific information */ + const struct korad_kaxxxxp_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. */ + gboolean ocp_enabled; /**< Output current protection enabled. */ + gboolean ovp_enabled; /**< Output voltage protection enabled. */ + + /* Temporary state across callbacks */ + int target; /**< What reply to expect. */ + int program; /**< Program to store or recall. */ + char reply[6]; +}; + +SR_PRIV int korad_kaxxxxp_send_cmd(struct sr_serial_dev_inst *serial, + const char *cmd); +SR_PRIV int korad_kaxxxxp_read_chars(struct sr_serial_dev_inst *serial, + int count, char *buf); +SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int korad_kaxxxxp_query_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int korad_kaxxxxp_get_reply(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data); + +#endif diff --git a/src/hardware/korad-kdxxxxp/api.c b/src/hardware/korad-kdxxxxp/api.c deleted file mode 100644 index c499d4b7..00000000 --- a/src/hardware/korad-kdxxxxp/api.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2015 Hannu Vuolasaho - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#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, - SR_CONF_REGULATION | SR_CONF_GET, - SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET | SR_CONF_SET, - SR_CONF_OVER_VOLTAGE_PROTECTION_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) -{ - return std_init(sr_ctx, di, LOG_PREFIX); -} - -static GSList *scan(struct sr_dev_driver *di, GSList *options) -{ - struct drv_context *drvc; - 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; - - 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) -{ - return ((struct drv_context *)(di->context))->instances; -} - -static int dev_clear(const struct sr_dev_driver *di) -{ - return std_dev_clear(di, NULL); -} - -static int cleanup(const struct sr_dev_driver *di) -{ - dev_clear(di); - return SR_OK; -} - -static int config_get(uint32_t key, GVariant **data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) -{ - struct dev_context *devc; - - (void)cg; - - if (!sdi || !data) - return SR_ERR_ARG; - - devc = sdi->priv; - - switch (key) { - 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; - case SR_CONF_REGULATION: - /* Dual channel not supported. */ - *data = g_variant_new_string((devc->cc_mode[0]) ? "CC" : "CV"); - break; - case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: - *data = g_variant_new_boolean(devc->ocp_enabled); - break; - case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: - *data = g_variant_new_boolean(devc->ovp_enabled); - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -static int config_set(uint32_t key, GVariant *data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) -{ - struct dev_context *devc; - double dval; - gboolean bval; - - (void)cg; - - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - - devc = sdi->priv; - - switch (key) { - 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; - case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: - bval = g_variant_get_boolean(data); - devc->ocp_enabled = bval; - devc->target = KDXXXXP_OCP; - if (korad_kdxxxxp_set_value(sdi->conn, devc) < 0) - return SR_ERR; - break; - case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: - bval = g_variant_get_boolean(data); - devc->ovp_enabled = bval; - devc->target = KDXXXXP_OVP; - if (korad_kdxxxxp_set_value(sdi->conn, devc) < 0) - return SR_ERR; - break; - default: - return SR_ERR_NA; - } - - return SR_OK; -} - -static int config_list(uint32_t key, GVariant **data, - const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) -{ - - struct dev_context *devc; - GVariant *gvar; - GVariantBuilder gvb; - double dval; - int idx; - - (void)cg; - - /* 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) { - 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 SR_OK; -} - -static int dev_acquisition_start(const struct sr_dev_inst *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; - - 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) -{ - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; - - 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 = { - .name = "korad-kdxxxxp", - .longname = "Korad KDxxxxP", - .api_version = 1, - .init = init, - .cleanup = cleanup, - .scan = scan, - .dev_list = dev_list, - .dev_clear = dev_clear, - .config_get = config_get, - .config_set = config_set, - .config_list = config_list, - .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 deleted file mode 100644 index bec63b4e..00000000 --- a/src/hardware/korad-kdxxxxp/protocol.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2015 Hannu Vuolasaho - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#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 %" PRIi64 " usec", sleeping_time); - } -} - -SR_PRIV int korad_kdxxxxp_set_value(struct sr_serial_dev_inst *serial, - struct dev_context *devc) -{ - char msg[21]; - const char *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_OCP: - cmd = "OCP%01.0f"; - value = (devc->ocp_enabled) ? 1 : 0; - break; - case KDXXXXP_OVP: - cmd = "OVP%01.0f"; - value = (devc->ovp_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, i; - 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) { - /* Handle the strange 'M'. */ - if (devc->reply[0] == 'M') { - for (i = 1; i < count; i++) - devc->reply[i - 1] = devc->reply[i]; - /* Get the last character. */ - if ((i = korad_kdxxxxp_read_chars(serial, 1, - &(devc->reply[count]))) < 0) - return i; - } - 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); - devc->ocp_enabled = (status_byte & (1 << 5)); - devc->output_enabled = (status_byte & (1 << 6)); - /* Velleman LABPS3005 quirk */ - if (devc->output_enabled) - devc->ovp_enabled = (status_byte & (1 << 7)); - sr_dbg("Status: 0x%02x", status_byte); - sr_spew("Status: CH1: constant %s CH2: constant %s. " - "Tracking would be %s. Device is " - "%s and %s. Buttons are %s. Output is %s " - "and extra byte is %s.", - (status_byte & (1 << 0)) ? "voltage" : "current", - (status_byte & (1 << 1)) ? "voltage" : "current", - (status_byte & (1 << 2)) ? "parallel" : "series", - (status_byte & (1 << 3)) ? "tracking" : "independent", - (status_byte & (1 << 4)) ? "beeping" : "silent", - (status_byte & (1 << 5)) ? "locked" : "unlocked", - (status_byte & (1 << 6)) ? "enabled" : "disabled", - (status_byte & (1 << 7)) ? "true" : "false"); - } - - 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; - /* Read back what was set. */ - case KDXXXXP_BEEP: - case KDXXXXP_OCP: - case KDXXXXP_OVP: - 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) -{ - struct sr_dev_inst *sdi; - struct dev_context *devc; - struct sr_serial_dev_inst *serial; - struct sr_datafeed_packet packet; - struct sr_datafeed_analog_old analog; - int64_t t, elapsed_us; - - (void)fd; - - if (!(sdi = cb_data)) - return TRUE; - - if (!(devc = sdi->priv)) - return TRUE; - - serial = sdi->conn; - - if (revents == G_IO_IN) { - /* Get the value. */ - korad_kdxxxxp_get_reply(serial, devc); - - /* Send the value forward. */ - packet.type = SR_DF_ANALOG_OLD; - 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 deleted file mode 100644 index a76b28ba..00000000 --- a/src/hardware/korad-kdxxxxp/protocol.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2015 Hannu Vuolasaho - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * 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 */ - const char *vendor; /**< Vendor name */ - const char *name; /**< Model name */ - const 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_OCP, - KDXXXXP_OVP, - 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. */ - gboolean ocp_enabled; /**< Output current protection enabled. */ - gboolean ovp_enabled; /**< Output voltage protection enabled. */ - - /* 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