From: Bert Vermeulen Date: Tue, 25 Sep 2012 17:38:59 +0000 (+0200) Subject: fluke-dmm: support for all basic 287 functionality X-Git-Tag: dsupstream~679 X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;h=d38d2ef0ce0e4ec49369e6cbfac616d9b1065c38;p=libsigrok.git fluke-dmm: support for all basic 287 functionality --- diff --git a/hardware/fluke-dmm/Makefile.am b/hardware/fluke-dmm/Makefile.am index 1140b4c9..8f378023 100644 --- a/hardware/fluke-dmm/Makefile.am +++ b/hardware/fluke-dmm/Makefile.am @@ -24,6 +24,7 @@ noinst_LTLIBRARIES = libsigrokhwflukedmm.la libsigrokhwflukedmm_la_SOURCES = \ api.c \ + fluke.c \ fluke-dmm.h libsigrokhwflukedmm_la_CFLAGS = \ diff --git a/hardware/fluke-dmm/api.c b/hardware/fluke-dmm/api.c index 3b3d3f7b..e75849e6 100644 --- a/hardware/fluke-dmm/api.c +++ b/hardware/fluke-dmm/api.c @@ -51,8 +51,8 @@ SR_PRIV struct sr_dev_driver flukedmm_driver_info; static struct sr_dev_driver *di = &flukedmm_driver_info; static const struct flukedmm_profile supported_flukedmm[] = { - { FLUKE_187, "187" }, - { FLUKE_287, "287" }, + { FLUKE_187, "187", 100 }, + { FLUKE_287, "287", 100 }, }; @@ -404,7 +404,14 @@ static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, sr_session_send(devc->cb_data, &packet); /* Poll every 100ms, or whenever some data comes in. */ - sr_source_add(devc->serial->fd, G_IO_IN, 100, fluke_receive_data, (void *)sdi); + sr_source_add(devc->serial->fd, G_IO_IN, 50, fluke_receive_data, (void *)sdi); + + if (serial_write(devc->serial->fd, "QM\r", 3) == -1) { + sr_err("fluke-dmm: unable to send QM: %s", strerror(errno)); + return SR_ERR; + } + devc->cmd_sent_at = g_get_monotonic_time() / 1000; + devc->expect_response = TRUE; return SR_OK; } diff --git a/hardware/fluke-dmm/fluke-dmm.h b/hardware/fluke-dmm/fluke-dmm.h index 7174531c..0aac45b7 100644 --- a/hardware/fluke-dmm/fluke-dmm.h +++ b/hardware/fluke-dmm/fluke-dmm.h @@ -33,6 +33,8 @@ enum { struct flukedmm_profile { int model; const char *modelname; + /* How often to poll, in ms. */ + int poll_period; }; /* Private driver context. */ @@ -53,8 +55,12 @@ struct dev_context { /* Runtime. */ uint64_t num_samples; - unsigned char buf[FLUKEDMM_BUFSIZE]; + char buf[FLUKEDMM_BUFSIZE]; int buflen; + int64_t cmd_sent_at; + int expect_response; }; +SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data); + #endif /* LIBSIGROK_FLUKE_DMM_H */ diff --git a/hardware/fluke-dmm/fluke.c b/hardware/fluke-dmm/fluke.c new file mode 100644 index 00000000..80950975 --- /dev/null +++ b/hardware/fluke-dmm/fluke.c @@ -0,0 +1,234 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2012 Bert Vermeulen + * + * 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 "libsigrok.h" +#include "libsigrok-internal.h" +#include "config.h" +#include "fluke-dmm.h" +#include +#include +#include +#include + + +static struct sr_datafeed_analog *handle_qm_v2(const struct sr_dev_inst *sdi, + char **tokens) +{ + struct sr_datafeed_analog *analog; + float fvalue; + char *eptr; + + (void)sdi; + fvalue = strtof(tokens[0], &eptr); + if (fvalue == 0.0 && eptr == tokens[0]) { + sr_err("fluke-dmm: invalid float"); + return NULL; + } + + analog = g_try_malloc0(sizeof(struct sr_datafeed_analog)); + analog->num_samples = 1; + analog->data = g_try_malloc(sizeof(float)); + *analog->data = fvalue; + analog->mq = -1; + + if (!strcmp(tokens[1], "VAC") || !strcmp(tokens[1], "VDC")) { + analog->mq = SR_MQ_VOLTAGE; + analog->unit = SR_UNIT_VOLT; + if (!strcmp(tokens[2], "NORMAL")) { + if (tokens[1][1] == 'A') { + analog->mqflags |= SR_MQFLAG_AC; + analog->mqflags |= SR_MQFLAG_RMS; + } else + analog->mqflags |= SR_MQFLAG_DC; + } else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) { + *analog->data = NAN; + } else + analog->mq = -1; + } if (!strcmp(tokens[1], "CEL") || !strcmp(tokens[1], "FAR")) { + if (!strcmp(tokens[2], "NORMAL")) { + analog->mq = SR_MQ_TEMPERATURE; + if (tokens[1][0] == 'C') + analog->unit = SR_UNIT_CELSIUS; + else + analog->unit = SR_UNIT_FAHRENHEIT; + } + } if (!strcmp(tokens[1], "OHM")) { + if (!strcmp(tokens[3], "NONE")) { + analog->mq = SR_MQ_RESISTANCE; + analog->unit = SR_UNIT_OHM; + if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) { + *analog->data = INFINITY; + } else if (strcmp(tokens[2], "NORMAL")) + analog->mq = -1; + } else if (!strcmp(tokens[3], "OPEN_CIRCUIT")) { + analog->mq = SR_MQ_CONTINUITY; + analog->unit = SR_UNIT_BOOLEAN; + *analog->data = 0.0; + } else if (!strcmp(tokens[3], "SHORT_CIRCUIT")) { + analog->mq = SR_MQ_CONTINUITY; + analog->unit = SR_UNIT_BOOLEAN; + *analog->data = 1.0; + } + } if (!strcmp(tokens[1], "F") + && !strcmp(tokens[2], "NORMAL") + && !strcmp(tokens[3], "NONE")) { + analog->mq = SR_MQ_CAPACITANCE; + analog->unit = SR_UNIT_FARAD; + } else if (!strcmp(tokens[1], "AAC") || !strcmp(tokens[1], "ADC")) { + analog->mq = SR_MQ_CURRENT; + analog->unit = SR_UNIT_AMPERE; + analog->mqflags = SR_MQFLAG_RMS; + if (!strcmp(tokens[2], "NORMAL")) { + if (tokens[1][1] == 'A') { + analog->mqflags |= SR_MQFLAG_AC; + analog->mqflags |= SR_MQFLAG_RMS; + } else + analog->mqflags |= SR_MQFLAG_DC; + } else if (!strcmp(tokens[2], "OL") || !strcmp(tokens[2], "OL_MINUS")) { + *analog->data = NAN; + } else + analog->mq = -1; + } if (!strcmp(tokens[1], "Hz") && !strcmp(tokens[2], "NORMAL")) { + analog->mq = SR_MQ_FREQUENCY; + analog->unit = SR_UNIT_HERTZ; + } if (!strcmp(tokens[1], "PCT") && !strcmp(tokens[2], "NORMAL")) { + analog->mq = SR_MQ_DUTY_CYCLE; + analog->unit = SR_UNIT_PERCENTAGE; + } if (!strcmp(tokens[1], "S") && !strcmp(tokens[2], "NORMAL")) { + analog->mq = SR_MQ_PULSE_WIDTH; + analog->unit = SR_UNIT_SECOND; + } if (!strcmp(tokens[1], "SIE") && !strcmp(tokens[2], "NORMAL")) { + analog->mq = SR_MQ_CONDUCTANCE; + analog->unit = SR_UNIT_SIEMENS; + } + + + if (analog->mq == -1) { + /* Not a valid measurement. */ + g_free(analog->data); + g_free(analog); + analog = NULL; + } + + return analog; +} + +static void handle_line(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog *analog; + char **tokens; + + devc = sdi->priv; + sr_spew("fluke-dmm: received line '%s' (%d)", devc->buf, devc->buflen); + + if (devc->buflen == 1) { + if (devc->buf[0] != '0') { + /* Not just a CMD_ACK from the query command. */ + sr_dbg("fluke-dmm: got CMD_ACK '%c'", devc->buf[0]); + devc->expect_response = FALSE; + } + devc->buflen = 0; + return; + } + + analog = NULL; + tokens = g_strsplit(devc->buf, ",", 0); + if (devc->profile->model == FLUKE_187) { + if (!strcmp(tokens[0], "QM")) { + /* Yes, this is the response to the query. */ + devc->expect_response = FALSE; + sr_spew("187 line"); + /* TODO */ + devc->buflen = 0; + } + } else if (devc->profile->model == FLUKE_287) { + devc->expect_response = FALSE; + analog = handle_qm_v2(sdi, tokens); + } + g_strfreev(tokens); + devc->buflen = 0; + + if (analog) { + /* Got a measurement. */ + sr_dbg("val %f", *analog->data); + packet.type = SR_DF_ANALOG; + packet.payload = analog; + sr_session_send(devc->cb_data, &packet); + devc->num_samples++; + g_free(analog->data); + g_free(analog); + } + +} + +SR_PRIV int fluke_receive_data(int fd, int revents, void *cb_data) +{ + const struct sr_dev_inst *sdi; + struct dev_context *devc; + int len; + int64_t now, elapsed; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + if (revents == G_IO_IN) { + /* Serial data arrived. */ + while(FLUKEDMM_BUFSIZE - devc->buflen - 1 > 0) { + len = serial_read(fd, devc->buf + devc->buflen, 1); + if (len < 1) + break; + devc->buflen++; + *(devc->buf + devc->buflen) = '\0'; + if (*(devc->buf + devc->buflen - 1) == '\r') { + *(devc->buf + --devc->buflen) = '\0'; + handle_line(sdi); + break; + } + } + } + + if (devc->num_samples >= devc->limit_samples) { + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + + now = g_get_monotonic_time() / 1000; + elapsed = now - devc->cmd_sent_at; + /* Send query command at poll_period interval, or after 1 second + * has elapsed. This will make it recover from any out-of-sync + * or temporary disconnect issues. */ + if ((devc->expect_response == FALSE && elapsed > devc->profile->poll_period) + || elapsed > 1000) { + sr_spew("fluke-dmm: sending QM"); + if (serial_write(devc->serial->fd, "QM\r", 3) == -1) + sr_err("fluke-dmm: unable to send QM: %s", strerror(errno)); + devc->cmd_sent_at = now; + devc->expect_response = TRUE; + } + + return TRUE; +} + +