Hello! I bough Velleman power supply and now I've written driver for it. This is no way ready but I would like to give to some review and for testing. I'm not familiar with glib and libsigrok is also rather unmapped terrain.
I have used Manson PS driver as reference. Best regards, Hannu Vuolasaho
From f594ae14f8993b616128682cb8ea1e52b55df8fd Mon Sep 17 00:00:00 2001 From: Hannu Vuolasaho <vuokkose...@gmail.com> Date: Thu, 24 Sep 2015 22:58:38 +0300 Subject: [PATCH 1/2] velleman-labps3005d: Initial driver skeleton. --- Makefile.am | 6 + configure.ac | 1 + src/drivers.c | 6 + src/hardware/velleman-labps3005d/api.c | 191 ++++++++++++++++++++++++++++ src/hardware/velleman-labps3005d/protocol.c | 41 ++++++ src/hardware/velleman-labps3005d/protocol.h | 44 +++++++ 6 files changed, 289 insertions(+) create mode 100644 src/hardware/velleman-labps3005d/api.c create mode 100644 src/hardware/velleman-labps3005d/protocol.c create mode 100644 src/hardware/velleman-labps3005d/protocol.h diff --git a/Makefile.am b/Makefile.am index c866213..18e2692 100644 --- a/Makefile.am +++ b/Makefile.am @@ -415,6 +415,12 @@ libsigrok_la_SOURCES += \ src/hardware/uni-t-ut32x/protocol.c \ src/hardware/uni-t-ut32x/api.c endif +if HW_VELLEMAN_LABPS3005D +libsigrok_la_SOURCES += \ + src/hardware/velleman-labps3005d/protocol.h \ + src/hardware/velleman-labps3005d/protocol.c \ + src/hardware/velleman-labps3005d/api.c +endif if HW_VICTOR_DMM libsigrok_la_SOURCES += \ src/hardware/victor-dmm/protocol.h \ diff --git a/configure.ac b/configure.ac index 5b89465..5125146 100644 --- a/configure.ac +++ b/configure.ac @@ -254,6 +254,7 @@ SR_DRIVER([Testo], [testo], [libusb]) SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [libserialport]) SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb]) SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [libusb]) +SR_DRIVER([Velleman LABPS3005D], [velleman-labps3005d]) SR_DRIVER([Victor DMM], [victor-dmm], [libusb]) SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm]) SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb]) diff --git a/src/drivers.c b/src/drivers.c index d2ad9f8..ec31074 100644 --- a/src/drivers.c +++ b/src/drivers.c @@ -158,6 +158,9 @@ extern SR_PRIV struct sr_dev_driver *uni_t_dmm_drivers[]; #ifdef HAVE_HW_UNI_T_UT32X extern SR_PRIV struct sr_dev_driver uni_t_ut32x_driver_info; #endif +#ifdef HAVE_HW_VELLEMAN_LABPS3005D +extern SR_PRIV struct sr_dev_driver velleman_labps3005d_driver_info; +#endif #ifdef HAVE_HW_VICTOR_DMM extern SR_PRIV struct sr_dev_driver victor_dmm_driver_info; #endif @@ -319,6 +322,9 @@ SR_PRIV struct sr_dev_driver **drivers_lists[] = { #ifdef HAVE_HW_UNI_T_UT32X (DRVS) {&uni_t_ut32x_driver_info, NULL}, #endif +#ifdef HAVE_HW_VELLEMAN_LABPS3005D + (DRVS) {&velleman_labps3005d_driver_info, NULL}, +#endif #ifdef HAVE_HW_VICTOR_DMM (DRVS) {&victor_dmm_driver_info, NULL}, #endif diff --git a/src/hardware/velleman-labps3005d/api.c b/src/hardware/velleman-labps3005d/api.c new file mode 100644 index 0000000..096b114 --- /dev/null +++ b/src/hardware/velleman-labps3005d/api.c @@ -0,0 +1,191 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Hannu Vuolasaho <vuokkose...@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include "protocol.h" + +SR_PRIV struct sr_dev_driver velleman_labps3005d_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; + GSList *devices; + + (void)options; + + devices = NULL; + drvc = di->context; + drvc->instances = NULL; + + /* TODO: scan for devices, either based on a SR_CONF_CONN option + * or on a USB scan. */ + + return devices; +} + +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 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; + + (void)sdi; + (void)data; + (void)cg; + + ret = SR_OK; + switch (key) { + /* TODO */ + default: + return SR_ERR_NA; + } + + return ret; +} + +static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + int ret; + + (void)data; + (void)cg; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + ret = SR_OK; + switch (key) { + /* TODO */ + default: + ret = SR_ERR_NA; + } + + return ret; +} + +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; + (void)cg; + + ret = SR_OK; + switch (key) { + /* TODO */ + default: + return SR_ERR_NA; + } + + return ret; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi, + void *cb_data) +{ + (void)sdi; + (void)cb_data; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; + + /* TODO: configure hardware, reset acquisition state, set up + * callbacks and send header packet. */ + + 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; +} + +SR_PRIV struct sr_dev_driver velleman_labps3005d_driver_info = { + .name = "velleman-labps3005d", + .longname = "Velleman LABPS3005D", + .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 = dev_open, + .dev_close = dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, +}; diff --git a/src/hardware/velleman-labps3005d/protocol.c b/src/hardware/velleman-labps3005d/protocol.c new file mode 100644 index 0000000..3f36e6e --- /dev/null +++ b/src/hardware/velleman-labps3005d/protocol.c @@ -0,0 +1,41 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Hannu Vuolasaho <vuokkose...@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include "protocol.h" + +SR_PRIV int velleman_labps3005d_receive_data(int fd, int revents, void *cb_data) +{ + const struct sr_dev_inst *sdi; + struct dev_context *devc; + + (void)fd; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + if (revents == G_IO_IN) { + /* TODO */ + } + + return TRUE; +} diff --git a/src/hardware/velleman-labps3005d/protocol.h b/src/hardware/velleman-labps3005d/protocol.h new file mode 100644 index 0000000..63fe994 --- /dev/null +++ b/src/hardware/velleman-labps3005d/protocol.h @@ -0,0 +1,44 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2015 Hannu Vuolasaho <vuokkose...@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBSIGROK_HARDWARE_VELLEMAN_LABPS3005D_PROTOCOL_H +#define LIBSIGROK_HARDWARE_VELLEMAN_LABPS3005D_PROTOCOL_H + +#include <stdint.h> +#include <glib.h> +#include <libsigrok/libsigrok.h> +#include "libsigrok-internal.h" + +#define LOG_PREFIX "velleman-labps3005d" + +/** Private, per-device-instance driver context. */ +struct dev_context { + /* Model-specific information */ + + /* Acquisition settings */ + + /* Operational state */ + + /* Temporary state across callbacks */ + +}; + +SR_PRIV int velleman_labps3005d_receive_data(int fd, int revents, void *cb_data); + +#endif -- 2.5.1
From f6b26b5436cabe5ec0370e110fc4f02a06902f67 Mon Sep 17 00:00:00 2001 From: Hannu Vuolasaho <vuokkose...@gmail.com> Date: Fri, 25 Sep 2015 17:43:17 +0300 Subject: [PATCH 2/2] Initial driver for Velleman LABPS3005D With this driver it is possible to set voltage target and current limit. Also enabling and disabling output is possible. Analog output sends read back values from output. If output is disabled analog outputs 0.00. On line 37 in protocol.c there is g_usleep() call. This gives almost every time enough time for PSU to parse and process input. Multichannel devices aren't supported. --- src/hardware/velleman-labps3005d/api.c | 342 ++++++++++++++++++++++------ src/hardware/velleman-labps3005d/protocol.c | 290 ++++++++++++++++++++++- src/hardware/velleman-labps3005d/protocol.h | 59 ++++- 3 files changed, 616 insertions(+), 75 deletions(-) diff --git a/src/hardware/velleman-labps3005d/api.c b/src/hardware/velleman-labps3005d/api.c index 096b114..36073ac 100644 --- a/src/hardware/velleman-labps3005d/api.c +++ b/src/hardware/velleman-labps3005d/api.c @@ -20,6 +20,35 @@ #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 velleman_labps_model models[] = { + {VELLEMAN_LABPS_3005D, "LABPS3005D", "VELLEMANLABPS3005DV2.0", {0, 31, 0.01}, {0, 5, 0.001}}, + {0, NULL, NULL, {0,0,0}, {0,0,0}} +}; + SR_PRIV struct sr_dev_driver velleman_labps3005d_driver_info; static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx) @@ -30,18 +59,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 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. */ - - return devices; + 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("Receiving %d bytes.", len); + if((velleman_labps3005d_send_cmd(serial, "*IDN?") < 0)) + return NULL; + + /*(len = serial_read_blocking(serial, reply, len, 0) < 0))*/ + if (velleman_labps3005d_read_chars(serial, len, reply) < 0) + return NULL; + sr_dbg("Received: %d, %s", len, 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; + } + + /* Init device instance, etc. */ + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("Velleman"); + 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; + sdi->priv = devc; + + /* Get current status of device */ + if(velleman_labps3005d_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); + + return NULL; + } static GSList *dev_list(const struct sr_dev_driver *di) @@ -54,121 +174,211 @@ 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; - - (void)sdi; - (void)data; + + struct dev_context *devc; + + if (!sdi || !data) + return SR_ERR_ARG; (void)cg; + devc = sdi->priv; - ret = SR_OK; 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; + gdouble 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 = VELLEMAN_LABPS_3005D_VOLTAGE_MAX; + if(velleman_labps3005d_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 = VELLEMAN_LABPS_3005D_CURRENT_MAX; + if(velleman_labps3005d_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 = VELLEMAN_LABPS_3005D_OUTPUT; + if(velleman_labps3005d_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 */ - default: - return SR_ERR_NA; - } - - return ret; + + 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) { - (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; + /* Poll every 10ms, or whenever some data comes in. */ + serial = sdi->conn; + serial_source_add(sdi->session, serial, G_IO_IN, 10, + velleman_labps3005d_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) + 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 velleman_labps3005d_driver_info = { @@ -183,8 +393,8 @@ SR_PRIV struct sr_dev_driver velleman_labps3005d_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/velleman-labps3005d/protocol.c b/src/hardware/velleman-labps3005d/protocol.c index 3f36e6e..d702112 100644 --- a/src/hardware/velleman-labps3005d/protocol.c +++ b/src/hardware/velleman-labps3005d/protocol.c @@ -20,11 +20,239 @@ #include <config.h> #include "protocol.h" +#define REQ_TIMEOUT_MS 500 + +SR_PRIV int velleman_labps3005d_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), + serial_timeout(serial, strlen(cmd)))) < 0) { + sr_err("Error sending command: %d.", ret); + return ret; + } + /* Some sleep time to let PSU to parse and evaluate. */ + g_usleep(35000); + return ret; +} +SR_PRIV int velleman_labps3005d_read_chars(struct sr_serial_dev_inst *serial, + int count, char *buf) +{ + int ret; + + if( (ret = serial_read_blocking(serial, buf, count, + serial_timeout(serial, count))) < 0){ + sr_err("Error %d reading %d bytes from device.",ret, count); + return ret; + } + return ret; +} + +SR_PRIV int velleman_labps3005d_set_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + char msg[21], * cmd; + float value; + + + msg[20] = 0; + switch(devc->target){ + case VELLEMAN_LABPS_3005D_CURRENT: + case VELLEMAN_LABPS_3005D_VOLTAGE: + case VELLEMAN_LABPS_3005D_STATUS: + sr_err("Can't set measurable parameter."); + return SR_ERR; + case VELLEMAN_LABPS_3005D_CURRENT_MAX: + cmd = "ISET1:%05.3f"; + value = devc->current_max; + break; + case VELLEMAN_LABPS_3005D_VOLTAGE_MAX: + cmd = "VSET1:%05.2f"; + value = devc->voltage_max; + break; + case VELLEMAN_LABPS_3005D_OUTPUT: + cmd = NULL; + msg[0] = 'O'; + msg[1] = 'U'; + msg[2] = 'T'; + if(devc->output_enabled) + msg[3] = '1'; + else + msg[3] = '0'; + break; + default: + /* OOPS! */ + sr_err("Don't know how to set %d.", devc->target); + return SR_ERR; + } + if(cmd){ + snprintf(msg, 20, cmd, value); + } + return velleman_labps3005d_send_cmd(serial, msg); + +} + +SR_PRIV int velleman_labps3005d_query_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + switch(devc->target){ + case VELLEMAN_LABPS_3005D_CURRENT: + /* Read current from device */ + return velleman_labps3005d_send_cmd(serial, "IOUT1?"); + case VELLEMAN_LABPS_3005D_CURRENT_MAX: + /* Read set current from device */ + return velleman_labps3005d_send_cmd(serial, "ISET1?"); + case VELLEMAN_LABPS_3005D_VOLTAGE: + /* Read voltage from device */ + return velleman_labps3005d_send_cmd(serial, "VOUT1?"); + case VELLEMAN_LABPS_3005D_VOLTAGE_MAX: + /* Read set voltage from device */ + return velleman_labps3005d_send_cmd(serial, "VSET1?"); + case VELLEMAN_LABPS_3005D_STATUS: + case VELLEMAN_LABPS_3005D_OUTPUT: + /* Read status from device */ + return velleman_labps3005d_send_cmd(serial, "STATUS?"); + default: + /* OOPS! */ + sr_err("Don't know how to query %d.", devc->target); + return SR_ERR; + } +} + +SR_PRIV int velleman_labps3005d_get_all_values(struct sr_serial_dev_inst *serial, + struct dev_context *devc) +{ + int ret; + devc->target = VELLEMAN_LABPS_3005D_CURRENT; + if( ((ret = velleman_labps3005d_query_value(serial, devc)) < 0 )&& + ((ret = velleman_labps3005d_get_reply(serial, devc)) < 0)) + return ret; + else + devc->target = VELLEMAN_LABPS_3005D_CURRENT_MAX; + + if( ((ret = velleman_labps3005d_query_value(serial, devc)) < 0 )&& + ((ret = velleman_labps3005d_get_reply(serial, devc)) < 0)) + return ret; + else + devc->target = VELLEMAN_LABPS_3005D_VOLTAGE; + if( ((ret = velleman_labps3005d_query_value(serial, devc)) < 0 )&& + ((ret = velleman_labps3005d_get_reply(serial, devc)) < 0)) + return ret; + else + devc->target = VELLEMAN_LABPS_3005D_VOLTAGE_MAX; + if( ((ret = velleman_labps3005d_query_value(serial, devc)) < 0 )&& + ((ret = velleman_labps3005d_get_reply(serial, devc)) < 0)) + return ret; + else + devc->target = VELLEMAN_LABPS_3005D_STATUS; + if( (ret = velleman_labps3005d_query_value(serial, devc)) >= 0 ) + ret = velleman_labps3005d_get_reply(serial, devc); + return ret; + +} + +SR_PRIV int velleman_labps3005d_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 VELLEMAN_LABPS_3005D_CURRENT: + /* Read current from device */ + target = &(devc->current); + break; + case VELLEMAN_LABPS_3005D_CURRENT_MAX: + /* Read set current from device */ + target = &(devc->current_max); + break; + case VELLEMAN_LABPS_3005D_VOLTAGE: + /* Read voltage from device */ + target = &(devc->voltage); + break; + case VELLEMAN_LABPS_3005D_VOLTAGE_MAX: + /* Read set voltage from device */ + target = &(devc->voltage_max); + break; + case VELLEMAN_LABPS_3005D_STATUS: + case VELLEMAN_LABPS_3005D_OUTPUT: + /* Read status from device */ + count = 1; + break; + default: + /* OOPS! */ + sr_err("Don't know where to put repply %d.", devc->target); + } + if( (ret = velleman_labps3005d_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 repply*/ + status_byte = devc->reply[0]; + /* Constant current */ + devc->cc_mode = !(status_byte & (1 << 0)); /* Channel one */ + /* CC (status_byte & (1 << 1)) Channel two + Tracking + status_byte & ((1 << 2)|(1 << 3)) + 00 independent 01 series 11 parallel + + status_byte & (1 << 4) Beep + status_byte & (1 << 5) Unlocked + */ + devc->output_enabled = (status_byte & (1<< 6)); + sr_dbg("status: %x", status_byte); + } + return ret; +} + +static void next_measurement(struct dev_context *devc) +{ + switch(devc->target){ + case VELLEMAN_LABPS_3005D_CURRENT: + devc->target = VELLEMAN_LABPS_3005D_VOLTAGE; + break; + case VELLEMAN_LABPS_3005D_CURRENT_MAX: + devc->target = VELLEMAN_LABPS_3005D_CURRENT; + break; + case VELLEMAN_LABPS_3005D_VOLTAGE: + devc->target = VELLEMAN_LABPS_3005D_STATUS; + break; + case VELLEMAN_LABPS_3005D_VOLTAGE_MAX: + devc->target = VELLEMAN_LABPS_3005D_CURRENT; + break; + case VELLEMAN_LABPS_3005D_OUTPUT: + devc->target = VELLEMAN_LABPS_3005D_STATUS; + break; + case VELLEMAN_LABPS_3005D_STATUS: + devc->target = VELLEMAN_LABPS_3005D_CURRENT; + break; + default: + devc->target = VELLEMAN_LABPS_3005D_CURRENT; + } +} SR_PRIV int velleman_labps3005d_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; if (!(sdi = cb_data)) @@ -32,10 +260,62 @@ SR_PRIV int velleman_labps3005d_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 */ + velleman_labps3005d_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 == VELLEMAN_LABPS_3005D_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 == VELLEMAN_LABPS_3005D_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++; + } } - + 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; + } + /* Send command to get next measurement. */ + next_measurement(devc); + if (velleman_labps3005d_query_value(serial, devc) < 0) + return TRUE; + + devc->req_sent_at = g_get_monotonic_time(); + devc->reply_pending = TRUE; + } return TRUE; } diff --git a/src/hardware/velleman-labps3005d/protocol.h b/src/hardware/velleman-labps3005d/protocol.h index 63fe994..1f00c1e 100644 --- a/src/hardware/velleman-labps3005d/protocol.h +++ b/src/hardware/velleman-labps3005d/protocol.h @@ -16,29 +16,80 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - +/** @file + * <em>Velleman LABPS3005D</em> power supply driver + * @internal + */ #ifndef LIBSIGROK_HARDWARE_VELLEMAN_LABPS3005D_PROTOCOL_H #define LIBSIGROK_HARDWARE_VELLEMAN_LABPS3005D_PROTOCOL_H #include <stdint.h> +#include <string.h> #include <glib.h> #include <libsigrok/libsigrok.h> #include "libsigrok-internal.h" #define LOG_PREFIX "velleman-labps3005d" +enum { + VELLEMAN_LABPS_3005D + /*Support for future devices with this protocol.*/ +}; + +/* Inofromation on single model*/ +struct velleman_labps_model { + int model_id; /**< Model info */ + char * name; /**< Model name */ + char * id; /**< Model ID, like delivered by interface */ + double voltage[3]; /**< Min, max, step */ + double current[3]; /**< Min, max, step */ +}; /** Private, per-device-instance driver context. */ +/* repply targets */ +enum { + VELLEMAN_LABPS_3005D_CURRENT, VELLEMAN_LABPS_3005D_CURRENT_MAX, + VELLEMAN_LABPS_3005D_VOLTAGE, VELLEMAN_LABPS_3005D_VOLTAGE_MAX, + VELLEMAN_LABPS_3005D_STATUS, VELLEMAN_LABPS_3005D_OUTPUT +}; + struct dev_context { /* Model-specific information */ - + const struct velleman_labps_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; /**< Device is in constant current mode (otherwise constant voltage). */ + + gboolean output_enabled; /**< Is the output enabled? */ /* Temporary state across callbacks */ - + int target; /**< what repply to expect */ + char reply[6]; }; +SR_PRIV int velleman_labps3005d_send_cmd(struct sr_serial_dev_inst *serial, + const char *cmd); +SR_PRIV int velleman_labps3005d_read_chars(struct sr_serial_dev_inst *serial, + int count, char *buf); +SR_PRIV int velleman_labps3005d_set_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int velleman_labps3005d_query_value(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int velleman_labps3005d_get_reply(struct sr_serial_dev_inst *serial, + struct dev_context *devc); +SR_PRIV int velleman_labps3005d_get_all_values(struct sr_serial_dev_inst *serial, + struct dev_context *devc); SR_PRIV int velleman_labps3005d_receive_data(int fd, int revents, void *cb_data); #endif -- 2.5.1
------------------------------------------------------------------------------
_______________________________________________ sigrok-devel mailing list sigrok-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/sigrok-devel