diff --git a/configure.ac b/configure.ac index 50d81239..63280d05 100644 --- a/configure.ac +++ b/configure.ac @@ -423,7 +423,7 @@ 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]) + AC_DEFINE(HAVE_HW_NORMA_DMM, 1, [Norma DMM support]) fi AM_CONDITIONAL(HW_OLS, test x$HW_OLS = xyes) diff --git a/hardware/norma-dmm/api.c b/hardware/norma-dmm/api.c index 143f0387..991a839b 100644 --- a/hardware/norma-dmm/api.c +++ b/hardware/norma-dmm/api.c @@ -17,13 +17,8 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "protocol.h" - static const int32_t hwopts[] = { SR_CONF_CONN, SR_CONF_SERIALCOMM, @@ -36,20 +31,18 @@ static const int32_t hwcaps[] = { SR_CONF_CONTINUOUS, }; +#define BUF_MAX 50 #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 init(struct sr_context *sr_ctx) { return std_init(sr_ctx, di, LOG_PREFIX); } - static GSList *scan(GSList *options) { struct sr_dev_inst *sdi; @@ -66,8 +59,6 @@ static GSList *scan(GSList *options) char req[10]; int auxtype; - #define BUF_MAX (50) - devices = NULL; drvc = di->priv; drvc->instances = NULL; @@ -97,28 +88,27 @@ static GSList *scan(GSList *options) serial_flush(serial); - len = BUF_MAX; - if (!(buf = g_try_malloc(len))) { + if (!(buf = g_try_malloc(BUF_MAX))) { sr_err("Serial buffer malloc failed."); return NULL; } snprintf(req, sizeof(req), "%s\r\n", - nmadmm_requests[NMADMM_REQ_IDN].reqstr); + nmadmm_requests[NMADMM_REQ_IDN].req_str); 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)); + errno, strerror(errno)); return NULL; } len = BUF_MAX; serial_readline(serial, &buf, &len, 1500); if (!len) continue; - buf[BUF_MAX-1] = '\0'; + 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)) { + /* 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); @@ -127,7 +117,7 @@ static GSList *scan(GSList *options) if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Norma", fmttype, buf + 9))) return NULL; - if (!(devc = g_try_malloc0(sizeof(*devc)))) { + if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) { sr_err("Device context malloc failed."); return NULL; } @@ -146,9 +136,13 @@ static GSList *scan(GSList *options) 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. */ + + /* + * 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); @@ -164,19 +158,16 @@ static GSList *scan(GSList *options) return devices; } - static GSList *dev_list(void) { return ((struct drv_context *)(di->priv))->instances; } - static int dev_clear(void) { return std_dev_clear(di, NULL); } - static int dev_open(struct sr_dev_inst *sdi) { struct sr_serial_dev_inst *serial; @@ -190,7 +181,6 @@ static int dev_open(struct sr_dev_inst *sdi) return SR_OK; } - static int dev_close(struct sr_dev_inst *sdi) { struct sr_serial_dev_inst *serial; @@ -202,7 +192,7 @@ static int dev_close(struct sr_dev_inst *sdi) sdi->status = SR_ST_INACTIVE; } - // Free dynamically allocated resources. + /* Free dynamically allocated resources. */ if ((devc = sdi->priv) && devc->version) { g_free(devc->version); devc->version = NULL; @@ -212,13 +202,11 @@ static int dev_close(struct sr_dev_inst *sdi) 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; @@ -254,7 +242,6 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi) return SR_OK; } - static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi) { (void)sdi; @@ -275,7 +262,6 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi) return SR_OK; } - static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) { @@ -308,7 +294,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, return SR_OK; } - static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) { struct dev_context *devc; @@ -321,10 +306,9 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) 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", + .longname = "Norma DM9x0 / Siemens B102x DMMs", .api_version = 1, .init = init, .cleanup = cleanup, diff --git a/hardware/norma-dmm/protocol.c b/hardware/norma-dmm/protocol.c index 3b2bff7c..45546ab0 100644 --- a/hardware/norma-dmm/protocol.c +++ b/hardware/norma-dmm/protocol.c @@ -19,377 +19,26 @@ #include "protocol.h" -#include -#include -#include -#include +SR_PRIV const struct nmadmm_req nmadmm_requests[] = { + { NMADMM_REQ_IDN, "IDN?" }, + { NMADMM_REQ_IDN, "STATUS?" }, + { 0, NULL }, +}; - -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) +static int nma_send_req(const struct sr_dev_inst *sdi, int 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:""); + nmadmm_requests[req].req_str, params ? params : ""); - sr_spew("Sending request: '%s'", buf); + sr_spew("Sending request: '%s'.", buf); devc->last_req = req; devc->last_req_pending = TRUE; @@ -404,9 +53,374 @@ static int nma_send_req(const struct sr_dev_inst *sdi, enum nmadmm_req_t req, return SR_OK; } +/** + * 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; +} -SR_PRIV const struct nmadmm_req nmadmm_requests[] = { - { NMADMM_REQ_IDN, "IDN?"}, - { NMADMM_REQ_IDN, "STATUS?"}, - { 0, NULL } -}; +/** + * 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((const char *)devc->buf) != 20) { + sr_err("line: Invalid status '%s', must be 20 hex digits.", + devc->buf); + 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 libsigrok. */ + 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 0xb: /* 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: Really abort? */ + devc->buflen = 0; + return; /* Cannot continue. */ + default: + sr_err("Unknown device status: 0x%02x", 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) /* LOBATT1: 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 */ + /* + * TODO: The Norma has an adjustable dB reference value. If + * changed from default, this is not correct. + */ + if (analog.unit == SR_UNIT_VOLT) + analog.unit = SR_UNIT_DECIBEL_VOLT; + 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; + gdouble elapsed_s; + + (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) { + 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; +} diff --git a/hardware/norma-dmm/protocol.h b/hardware/norma-dmm/protocol.h index 70a777af..e9a956e3 100644 --- a/hardware/norma-dmm/protocol.h +++ b/hardware/norma-dmm/protocol.h @@ -21,6 +21,10 @@ #define LIBSIGROK_HARDWARE_NORMA_DMM_PROTOCOL_H #include +#include +#include +#include +#include #include #include "libsigrok.h" #include "libsigrok-internal.h" @@ -36,17 +40,16 @@ #define NMADMM_BUFSIZE 256 -/** Norma DMM request types (used ones only, the multimeters support about 50). - */ -enum nmadmm_req_t { +/** Norma DMM request types (used ones only, the DMMs support about 50). */ +enum { 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 */ + int req_type; /**< Request type. */ + const char *req_str; /**< Request string. */ }; /** Strings for requests. */ @@ -55,8 +58,9 @@ 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 */ + 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 */ @@ -65,13 +69,14 @@ struct dev_context { 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.*/ + int 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 */ + GTimer *elapsed_msec; /**< Used for limit_msec */ + uint8_t buf[NMADMM_BUFSIZE]; /**< Buffer for read callback */ int buflen; /**< Data len in buf */ };