From: Bert Vermeulen Date: Tue, 6 Nov 2012 18:37:33 +0000 (+0100) Subject: victor-dmm: add protocol decoder X-Git-Tag: dsupstream~582 X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;h=ff945683664a916566289f9bdbb371dfba6f3176;p=libsigrok.git victor-dmm: add protocol decoder --- diff --git a/hardware/victor-dmm/api.c b/hardware/victor-dmm/api.c index 9ee2b286..c98bdcc9 100644 --- a/hardware/victor-dmm/api.c +++ b/hardware/victor-dmm/api.c @@ -322,10 +322,8 @@ static void receive_transfer(struct libusb_transfer *transfer) hw_dev_acquisition_stop(sdi, sdi); } else if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { sr_dbg("got %d-byte packet", transfer->actual_length); - if (transfer->actual_length == 14) { - devc->num_samples++; - /* TODO */ - + if (transfer->actual_length == DMM_DATA_SIZE) { + victor_dmm_receive_data(sdi, transfer->buffer); if (devc->limit_samples) { if (devc->num_samples >= devc->limit_samples) hw_dev_acquisition_stop(sdi, sdi); @@ -434,14 +432,14 @@ static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, } devc->usbfd[i] = -1; - buf = g_try_malloc(14); + buf = g_try_malloc(DMM_DATA_SIZE); transfer = libusb_alloc_transfer(0); /* Each transfer request gets 100ms to arrive before it's restarted. * The device only sends 1 transfer/second no matter how many * times you ask, but we want to keep step with the USB events * handling above. */ libusb_fill_interrupt_transfer(transfer, devc->usb->devhdl, - VICTOR_ENDPOINT, buf, 14, receive_transfer, cb_data, 100); + VICTOR_ENDPOINT, buf, DMM_DATA_SIZE, receive_transfer, cb_data, 100); if ((ret = libusb_submit_transfer(transfer) != 0)) { sr_err("unable to submit transfer: %s", libusb_error_name(ret)); libusb_free_transfer(transfer); diff --git a/hardware/victor-dmm/protocol.c b/hardware/victor-dmm/protocol.c index efa01168..cd92bafb 100644 --- a/hardware/victor-dmm/protocol.c +++ b/hardware/victor-dmm/protocol.c @@ -17,26 +17,279 @@ * along with this program. If not, see . */ -#include #include #include "libsigrok.h" #include "libsigrok-internal.h" #include "protocol.h" +#include +#include -SR_PRIV int victor_dmm_receive_data(int fd, int revents, void *cb_data) + +/* Reverse the high nibble into the low nibble */ +static uint8_t decode_digit(uint8_t in) +{ + uint8_t out, i; + + out = 0; + in >>= 4; + for (i = 0x08; i; i >>= 1) { + out >>= 1; + if (in & i) + out |= 0x08; + } + + return out; +} + +static void decode_buf(struct dev_context *devc, unsigned char *data) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + long factor, ivalue; + uint8_t digits[4]; + gboolean is_duty, is_continuity, is_diode, is_ac, is_dc, is_auto; + gboolean is_hold, is_max, is_min, is_relative, minus; + float fvalue; + + digits[0] = decode_digit(data[12]); + digits[1] = decode_digit(data[11]); + digits[2] = decode_digit(data[10]); + digits[3] = decode_digit(data[9]); + + if (digits[0] == 0x0f && digits[1] == 0x00 && digits[2] == 0x0a && + digits[3] == 0x0f) + /* The "over limit" (OL) display comes through like this */ + ivalue = -1; + else if (digits[0] > 9 || digits[1] > 9 || digits[2] > 9 || digits[3] > 9) + /* An invalid digit in any position denotes no value. */ + ivalue = -2; + else { + ivalue = digits[0] * 1000; + ivalue += digits[1] * 100; + ivalue += digits[2] * 10; + ivalue += digits[3]; + } + + /* Decimal point position */ + switch (data[7] >> 4) { + case 0x00: + factor = 0; + break; + case 0x02: + factor = 1; + break; + case 0x04: + factor = 2; + break; + case 0x08: + factor = 3; + break; + default: + sr_err("Unknown decimal point value %.2x.", data[7]); + } + + /* Minus flag */ + minus = data[2] & 0x01; + + /* Mode detail symbols on the right side of the digits */ + is_duty = is_continuity = is_diode = FALSE; + switch (data[4]) { + case 0x00: + /* None. */ + break; + case 0x01: + /* Micro */ + factor += 6; + break; + case 0x02: + /* Milli */ + factor += 3; + break; + case 0x04: + /* Kilo */ + ivalue *= 1000; + break; + case 0x08: + /* Mega */ + ivalue *= 1000000; + break; + case 0x10: + /* Continuity shows up as Ohm + this bit */ + is_continuity = TRUE; + break; + case 0x20: + /* Diode tester is Volt + this bit */ + is_diode = TRUE; + break; + case 0x40: + is_duty = TRUE; + break; + case 0x80: + /* Never seen */ + sr_dbg("Unknown mode right detail %.2x.", data[4]); + break; + default: + sr_dbg("Unknown/invalid mode right detail %.2x.", data[4]); + } + + /* Scale flags on the right, continued */ + is_max = is_min = TRUE; + if (data[5] & 0x04) + is_max = TRUE; + if (data[5] & 0x08) + is_min = TRUE; + if (data[5] & 0x40) + /* Nano */ + factor += 9; + + /* Mode detail symbols on the left side of the digits */ + is_auto = is_dc = is_ac = is_hold = is_relative = FALSE; + if (data[6] & 0x04) + is_auto = TRUE; + if (data[6] & 0x08) + is_dc = TRUE; + if (data[6] & 0x10) + is_ac = TRUE; + if (data[6] & 0x20) + is_relative = TRUE; + if (data[6] & 0x40) + is_hold = TRUE; + + fvalue = (float)ivalue / pow(10, factor); + if (minus) + fvalue = -fvalue; + + memset(&analog, 0, sizeof(struct sr_datafeed_analog)); + + /* Measurement mode */ + analog.mq = -1; + switch (data[3]) { + case 0x00: + if (is_duty) { + analog.mq = SR_MQ_DUTY_CYCLE; + analog.unit = SR_UNIT_PERCENTAGE; + } else + sr_dbg("Unknown measurement mode %.2x.", data[3]); + break; + case 0x01: + if (is_diode) { + analog.mq = SR_MQ_VOLTAGE; + analog.unit = SR_UNIT_VOLT; + analog.mqflags |= SR_MQFLAG_DIODE; + if (ivalue < 0) + fvalue = NAN; + } else { + if (ivalue < 0) + break; + analog.mq = SR_MQ_VOLTAGE; + analog.unit = SR_UNIT_VOLT; + if (is_ac) + analog.mqflags |= SR_MQFLAG_AC; + if (is_dc) + analog.mqflags |= SR_MQFLAG_DC; + } + break; + case 0x02: + analog.mq = SR_MQ_CURRENT; + analog.unit = SR_UNIT_AMPERE; + if (is_ac) + analog.mqflags |= SR_MQFLAG_AC; + if (is_dc) + analog.mqflags |= SR_MQFLAG_DC; + break; + case 0x04: + if (is_continuity) { + analog.mq = SR_MQ_CONTINUITY; + analog.unit = SR_UNIT_BOOLEAN; + fvalue = ivalue < 0 ? 0.0 : 1.0; + } else { + analog.mq = SR_MQ_RESISTANCE; + analog.unit = SR_UNIT_OHM; + if (ivalue < 0) + fvalue = INFINITY; + } + break; + case 0x08: + /* Never seen */ + sr_dbg("Unknown measurement mode %.2x.", data[3]); + break; + case 0x10: + analog.mq = SR_MQ_FREQUENCY; + analog.unit = SR_UNIT_HERTZ; + break; + case 0x20: + analog.mq = SR_MQ_CAPACITANCE; + analog.unit = SR_UNIT_FARAD; + break; + case 0x40: + analog.mq = SR_MQ_TEMPERATURE; + analog.unit = SR_UNIT_CELSIUS; + break; + case 0x80: + analog.mq = SR_MQ_TEMPERATURE; + analog.unit = SR_UNIT_FAHRENHEIT; + break; + default: + sr_dbg("Unknown/invalid measurement mode %.2x.", data[3]); + } + if (analog.mq == -1) + return; + + if (is_auto) + analog.mqflags |= SR_MQFLAG_AUTORANGE; + if (is_hold) + analog.mqflags |= SR_MQFLAG_HOLD; + if (is_max) + analog.mqflags |= SR_MQFLAG_MAX; + if (is_min) + analog.mqflags |= SR_MQFLAG_MIN; + if (is_relative) + analog.mqflags |= SR_MQFLAG_RELATIVE; + + analog.num_samples = 1; + analog.data = &fvalue; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(devc->cb_data, &packet); + + devc->num_samples++; +} + +SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf) { - const struct sr_dev_inst *sdi; struct dev_context *devc; + GString *dbg; + int i; + unsigned char data[DMM_DATA_SIZE]; + unsigned char obfuscation[DMM_DATA_SIZE] = "jodenxunickxia"; + unsigned char shuffle[DMM_DATA_SIZE] = { + 6, 13, 5, 11, 2, 7, 9, 8, 3, 10, 12, 0, 4, 1 + }; - if (!(sdi = cb_data)) - return TRUE; + devc = sdi->priv; - if (!(devc = sdi->priv)) - return TRUE; + for (i = 0; i < DMM_DATA_SIZE && buf[i] == 0; i++); + if (i == DMM_DATA_SIZE) { + /* This DMM outputs all zeroes from time to time, just ignore it. */ + sr_dbg("Received all zeroes."); + return SR_OK; + } - if (revents == G_IO_IN) { - /* TODO */ + /* Deobfuscate and reorder data. */ + for (i = 0; i < DMM_DATA_SIZE; i++) + data[shuffle[i]] = (buf[i] - obfuscation[i]) & 0xff; + + if (sr_log_loglevel_get() >= SR_LOG_SPEW) { + dbg = g_string_sized_new(128); + g_string_printf(dbg, "Deobfuscated."); + for (i = 0; i < DMM_DATA_SIZE; i++) + g_string_append_printf(dbg, " %.2x", data[i]); + sr_spew("%s", dbg->str); + g_string_free(dbg, TRUE); } - return TRUE; + decode_buf(devc, data); + + return SR_OK; } + diff --git a/hardware/victor-dmm/protocol.h b/hardware/victor-dmm/protocol.h index cab77774..e4ed8442 100644 --- a/hardware/victor-dmm/protocol.h +++ b/hardware/victor-dmm/protocol.h @@ -33,6 +33,8 @@ #define sr_warn(s, args...) sr_warn(DRIVER_LOG_DOMAIN s, ## args) #define sr_err(s, args...) sr_err(DRIVER_LOG_DOMAIN s, ## args) +#define DMM_DATA_SIZE 14 + /** Private, per-device-instance driver context. */ struct dev_context { /** The current sampling limit (in number of samples). */ @@ -53,6 +55,6 @@ struct dev_context { int usbfd[10]; }; -SR_PRIV int victor_dmm_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int victor_dmm_receive_data(struct sr_dev_inst *sdi, unsigned char *buf); #endif