From: Janne Huttunen Date: Wed, 30 Jul 2014 07:28:58 +0000 (+0300) Subject: Implement Brymen BM25x series as a serial DMM. X-Git-Tag: libsigrok-0.4.0~1177 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=a24c3f4a899d61a6bfc837a1969dbacf42e2ea72;p=libsigrok.git Implement Brymen BM25x series as a serial DMM. The Brymen BM25x series supports the BC-20X that is an opto-isolated serial cable. The link seems to be unidirectional i.e. when activated the DMM periodically sends updates to the host while the host cannot control the DMM in any way. The protocol is documented in "6000-count-digital-multimeters-r1.pdf" that is available from the manufacturer. Every 15 byte packet consists of a bitmap where the bits correspond to segments or symbols on the LCD display i.e. the DMM essentially sends the contents of its screen to the host in every update. This driver then decodes the measured quantity, unit and its value from the bitmap. --- diff --git a/Makefile.am b/Makefile.am index 8ffc99a9..d961ad56 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,7 +99,8 @@ libsigrok_la_SOURCES += \ src/dmm/fs9922.c \ src/dmm/m2110.c \ src/dmm/metex14.c \ - src/dmm/rs9lcd.c + src/dmm/rs9lcd.c \ + src/dmm/bm25x.c # Hardware drivers if HW_AGILENT_DMM diff --git a/src/dmm/bm25x.c b/src/dmm/bm25x.c new file mode 100644 index 00000000..67c2d04f --- /dev/null +++ b/src/dmm/bm25x.c @@ -0,0 +1,214 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2014 Janne Huttunen + * + * 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 . + */ + +/** + * @file + * + * Brymen BM25x serial protocol parser. + */ + +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "brymen-bm25x" + +#define MAX_DIGITS 4 + +SR_PRIV gboolean sr_brymen_bm25x_packet_valid(const uint8_t *buf) +{ + int i; + + if (buf[0] != 2) + return FALSE; + + for (i = 1; i < BRYMEN_BM25X_PACKET_SIZE; i++) + if ((buf[i] >> 4) != i) + return FALSE; + + return TRUE; +} + +static int decode_digit(int num, const uint8_t *buf) +{ + int val; + + val = (buf[3 + 2 * num] & 0xe) | ((buf[4 + 2 * num] << 4) & 0xf0); + + switch (val) { + case 0xbe: return 0; + case 0xa0: return 1; + case 0xda: return 2; + case 0xf8: return 3; + case 0xe4: return 4; + case 0x7c: return 5; + case 0x7e: return 6; + case 0xa8: return 7; + case 0xfe: return 8; + case 0xfc: return 9; + case 0x00: return ' '; + case 0x40: return '-'; + case 0x16: return 'L'; + case 0x1e: return 'C'; + case 0x4e: return 'F'; + case 0x5e: return 'E'; + case 0x62: return 'n'; + case 0x42: return 'r'; + default: + sr_dbg("Unknown digit: 0x%02x.", val); + return -1; + } +} + +static int decode_point(const uint8_t *buf) +{ + int i, p = 0; + + for (i = 1; i < MAX_DIGITS; i++) { + if ((buf[11 - 2 * i] & 1) == 0) + continue; + if (p != 0) { + sr_spew("Multiple decimal points found!"); + return -1; + } + p = i; + } + + return p; +} + +static float scale_value(float val, int point, int digits) +{ + int pos; + + pos = point ? point + digits - MAX_DIGITS : 0; + + switch (pos) { + case 0: return val; + case 1: return val * 1e-1; + case 2: return val * 1e-2; + case 3: return val * 1e-3; + } + + sr_dbg("Invalid decimal point %d (%d digits).", point, digits); + + return NAN; +} + +static float decode_prefix(const uint8_t *buf) +{ + if (buf[11] & 2) return 1e+6; + if (buf[11] & 1) return 1e+3; + if (buf[13] & 1) return 1e-3; + if (buf[13] & 2) return 1e-6; + if (buf[12] & 1) return 1e-9; + + return 1.0f; +} + +static float decode_value(const uint8_t *buf) +{ + float val = 0.0f; + int i, digit; + + for (i = 0; i < MAX_DIGITS; i++) { + digit = decode_digit(i, buf); + if (i == 3 && (digit == 'C' || digit == 'F')) + break; + if (digit < 0 || digit > 9) + goto special; + val = 10.0 * val + digit; + } + + return scale_value(val, decode_point(buf), i); + +special: + if (decode_digit(1, buf) == 0 && decode_digit(2, buf) == 'L') + return INFINITY; + + return NAN; +} + +SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval, + struct sr_datafeed_analog *analog, void *info) +{ + float val; + + (void)info; + + analog->mq = SR_MQ_GAIN; + analog->unit = SR_UNIT_UNITLESS; + analog->mqflags = 0; + + if (buf[1] & 8) + analog->mqflags |= SR_MQFLAG_AUTORANGE; + if (buf[1] & 4) + analog->mqflags |= SR_MQFLAG_DC; + if (buf[1] & 2) + analog->mqflags |= SR_MQFLAG_AC; + if (buf[1] & 1) + analog->mqflags |= SR_MQFLAG_RELATIVE; + if (buf[11] & 8) + analog->mqflags |= SR_MQFLAG_HOLD; + if (buf[13] & 8) + analog->mqflags |= SR_MQFLAG_MAX; + if (buf[14] & 8) + analog->mqflags |= SR_MQFLAG_MIN; + + if (buf[14] & 4) { + analog->mq = SR_MQ_VOLTAGE; + analog->unit = SR_UNIT_VOLT; + if ((analog->mqflags & (SR_MQFLAG_DC | SR_MQFLAG_AC)) == 0) + analog->mqflags |= SR_MQFLAG_DIODE; + } + if (buf[14] & 2) { + analog->mq = SR_MQ_CURRENT; + analog->unit = SR_UNIT_AMPERE; + } + if (buf[12] & 4) { + analog->mq = SR_MQ_RESISTANCE; + analog->unit = SR_UNIT_OHM; + } + if (buf[13] & 4) { + analog->mq = SR_MQ_CAPACITANCE; + analog->unit = SR_UNIT_FARAD; + } + if (buf[12] & 2) { + analog->mq = SR_MQ_FREQUENCY; + analog->unit = SR_UNIT_HERTZ; + } + + if (decode_digit(3, buf) == 'C') { + analog->mq = SR_MQ_TEMPERATURE; + analog->unit = SR_UNIT_CELSIUS; + } + if (decode_digit(3, buf) == 'F') { + analog->mq = SR_MQ_TEMPERATURE; + analog->unit = SR_UNIT_FAHRENHEIT; + } + + val = decode_value(buf) * decode_prefix(buf); + + if (buf[3] & 1) + val = -val; + + *floatval = val; + + return SR_OK; +} diff --git a/src/drivers.c b/src/drivers.c index f93214d3..8aa9acf5 100644 --- a/src/drivers.c +++ b/src/drivers.c @@ -149,6 +149,7 @@ extern SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info; extern SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info; extern SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info; extern SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info; +extern SR_PRIV struct sr_dev_driver brymen_bm25x_ser_driver_info; #endif #ifdef HAVE_HW_SYSCLK_LWLA extern SR_PRIV struct sr_dev_driver sysclk_lwla_driver_info; @@ -316,6 +317,7 @@ SR_PRIV struct sr_dev_driver *drivers_list[] = { &iso_tech_idm103n_driver_info, &tenma_72_7745_ser_driver_info, &tenma_72_7750_ser_driver_info, + &brymen_bm25x_ser_driver_info, #endif #ifdef HAVE_HW_SYSCLK_LWLA &sysclk_lwla_driver_info, diff --git a/src/hardware/serial-dmm/api.c b/src/hardware/serial-dmm/api.c index b96c7a0d..d56a9d7a 100644 --- a/src/hardware/serial-dmm/api.c +++ b/src/hardware/serial-dmm/api.c @@ -74,6 +74,7 @@ SR_PRIV struct sr_dev_driver uni_t_ut61e_ser_driver_info; SR_PRIV struct sr_dev_driver iso_tech_idm103n_driver_info; SR_PRIV struct sr_dev_driver tenma_72_7745_ser_driver_info; SR_PRIV struct sr_dev_driver tenma_72_7750_ser_driver_info; +SR_PRIV struct sr_dev_driver brymen_bm25x_ser_driver_info; SR_PRIV struct dmm_info dmms[] = { { @@ -318,6 +319,13 @@ SR_PRIV struct dmm_info dmms[] = { NULL, &tenma_72_7750_ser_driver_info, receive_data_TENMA_72_7750_SER, }, + { + "Brymen", "BM25x (BC20X cable)", "9600/8n1/rts=1/dtr=1", + 9600, BRYMEN_BM25X_PACKET_SIZE, 0, 0, NULL, + sr_brymen_bm25x_packet_valid, sr_brymen_bm25x_parse, + NULL, + &brymen_bm25x_ser_driver_info, receive_data_BRYMEN_BM25X_SER, + }, }; static int dev_clear(int dmm) @@ -630,3 +638,4 @@ DRV(uni_t_ut61e_ser, UNI_T_UT61E_SER, "uni-t-ut61e-ser", "UNI-T UT61E (UT-D02 ca DRV(iso_tech_idm103n, ISO_TECH_IDM103N, "iso-tech-idm103n", "ISO-TECH IDM103N") DRV(tenma_72_7745_ser, TENMA_72_7745_SER, "tenma-72-7745-ser", "Tenma 72-7745 (UT-D02 cable)") DRV(tenma_72_7750_ser, TENMA_72_7750_SER, "tenma-72-7750-ser", "Tenma 72-7750 (UT-D02 cable)") +DRV(brymen_bm25x_ser, BRYMEN_BM25X_SER, "brymen-bm25x-ser", "Brymen BM25x (BC20X cable)") diff --git a/src/hardware/serial-dmm/protocol.c b/src/hardware/serial-dmm/protocol.c index 0b2472fb..c8127939 100644 --- a/src/hardware/serial-dmm/protocol.c +++ b/src/hardware/serial-dmm/protocol.c @@ -223,3 +223,4 @@ RECEIVE_DATA(UNI_T_UT61E_SER, es519xx) RECEIVE_DATA(ISO_TECH_IDM103N, es519xx) RECEIVE_DATA(TENMA_72_7745_SER, fs9721) RECEIVE_DATA(TENMA_72_7750_SER, es519xx) +RECEIVE_DATA(BRYMEN_BM25X_SER, bm25x) diff --git a/src/hardware/serial-dmm/protocol.h b/src/hardware/serial-dmm/protocol.h index fb5a2a33..24689467 100644 --- a/src/hardware/serial-dmm/protocol.h +++ b/src/hardware/serial-dmm/protocol.h @@ -56,6 +56,7 @@ enum { ISO_TECH_IDM103N, TENMA_72_7745_SER, TENMA_72_7750_SER, + BRYMEN_BM25X_SER, }; struct dmm_info { @@ -155,5 +156,6 @@ SR_PRIV int receive_data_UNI_T_UT61E_SER(int fd, int revents, void *cb_data); SR_PRIV int receive_data_ISO_TECH_IDM103N(int fd, int revents, void *cb_data); SR_PRIV int receive_data_TENMA_72_7745_SER(int fd, int revents, void *cb_data); SR_PRIV int receive_data_TENMA_72_7750_SER(int fd, int revents, void *cb_data); +SR_PRIV int receive_data_BRYMEN_BM25X_SER(int fd, int revents, void *cb_data); #endif diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h index 642277eb..a18ccf6a 100644 --- a/src/libsigrok-internal.h +++ b/src/libsigrok-internal.h @@ -740,4 +740,15 @@ SR_PRIV gboolean sr_rs9lcd_packet_valid(const uint8_t *buf); SR_PRIV int sr_rs9lcd_parse(const uint8_t *buf, float *floatval, struct sr_datafeed_analog *analog, void *info); +/*--- hardware/common/dmm/bm25x.c -----------------------------------------*/ + +#define BRYMEN_BM25X_PACKET_SIZE 15 + +/* Dummy info struct. The parser does not use it. */ +struct bm25x_info { int dummy; }; + +SR_PRIV gboolean sr_brymen_bm25x_packet_valid(const uint8_t *buf); +SR_PRIV int sr_brymen_bm25x_parse(const uint8_t *buf, float *floatval, + struct sr_datafeed_analog *analog, void *info); + #endif