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