From: Uwe Hermann Date: Sun, 22 Jun 2014 22:40:58 +0000 (+0200) Subject: manson-hcs-3xxx: Initial driver implementation. X-Git-Tag: libsigrok-0.4.0~1279 X-Git-Url: http://sigrok.org/gitweb/?p=libsigrok.git;a=commitdiff_plain;h=b5e926479019bbd08b555102c3112f654bfd69a8 manson-hcs-3xxx: Initial driver implementation. Currently supports only few HCS-3xxx models (tested on HCS-3202), and not all commands/operations yet. --- diff --git a/hardware/manson-hcs-3xxx/api.c b/hardware/manson-hcs-3xxx/api.c index 983b06fd..2c8d1d7b 100644 --- a/hardware/manson-hcs-3xxx/api.c +++ b/hardware/manson-hcs-3xxx/api.c @@ -20,9 +20,36 @@ #include "protocol.h" +static const int32_t hwopts[] = { + SR_CONF_CONN, + SR_CONF_SERIALCOMM, +}; + +static const int32_t devopts[] = { + SR_CONF_POWER_SUPPLY, + SR_CONF_LIMIT_SAMPLES, + SR_CONF_LIMIT_MSEC, + SR_CONF_CONTINUOUS, + SR_CONF_OUTPUT_VOLTAGE, + SR_CONF_OUTPUT_CURRENT, +}; + +/* Note: All models have one power supply output only. */ +static struct hcs_model models[] = { + { MANSON_HCS_3200, "HCS-3200", { 1, 18, 0.1 }, { 0, 20, 0.01 } }, + { MANSON_HCS_3202, "HCS-3202", { 1, 36, 0.1 }, { 0, 10, 0.01 } }, + { MANSON_HCS_3204, "HCS-3204", { 1, 60, 0.1 }, { 0, 5, 0.01 } }, + { 0, NULL, { 0, 0, 0 }, { 0, 0, 0 }, }, +}; + SR_PRIV struct sr_dev_driver manson_hcs_3xxx_driver_info; static struct sr_dev_driver *di = &manson_hcs_3xxx_driver_info; +static int dev_clear(void) +{ + return std_dev_clear(di, NULL); +} + static int init(struct sr_context *sr_ctx) { return std_init(sr_ctx, di, LOG_PREFIX); @@ -30,44 +57,105 @@ static int init(struct sr_context *sr_ctx) static GSList *scan(GSList *options) { + int i, model_id; struct drv_context *drvc; - GSList *devices; + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_config *src; + struct sr_channel *ch; + GSList *devices, *l; + const char *conn, *serialcomm; + struct sr_serial_dev_inst *serial; + char reply[50], **tokens; - (void)options; - - devices = NULL; drvc = di->priv; drvc->instances = NULL; + devices = NULL; + conn = NULL; + serialcomm = 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; + } + } - return devices; -} + if (!conn) + return NULL; + if (!serialcomm) + serialcomm = "9600/8n1"; -static GSList *dev_list(void) -{ - return ((struct drv_context *)(di->priv))->instances; -} + if (!(serial = sr_serial_dev_inst_new(conn, serialcomm))) + return NULL; -static int dev_clear(void) -{ - return std_dev_clear(di, NULL); -} + if (serial_open(serial, SERIAL_RDWR) != SR_OK) + return NULL; -static int dev_open(struct sr_dev_inst *sdi) -{ - (void)sdi; + serial_flush(serial); - sdi->status = SR_ST_ACTIVE; + sr_info("Probing serial port %s.", conn); - return SR_OK; -} + /* Get the device model. */ + memset(&reply, 0, sizeof(reply)); + serial_write_blocking(serial, "GMOD\r", 5); + serial_read_blocking(serial, &reply, 8); + tokens = g_strsplit((const gchar *)&reply, "\r", 2); -static int dev_close(struct sr_dev_inst *sdi) -{ - (void)sdi; + model_id = -1; + for (i = 0; models[i].name != NULL; i++) { + if (!strcmp(models[i].name + 4, tokens[0])) + model_id = i; + } + if (model_id < 0) { + sr_err("Unknown model '%s' detected, aborting.", tokens[0]); + return NULL; + } + + if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Manson", + models[model_id].name, NULL))) { + sr_err("Failed to create device instance."); + return NULL; + } - sdi->status = SR_ST_INACTIVE; + g_strfreev(tokens); - return SR_OK; + sdi->inst_type = SR_INST_SERIAL; + sdi->conn = serial; + sdi->driver = di; + + if (!(ch = sr_channel_new(0, SR_CHANNEL_ANALOG, TRUE, "CH1"))) { + sr_err("Failed to create channel."); + return NULL; + } + sdi->channels = g_slist_append(sdi->channels, ch); + + devc = g_malloc0(sizeof(struct dev_context)); + devc->model = &models[model_id]; + + sdi->priv = devc; + + 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; +} + +static GSList *dev_list(void) +{ + return ((struct drv_context *)(di->priv))->instances; } static int cleanup(void) @@ -78,79 +166,117 @@ static int cleanup(void) static int config_get(int 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) + 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_OUTPUT_VOLTAGE: + *data = g_variant_new_double(devc->voltage); + break; + case SR_CONF_OUTPUT_CURRENT: + *data = g_variant_new_double(devc->current); + break; default: return SR_ERR_NA; } - return ret; + return SR_OK; } static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + struct dev_context *devc; - (void)data; (void)cg; if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - ret = SR_OK; + 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; default: - ret = SR_ERR_NA; + return SR_ERR_NA; } - return ret; + return SR_OK; } static int config_list(int 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) { + case SR_CONF_SCAN_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, + hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t)); + break; + case SR_CONF_DEVICE_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, + devopts, ARRAY_SIZE(devopts), sizeof(int32_t)); + 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) +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; + 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(serial, G_IO_IN, 10, hcs_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; - - 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 manson_hcs_3xxx_driver_info = { @@ -165,8 +291,8 @@ SR_PRIV struct sr_dev_driver manson_hcs_3xxx_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, .priv = NULL, diff --git a/hardware/manson-hcs-3xxx/protocol.c b/hardware/manson-hcs-3xxx/protocol.c index 821efc9f..03091ba1 100644 --- a/hardware/manson-hcs-3xxx/protocol.c +++ b/hardware/manson-hcs-3xxx/protocol.c @@ -20,10 +20,139 @@ #include "protocol.h" -SR_PRIV int manson_hcs_3xxx_receive_data(int fd, int revents, void *cb_data) +#define REQ_TIMEOUT_MS 500 + +static int send_cmd(struct sr_serial_dev_inst *serial, const char *cmd) +{ + int ret; + char *cmd_esc; + + cmd_esc = g_strescape(cmd, NULL); + sr_dbg("Sending '%s'.", cmd_esc); + + if ((ret = serial_write_blocking(serial, cmd, strlen(cmd))) < 0) { + sr_err("Error sending command: %d.", ret); + g_free(cmd_esc); + return ret; + } + + g_free(cmd_esc); + + return ret; +} + +static int parse_volt_curr_mode(struct sr_dev_inst *sdi, char **tokens) +{ + char *str; + double val; + struct dev_context *devc; + + devc = sdi->priv; + + /* Bytes 0-3: Voltage. */ + str = g_strndup(tokens[0], 4); + val = g_ascii_strtod(str, NULL) / 100; + devc->voltage = val; + g_free(str); + + /* Bytes 4-7: Current. */ + str = g_strndup((tokens[0] + 4), 4); + val = g_ascii_strtod(str, NULL) / 100; + devc->current = val; + g_free(str); + + /* Byte 8: Mode ('0' means CV, '1' means CC). */ + devc->cc_mode = (tokens[0][8] == '1'); + + return SR_OK; +} + +static void send_sample(struct sr_dev_inst *sdi) { - const struct sr_dev_inst *sdi; struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + + devc = sdi->priv; + + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + analog.channels = sdi->channels; + analog.num_samples = 1; + + analog.mq = SR_MQ_VOLTAGE; + analog.unit = SR_UNIT_VOLT; + analog.mqflags = SR_MQFLAG_DC; + analog.data = &devc->voltage; + sr_session_send(sdi, &packet); + + analog.mq = SR_MQ_CURRENT; + analog.unit = SR_UNIT_AMPERE; + analog.mqflags = 0; + analog.data = &devc->current; + sr_session_send(sdi, &packet); + + devc->num_samples++; +} + +static int parse_reply(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + char *reply_esc, **tokens; + + devc = sdi->priv; + + reply_esc = g_strescape(devc->buf, NULL); + sr_dbg("Received '%s'.", reply_esc); + + tokens = g_strsplit(devc->buf, "\r", 0); + + if (parse_volt_curr_mode(sdi, tokens) < 0) + return SR_ERR; + send_sample(sdi); + + g_free(reply_esc); + g_strfreev(tokens); + + return SR_OK; +} + +static int handle_new_data(struct sr_dev_inst *sdi) +{ + int len; + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + + devc = sdi->priv; + serial = sdi->conn; + + len = serial_read(serial, devc->buf + devc->buflen, 1); + if (len < 1) + return SR_ERR; + + devc->buflen += len; + devc->buf[devc->buflen] = '\0'; + + /* Wait until we received an "OK\r" (among other bytes). */ + if (!g_str_has_suffix(devc->buf, "OK\r")) + return SR_OK; + + parse_reply(sdi); + + devc->buf[0] = '\0'; + devc->buflen = 0; + + devc->reply_pending = FALSE; + + return SR_OK; +} + +SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + int64_t t, elapsed_us; (void)fd; @@ -33,7 +162,45 @@ SR_PRIV int manson_hcs_3xxx_receive_data(int fd, int revents, void *cb_data) if (!(devc = sdi->priv)) return TRUE; + serial = sdi->conn; + if (revents == G_IO_IN) { + /* New data arrived. */ + handle_new_data(sdi); + } else { + /* Timeout. */ + } + + 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 voltage, current, and mode (CC or CV). */ + if (send_cmd(serial, "GETD\r") < 0) + return TRUE; + + devc->req_sent_at = g_get_monotonic_time(); + devc->reply_pending = TRUE; } return TRUE; diff --git a/hardware/manson-hcs-3xxx/protocol.h b/hardware/manson-hcs-3xxx/protocol.h index e5fdeeee..572d9274 100644 --- a/hardware/manson-hcs-3xxx/protocol.h +++ b/hardware/manson-hcs-3xxx/protocol.h @@ -22,16 +22,48 @@ #define LIBSIGROK_HARDWARE_MANSON_HCS_3XXX_PROTOCOL_H #include +#include +#include #include #include "libsigrok.h" #include "libsigrok-internal.h" #define LOG_PREFIX "manson-hcs-3xxx" +enum { + MANSON_HCS_3200, + MANSON_HCS_3202, + MANSON_HCS_3204, +}; + +struct hcs_model { + int model_id; + char *name; + double voltage[3]; /* Min, max, step */ + double current[3]; /* Min, max, step */ +}; + /** Private, per-device-instance driver context. */ struct dev_context { + struct hcs_model *model; + + 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; + + float voltage; + float current; + gboolean cc_mode; + + char buf[50]; + int buflen; }; -SR_PRIV int manson_hcs_3xxx_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int hcs_receive_data(int fd, int revents, void *cb_data); #endif