---
configure.ac | 12 ++
hardware/Makefile.am | 5 +
hardware/common/serial.c | 11 +-
hardware/norma-dmm/Makefile.am | 33 ++++
hardware/norma-dmm/api.c | 338 +++++++++++++++++++++++++++++++++
hardware/norma-dmm/protocol.c | 412 +++++++++++++++++++++++++++++++++++++++++
hardware/norma-dmm/protocol.h | 81 ++++++++
hwdriver.c | 6 +
std.c | 8 +-
9 files changed, 899 insertions(+), 7 deletions(-)
create mode 100644 hardware/norma-dmm/Makefile.am
create mode 100644 hardware/norma-dmm/api.c
create mode 100644 hardware/norma-dmm/protocol.c
create mode 100644 hardware/norma-dmm/protocol.h
diff --git a/configure.ac b/configure.ac
index 98fa39e..7c474d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -174,6 +174,11 @@ AC_ARG_ENABLE(mic-985xx,
AC_HELP_STRING([--enable-mic-985xx],
[HW_MIC_985XX="$enableval"],
[HW_MIC_985XX=$HW_ENABLED_DEFAULT])
+AC_ARG_ENABLE(norma-dmm, AC_HELP_STRING([--enable-norma-dmm],
+ [enable Norma DMM support [default=yes]]),
+ [HW_NORMA_DMM="$enableval"],
+ [HW_NORMA_DMM=$HW_ENABLED_DEFAULT])
+
AC_ARG_ENABLE(ols, AC_HELP_STRING([--enable-ols],
[enable OpenBench Logic Sniffer (OLS) support [default=yes]]),
[HW_OLS="$enableval"],
@@ -407,6 +412,11 @@ if test "x$HW_MIC_985XX" = "xyes"; then
AC_DEFINE(HAVE_HW_MIC_985XX, 1, [MIC 985xx support])
fi
+AM_CONDITIONAL(HW_NORMA_DMM, test x$HW_NORMA_DMM = xyes)
+if test "x$HW_NORMA_DMM" = "xyes"; then
+ AC_DEFINE(HAVE_HW_NORMA_DMM, 1, [norma-dmm support])
+fi
+
AM_CONDITIONAL(HW_OLS, test x$HW_OLS = xyes)
if test "x$HW_OLS" = "xyes"; then
AC_DEFINE(HAVE_HW_OLS, 1, [OpenBench Logic Sniffer (OLS) support])
@@ -511,6 +521,7 @@ AC_CONFIG_FILES([Makefile version.h hardware/Makefile
hardware/fx2lafw/Makefile
hardware/hantek-dso/Makefile
hardware/link-mso19/Makefile
+ hardware/norma-dmm/Makefile
hardware/openbench-logic-sniffer/Makefile
hardware/serial-dmm/Makefile
hardware/uni-t-dmm/Makefile
@@ -568,6 +579,7 @@ echo " - kecheng-kc-330b.................
$HW_KECHENG_KC_330B"
echo " - lascar-el-usb................... $HW_LASCAR_EL_USB"
echo " - link-mso19 (EXPERIMENTAL)....... $HW_LINK_MSO19"
echo " - mic-985xx....................... $HW_MIC_985XX"
+echo " - norma-dmm....................... $HW_NORMA_DMM"
echo " - openbench-logic-sniffer......... $HW_OLS"
echo " - rigol-ds1xx2.................... $HW_RIGOL_DS1XX2"
echo " - saleae-logic16.................. $HW_SALEAE_LOGIC16"
diff --git a/hardware/Makefile.am b/hardware/Makefile.am
index aa6a93f..06d2e06 100644
--- a/hardware/Makefile.am
+++ b/hardware/Makefile.am
@@ -38,6 +38,7 @@ SUBDIRS = \
lascar-el-usb \
link-mso19 \
mic-985xx \
+ norma-dmm \
openbench-logic-sniffer \
rigol-ds1xx2 \
saleae-logic16 \
@@ -127,6 +128,10 @@ if HW_MIC_985XX
libsigrokhardware_la_LIBADD += mic-985xx/libsigrok_hw_mic_985xx.la
endif
+if HW_NORMA_DMM
+libsigrokhardware_la_LIBADD += norma-dmm/libsigrok_hw_norma_dmm.la
+endif
+
if HW_OLS
libsigrokhardware_la_LIBADD += openbench-logic-sniffer/libsigrok_hw_ols.la
endif
diff --git a/hardware/common/serial.c b/hardware/common/serial.c
index 557022c..ebc6979 100644
--- a/hardware/common/serial.c
+++ b/hardware/common/serial.c
@@ -548,7 +548,7 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst
*serial, int baudrate,
return SR_ERR;
}
- term.c_iflag &= ~(IXON | IXOFF);
+ term.c_iflag &= ~(IXON | IXOFF | IXANY);
term.c_cflag &= ~CRTSCTS;
switch (flowcontrol) {
case 0:
@@ -561,7 +561,7 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst
*serial, int baudrate,
break;
case 2:
sr_spew("Configuring XON/XOFF flow control.");
- term.c_iflag |= IXON | IXOFF;
+ term.c_iflag |= (IXON | IXOFF | IXANY);
break;
default:
sr_err("Unsupported flow control setting %d.", flowcontrol);
@@ -569,7 +569,7 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst
*serial, int baudrate,
}
term.c_iflag &= ~IGNPAR;
- term.c_cflag &= ~(PARODD | PARENB);
+ term.c_cflag &= ~(PARENB | PARODD);
switch (parity) {
case SERIAL_PARITY_NONE:
sr_spew("Configuring no parity.");
@@ -590,7 +590,7 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst
*serial, int baudrate,
/* Turn off all serial port cooking. */
term.c_iflag &= ~(ISTRIP | INLCR | ICRNL);
- term.c_oflag &= ~(ONLCR | OCRNL | ONOCR);
+ term.c_oflag &= ~(OPOST | ONLCR | OCRNL | ONOCR);
#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
term.c_oflag &= ~OFILL;
#endif
@@ -598,6 +598,9 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst
*serial, int baudrate,
/* Disable canonical mode, and don't echo input characters. */
term.c_lflag &= ~(ICANON | ECHO);
+ /* Ignore modem status lines; enable receiver */
+ term.c_cflag |= (CLOCAL | CREAD);
+
/* Write the configured settings. */
if (tcsetattr(serial->fd, TCSADRAIN, &term) < 0) {
sr_err("tcsetattr() error: %s.", strerror(errno));
diff --git a/hardware/norma-dmm/Makefile.am b/hardware/norma-dmm/Makefile.am
new file mode 100644
index 0000000..e7fe49c
--- /dev/null
+++ b/hardware/norma-dmm/Makefile.am
@@ -0,0 +1,33 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2013 Matthias Heidbrink <[email protected]>
+##
+## 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/>.
+##
+
+if HW_NORMA_DMM
+
+# Local lib, this is NOT meant to be installed!
+noinst_LTLIBRARIES = libsigrok_hw_norma_dmm.la
+
+libsigrok_hw_norma_dmm_la_SOURCES = \
+ api.c \
+ protocol.c \
+ protocol.h
+
+libsigrok_hw_norma_dmm_la_CFLAGS = \
+ -I$(top_srcdir)
+
+endif
diff --git a/hardware/norma-dmm/api.c b/hardware/norma-dmm/api.c
new file mode 100644
index 0000000..1ed7f61
--- /dev/null
+++ b/hardware/norma-dmm/api.c
@@ -0,0 +1,338 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <[email protected]>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "protocol.h"
+
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+ SR_CONF_SERIALCOMM,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_MULTIMETER,
+ SR_CONF_LIMIT_SAMPLES,
+ SR_CONF_LIMIT_MSEC,
+ SR_CONF_CONTINUOUS,
+};
+
+
+#define SERIALCOMM "4800/8n1/dtr=1/rts=0/flow=1"
+
+
+SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
+static struct sr_dev_driver *di = &norma_dmm_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);
+}
+
+
+static GSList *scan(GSList *options)
+{
+ struct sr_dev_inst *sdi;
+ struct drv_context *drvc;
+ struct dev_context *devc;
+ struct sr_config *src;
+ struct sr_probe *probe;
+ struct sr_serial_dev_inst *serial;
+ GSList *l, *devices;
+ int len, cnt;
+ const char *conn, *serialcomm;
+ char *buf;
+ char fmttype[10];
+ char req[10];
+ int auxtype;
+
+ #define BUF_MAX (50)
+
+ devices = NULL;
+ drvc = di->priv;
+ drvc->instances = NULL;
+ conn = 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;
+ }
+ }
+ if (!conn)
+ return NULL;
+ if (!serialcomm)
+ serialcomm = SERIALCOMM;
+
+ if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+ return NULL;
+
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return NULL;
+
+ serial_flush(serial);
+
+ len = BUF_MAX;
+ if (!(buf = g_try_malloc(len))) {
+ sr_err("Serial buffer malloc failed.");
+ return NULL;
+ }
+
+ snprintf(req, sizeof(req), "%s\r\n",
+ nmadmm_requests[NMADMM_REQ_IDN].reqstr);
+ for (cnt = 0; cnt < 7; cnt++) {
+ if (serial_write(serial, req, strlen(req)) == -1) {
+ sr_err("Unable to send identification request: %d %s.",
+ errno, strerror(errno));
+ return NULL;
+ }
+ len = BUF_MAX;
+ serial_readline(serial, &buf, &len, 1500);
+ if (!len)
+ continue;
+ buf[BUF_MAX-1] = '\0';
+
+ /* Match id string, e.g. "1834 065 V1.06,IF V1.02" (DM950) */
+ if (g_regex_match_simple("^1834 [^,]*,IF V*", (char*)buf,0,0)) {
+ auxtype = xgittoint(buf[7]);
+ // TODO: Will this work with non-DM950?
+ snprintf(fmttype, sizeof(fmttype), "DM9%d0", auxtype);
+ sr_spew("Norma %s DMM %s detected!", fmttype, &buf[9]);
+
+ if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE,
+ "Norma", fmttype, buf + 9)))
+ return NULL;
+ if (!(devc = g_try_malloc0(sizeof(*devc)))) {
+ sr_err("Device context malloc failed.");
+ return NULL;
+ }
+ devc->type = auxtype;
+ devc->version = g_strdup(&buf[9]);
+ devc->elapsed_msec = g_timer_new();
+
+ sdi->conn = serial;
+ sdi->priv = devc;
+ sdi->driver = di;
+ if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE,
+ "P1")))
+ return NULL;
+ sdi->probes = g_slist_append(sdi->probes, probe);
+ drvc->instances = g_slist_append(drvc->instances, sdi);
+ devices = g_slist_append(devices, sdi);
+ break;
+ }
+ /* The interface of the DM9x0 contains a cap that needs to
+ charge for up to 10s before the interface works, if not powered
+ externally. Therefore wait a little to improve chances. */
+ if (cnt == 3) {
+ sr_info("Waiting 5s to allow interface to settle.");
+ g_usleep(5 * 1000 * 1000);
+ }
+ }
+
+ g_free(buf);
+
+ 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 dev_open(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+
+ serial = sdi->conn;
+ if (serial && serial->fd != -1) {
+ serial_close(serial);
+ sdi->status = SR_ST_INACTIVE;
+ }
+
+ // Free dynamically allocated resources.
+ if ((devc = sdi->priv) && devc->version) {
+ g_free(devc->version);
+ devc->version = NULL;
+ g_timer_destroy(devc->elapsed_msec);
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ if (!(devc = sdi->priv)) {
+ sr_err("sdi->priv was NULL.");
+ return SR_ERR_BUG;
+ }
+
+ switch (key) {
+ case SR_CONF_LIMIT_MSEC:
+ /* TODO: not yet implemented */
+ if (g_variant_get_uint64(data) == 0) {
+ sr_err("LIMIT_MSEC can't be 0.");
+ return SR_ERR;
+ }
+ devc->limit_msec = g_variant_get_uint64(data);
+ sr_dbg("Setting time limit to %" PRIu64 "ms.",
+ devc->limit_msec);
+ break;
+ case SR_CONF_LIMIT_SAMPLES:
+ devc->limit_samples = g_variant_get_uint64(data);
+ sr_dbg("Setting sample limit to %" PRIu64 ".",
+ devc->limit_samples);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
+{
+ (void)sdi;
+
+ 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,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ 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;
+
+ (void)sdi;
+ (void)cb_data;
+
+ if (!sdi || !cb_data || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ devc->cb_data = cb_data;
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Start timer, if required. */
+ if (devc->limit_msec)
+ g_timer_start(devc->elapsed_msec);
+
+ /* Poll every 100ms, or whenever some data comes in. */
+ serial = sdi->conn;
+ sr_source_add(serial->fd, G_IO_IN, 100, norma_dmm_receive_data,
+ (void *)sdi);
+
+ return SR_OK;
+}
+
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+
+ /* Stop timer, if required. */
+ if (sdi && (devc = sdi->priv) && devc->limit_msec)
+ g_timer_stop(devc->elapsed_msec);
+
+ return std_dev_acquisition_stop_serial(sdi, cb_data, dev_close,
+ sdi->conn, LOG_PREFIX);
+}
+
+
+SR_PRIV struct sr_dev_driver norma_dmm_driver_info = {
+ .name = "norma-dmm",
+ .longname = "Norma DM910..950, Siemens B1024..1028 DMMs",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = dev_clear,
+ .config_get = NULL,
+ .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,
+ .priv = NULL,
+};
diff --git a/hardware/norma-dmm/protocol.c b/hardware/norma-dmm/protocol.c
new file mode 100644
index 0000000..3b2bff7
--- /dev/null
+++ b/hardware/norma-dmm/protocol.c
@@ -0,0 +1,412 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <[email protected]>
+ *
+ * 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 "protocol.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+
+
+static int nma_send_req(const struct sr_dev_inst *sdi, enum nmadmm_req_t reqt,
+ char* params);
+
+
+/** Convert hexadecimal digit to int.
+ * \param[in] xgit Hexadecimal digit to convert.
+ * \return Int value of xgit (0 on invalid xgit).
+ */
+SR_PRIV int xgittoint(char xgit)
+{
+ if ((xgit >= '0') && (xgit <= '9'))
+ return xgit - '0';
+ xgit = tolower(xgit);
+ if ((xgit >= 'a') && (xgit <= 'f'))
+ return xgit - 'a';
+ return 0;
+}
+
+
+/** Process received line. It consists of 20 hex digits + \r\n,
+ * e.g. '08100400018100400000'. */
+static void nma_process_line(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int pos, flags;
+ int vt, range; /* Measurement value type, range in device format */
+ int mmode, devstat; /* Measuring mode, device status */
+ float value; /* Measured value */
+ float scale; /* Scaling factor depending on range and function */
+
+ struct sr_datafeed_analog analog;
+ struct sr_datafeed_packet packet;
+
+ devc = sdi->priv;
+
+ devc->buf[20] = '\0';
+
+ sr_spew("Received line '%s'.", devc->buf);
+
+ /* Check line */
+ if (strlen((char*)devc->buf) != 20) {
+ sr_err("line: Invalid status '%s', must be 20 hex digits");
+ devc->buflen = 0;
+ return;
+ }
+ for (pos = 0; pos < 20; pos++)
+ if (!isxdigit(devc->buf[pos])) {
+ sr_err("line: Expected hex digit in '%s' at pos %d!",
+ devc->buf, pos);
+ devc->buflen = 0;
+ return;
+ }
+
+ /* Start decoding. */
+ value = 0.0;
+ scale = 1.0;
+ memset(&analog, 0, sizeof(analog));
+
+ /* The numbers are hex digits, starting from 0. */
+ /* 0: Keyboard status, currently not interesting. */
+ /* 1: Central switch status, currently not interesting. */
+ /* 2: Type of measured value */
+ vt = xgittoint(devc->buf[2]);
+ switch (vt) {
+ case 0: analog.mq = SR_MQ_VOLTAGE;
+ break;
+ case 1: analog.mq = SR_MQ_CURRENT; // 2A
+ break;
+ case 2: analog.mq = SR_MQ_RESISTANCE;
+ break;
+ case 3: analog.mq = SR_MQ_CAPACITANCE;
+ break;
+ case 4: analog.mq = SR_MQ_TEMPERATURE;
+ break;
+ case 5: analog.mq = SR_MQ_FREQUENCY;
+ break;
+ case 6: analog.mq = SR_MQ_CURRENT; // 10A
+ break;
+ case 7: analog.mq = SR_MQ_GAIN; // TODO: Scale factor
+ break;
+ case 8: analog.mq = SR_MQ_GAIN; // Percentage
+ scale /= 100.0;
+ break;
+ case 9: analog.mq = SR_MQ_GAIN; // dB
+ scale /= 100.0;
+ break;
+ default:sr_err("Unknown value type 0x%02x", vt);
+ break;
+ }
+ /* 3: Measurement range for measured value */
+ range = xgittoint(devc->buf[3]);
+ switch (vt) {
+ case 0: // V
+ scale *= pow(10.0, range - 5);
+ break;
+ case 1: // A
+ scale *= pow(10.0, range - 7);
+ break;
+ case 2: // Ω
+ scale *= pow(10.0, range - 2);
+ break;
+ case 3: // F
+ scale *= pow(10.0, range - 12);
+ break;
+ case 4: // °C
+ scale *= pow(10.0, range - 1);
+ break;
+ case 5: // Hz
+ scale *= pow(10.0, range - 2);
+ break;
+ // No default, other value types have fixed display format
+ }
+
+ /* 5: Sign and 1st digit */
+ flags = xgittoint(devc->buf[5]);
+ value = (flags & 0x03);
+ if (flags & 0x04) scale *= -1;
+ /* 6-9: 2nd-4th digit */
+ for (pos = 6; pos < 10; pos++)
+ value = value * 10 + xgittoint(devc->buf[pos]);
+ value *= scale;
+
+ /* 10: Display counter */
+ mmode = xgittoint(devc->buf[10]);
+ switch (mmode) {
+ case 0: /* Frequency */
+ analog.unit = SR_UNIT_HERTZ;
+ break;
+ case 1: /* V TRMS, only type 5 */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
+ break;
+ case 2: /* V AC */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= SR_MQFLAG_AC;
+ if (devc->type >= 3) analog.mqflags |= SR_MQFLAG_RMS;
+ break;
+ case 3: /* V DC */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= SR_MQFLAG_DC;
+ break;
+ case 4: /* Ohm */
+ analog.unit = SR_UNIT_OHM;
+ break;
+ case 5: /* Continuity */
+ analog.unit = SR_UNIT_BOOLEAN;
+ analog.mq = SR_MQ_CONTINUITY;
+ /* TODO Continuity handling is a bit odd in sigrok. */
+ break;
+ case 6: /* Degree Celsius */
+ analog.unit = SR_UNIT_CELSIUS;
+ break;
+ case 7: /* Capacity */
+ analog.unit = SR_UNIT_FARAD;
+ break;
+ case 8: /* Current DC */
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags |= SR_MQFLAG_DC;
+ break;
+ case 9: /* Current AC */
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags |= SR_MQFLAG_AC;
+ if (devc->type >= 3) analog.mqflags |= SR_MQFLAG_RMS;
+ break;
+ case 0xA:/* Current TRMS, only type 5 */
+ analog.unit = SR_UNIT_AMPERE;
+ analog.mqflags |= (SR_MQFLAG_AC | SR_MQFLAG_DC | SR_MQFLAG_RMS);
+ break;
+ case 0x0B:/* Diode */
+ analog.unit = SR_UNIT_VOLT;
+ analog.mqflags |= (SR_MQFLAG_DIODE | SR_MQFLAG_DC);
+ break;
+ default:
+ sr_err("unknown mmode 0x%02x", mmode);
+ break;
+ }
+
+ /* 11: Device status */
+ devstat = xgittoint(devc->buf[11]);
+
+ switch (devstat) {
+ case 1: // Normal measurement
+ break;
+ case 2: // Input loop (limit, reference values)
+ break;
+ case 3: // TRANS/SENS
+ break;
+ case 4: // Error
+ sr_err("Device error. Fuse?"); // TODO: Wirklich abbrechen???
+ devc->buflen = 0;
+ return; // Cannot continue.
+ default:
+ sr_err("Unknown device status 0x02x", devstat);
+ break;
+ }
+
+ /* 12-19: Flags and display symbols */
+ /* 12, 13 */
+ flags = (xgittoint(devc->buf[12]) << 8) | xgittoint(devc->buf[13]);
+ /* 0x80: PRINT TODO: Stop polling when discovered? */
+ /* 0x40: EXTR */
+ if (analog.mq == SR_MQ_CONTINUITY) {
+ if (flags & 0x20) value = 1.0; /* Beep */
+ else value = 0.0;
+ }
+ /* 0x10: AVG */
+ /* 0x08: Diode */
+ if (flags & 0x04) /* REL */
+ analog.mqflags |= SR_MQFLAG_RELATIVE;
+ /* 0x02: SHIFT */
+ if (flags & 0x01) /* % */
+ analog.unit = SR_UNIT_PERCENTAGE;
+
+ /* 14, 15 */
+ flags = (xgittoint(devc->buf[14]) << 8) | xgittoint(devc->buf[15]);
+ if (!(flags & 0x80)) /* MAN Manual range */
+ analog.mqflags |= SR_MQFLAG_AUTORANGE;
+ if (flags & 0x40)/* LOBAT1 Low battery,measurement still within specs.*/
+ devc->lowbatt = 1;
+ /* 0x20: PEAK */
+ /* 0x10: COUNT */
+ if (flags & 0x08) // HOLD
+ analog.mqflags |= SR_MQFLAG_HOLD;
+ /* 0x04: LIMIT */
+ if (flags & 0x02) // MAX
+ analog.mqflags |= SR_MQFLAG_MAX;
+ if (flags & 0x01) // MIN
+ analog.mqflags |= SR_MQFLAG_MIN;
+
+ /* 16, 17 */
+ flags = (xgittoint(devc->buf[16]) << 8) | xgittoint(devc->buf[17]);
+ /* 0xe0: undefined */
+ if (flags & 0x10) { /* LOBATT2 Low battery, measurement inaccurate */
+ devc->lowbatt = 2;
+ sr_warn("Low battery, measurement quality degraded!");
+ }
+ /* 0x08: SCALED */
+ /* 0x04: RATE (=lower resolution, allows higher rata rate up to 10/s. */
+ /* 0x02: Current Clamp */
+ if (flags & 0x01) { /* dB */
+ if (analog.unit == SR_UNIT_VOLT)
+ analog.unit = SR_UNIT_DECIBEL_VOLT;
+ /* TODO: The Norma has an adjustable dB reference value. If
+ * changed from default, this is not correct. */
+ else
+ analog.unit = SR_UNIT_UNITLESS;
+ }
+
+ /* 18, 19 */
+ /* flags = (xgittoint(devc->buf[18]) << 8) | xgittoint(devc->buf[19]);*/
+ /* 0x80: Undefined. */
+ /* 0x40: Remote mode, keyboard locked */
+ /* 0x38: Undefined. */
+ /* 0x04: MIN>MAX */
+ /* 0x02: Measured value < Min */
+ /* 0x01: Measured value > Max */
+
+ /* 4: Flags. Evaluating this after setting value! */
+ flags = xgittoint(devc->buf[4]);
+ if (flags & 0x04) { // Invalid value
+ value = NAN;
+ }
+ else if (flags & 0x01) { // Overload
+ value = INFINITY;
+ }
+ if (flags & 0x02) { // Duplicate value, has been sent before.
+ sr_spew("Duplicate value, dismissing!");
+ devc->buflen = 0;
+ return;
+ }
+
+ sr_spew("range=%d/scale=%f/value=%f",range,(double)scale,(double)value);
+
+ /* Finish and send packet */
+ analog.probes = sdi->probes;
+ analog.num_samples = 1;
+ analog.data = &value;
+
+ memset(&packet, 0, sizeof(packet));
+ packet.type = SR_DF_ANALOG;
+ packet.payload = &analog;
+ sr_session_send(devc->cb_data, &packet);
+
+ /* Finish processing */
+ devc->num_samples++;
+ devc->buflen = 0;
+}
+
+
+SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+ int len;
+ gboolean terminating;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+ if (revents == G_IO_IN) {
+ /* Serial data arrived. */
+ while(NMADMM_BUFSIZE - devc->buflen - 1 > 0) {
+ len = serial_read(serial, devc->buf + devc->buflen, 1);
+ if (len < 1)
+ break;
+ devc->buflen += len;
+ *(devc->buf + devc->buflen) = '\0';
+ if (*(devc->buf + devc->buflen - 1) == '\n') {
+ /* TODO: According to specs, should be \r, but
+ * then we'd have to get rid of the \n */
+ devc->last_req_pending = FALSE;
+ nma_process_line(sdi);
+ break;
+ }
+ }
+ }
+
+ // If number of samples or time limit reached, stop aquisition.
+ terminating = FALSE;
+ if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ terminating = TRUE;
+ }
+
+ if (devc->limit_msec) {
+ gdouble elapsed_s = g_timer_elapsed(devc->elapsed_msec, NULL);
+ if ((elapsed_s * 1000) >= devc->limit_msec) {
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ terminating = TRUE;
+ }
+ }
+
+ // Request next package
+ if (!terminating && !devc->last_req_pending) {
+ if (nma_send_req(sdi, NMADMM_REQ_STATUS, NULL) != SR_OK)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int nma_send_req(const struct sr_dev_inst *sdi, enum nmadmm_req_t req,
+ char* params)
+{
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+ char buf[NMADMM_BUFSIZE];
+ int len;
+
+
+ if (!sdi || !(serial = sdi->conn) || !(devc = sdi->priv))
+ return SR_ERR_BUG;
+
+ len = snprintf(buf, sizeof(buf), "%s%s\r\n",
+ nmadmm_requests[req].reqstr, params?params:"");
+
+ sr_spew("Sending request: '%s'", buf);
+
+ devc->last_req = req;
+ devc->last_req_pending = TRUE;
+
+ if (serial_write(serial, buf, len) == -1) {
+ sr_err("Unable to send request: %d %s.",
+ errno, strerror(errno));
+ devc->last_req_pending = FALSE;
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+
+SR_PRIV const struct nmadmm_req nmadmm_requests[] = {
+ { NMADMM_REQ_IDN, "IDN?"},
+ { NMADMM_REQ_IDN, "STATUS?"},
+ { 0, NULL }
+};
diff --git a/hardware/norma-dmm/protocol.h b/hardware/norma-dmm/protocol.h
new file mode 100644
index 0000000..70a777a
--- /dev/null
+++ b/hardware/norma-dmm/protocol.h
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013 Matthias Heidbrink <[email protected]>
+ *
+ * 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_NORMA_DMM_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_NORMA_DMM_PROTOCOL_H
+
+#include <stdint.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/* Message logging helpers with subsystem-specific prefix string. */
+#define LOG_PREFIX "norma-dmm: "
+#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
+#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
+#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
+#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
+#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
+#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
+
+#define NMADMM_BUFSIZE 256
+
+/** Norma DMM request types (used ones only, the multimeters support about 50).
+ */
+enum nmadmm_req_t {
+ NMADMM_REQ_IDN = 0, /**< Request identity */
+ NMADMM_REQ_STATUS, /**< Request device status (value + ...) */
+};
+
+/** Defines requests used to communicate with device. */
+struct nmadmm_req {
+ enum nmadmm_req_t req_t; /** Request type. */
+ const char* reqstr; /** Request string */
+};
+
+/** Strings for requests. */
+extern const struct nmadmm_req nmadmm_requests[];
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Model-specific information */
+ char* version; /**< Version string */
+ int type; /**< DM9x0, e.g. 5 = DM950 */
+ /* Acquisition settings */
+ uint64_t limit_samples; /**< Target number of samples */
+ uint64_t limit_msec; /**< Target sampling time */
+
+ /* Opaque pointer passed in by frontend. */
+ void *cb_data;
+
+ /* Operational state */
+ enum nmadmm_req_t last_req; /**< Last request. */
+ gboolean last_req_pending; /**< Last request not answered yet.*/
+ int lowbatt; /**< Low battery. 1=low, 2=critical.*/
+ /* Temporary state across callbacks */
+ uint64_t num_samples; /**< Current #samples. */
+ GTimer* elapsed_msec; /**< Used for sampling with limit_msec*/
+ unsigned char buf[NMADMM_BUFSIZE]; /**< Buffer for read callback */
+ int buflen; /**< Data len in buf */
+};
+
+SR_PRIV int norma_dmm_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV int xgittoint(char xgit);
+
+#endif
diff --git a/hwdriver.c b/hwdriver.c
index 50615f5..c13f964 100644
--- a/hwdriver.c
+++ b/hwdriver.c
@@ -139,6 +139,9 @@ extern SR_PRIV struct sr_dev_driver
lascar_el_usb_driver_info;
extern SR_PRIV struct sr_dev_driver mic_98581_driver_info;
extern SR_PRIV struct sr_dev_driver mic_98583_driver_info;
#endif
+#ifdef HAVE_HW_NORMA_DMM
+extern SR_PRIV struct sr_dev_driver norma_dmm_driver_info;
+#endif
#ifdef HAVE_HW_OLS
extern SR_PRIV struct sr_dev_driver ols_driver_info;
#endif
@@ -253,6 +256,9 @@ static struct sr_dev_driver *drivers_list[] = {
&mic_98581_driver_info,
&mic_98583_driver_info,
#endif
+#ifdef HAVE_HW_NORMA_DMM
+ &norma_dmm_driver_info,
+#endif
#ifdef HAVE_HW_OLS
&ols_driver_info,
#endif
diff --git a/std.c b/std.c
index 46ab5ad..137d4e5 100644
--- a/std.c
+++ b/std.c
@@ -117,8 +117,10 @@ SR_PRIV int std_session_send_df_header(const struct
sr_dev_inst *sdi,
* @param prefix A driver-specific prefix string used for log messages.
* Must not be NULL. An empty string is allowed.
*
- * @return SR_OK upon success, SR_ERR_ARG upon invalid arguments, or
- * SR_ERR upon other errors.
+ * @retval SR_OK Success.
+ * @retval SR_ERR_ARG Invalid arguments.
+ * @retval SR_ERR_DEV_CLOSED Device is closed.
+ * @retval SR_ERR other errors.
*/
SR_PRIV int std_dev_acquisition_stop_serial(struct sr_dev_inst *sdi,
void *cb_data, dev_close_t dev_close_fn,
@@ -134,7 +136,7 @@ SR_PRIV int std_dev_acquisition_stop_serial(struct
sr_dev_inst *sdi,
if (sdi->status != SR_ST_ACTIVE) {
sr_err("%sDevice inactive, can't stop acquisition.", prefix);
- return SR_ERR;
+ return SR_ERR_DEV_CLOSED;
}
sr_dbg("%sStopping acquisition.", prefix);
--
1.8.0
------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135031&iu=/4140/ostg.clktrk
_______________________________________________
sigrok-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sigrok-devel