From: Timo Kokkonen Date: Tue, 2 Jun 2020 06:29:47 +0000 (-0700) Subject: scpi-dmm: Add support for GW-Instek 8200A series bench multimeters. X-Git-Url: http://sigrok.org/gitweb/?a=commitdiff_plain;h=d0b602f00f68e10c52eb5204017e44acf58037f7;p=libsigrok.git scpi-dmm: Add support for GW-Instek 8200A series bench multimeters. - Add support for GDM-8251A and GDM-8255A DMMs. --- diff --git a/src/hardware/scpi-dmm/api.c b/src/hardware/scpi-dmm/api.c index 9cf4c339..3098a95f 100644 --- a/src/hardware/scpi-dmm/api.c +++ b/src/hardware/scpi-dmm/api.c @@ -80,6 +80,17 @@ static const struct scpi_command cmdset_hp[] = { ALL_ZERO, }; +static const struct scpi_command cmdset_gwinstek[] = { + { DMM_CMD_SETUP_REMOTE, "SYST:REM", }, + { DMM_CMD_SETUP_FUNC, "CONF:%s", }, + { DMM_CMD_QUERY_FUNC, "CONF:STAT:FUNC?", }, + { DMM_CMD_START_ACQ, "*CLS;SYST:REM", }, + { DMM_CMD_STOP_ACQ, "SYST:LOC", }, + { DMM_CMD_QUERY_VALUE, "VAL1?", }, + { DMM_CMD_QUERY_PREC, "SENS:DET:RATE?", }, + ALL_ZERO, +}; + static const struct mqopt_item mqopts_agilent_34405a[] = { { SR_MQ_VOLTAGE, SR_MQFLAG_DC, "VOLT:DC", "VOLT ", NO_DFLT_PREC, }, { SR_MQ_VOLTAGE, SR_MQFLAG_AC, "VOLT:AC", "VOLT:AC ", NO_DFLT_PREC, }, @@ -106,6 +117,23 @@ static const struct mqopt_item mqopts_agilent_34401a[] = { { SR_MQ_TIME, 0, "PER", "PER ", NO_DFLT_PREC, }, }; +static const struct mqopt_item mqopts_gwinstek_gdm8200a[] = { + { SR_MQ_VOLTAGE, SR_MQFLAG_DC, "VOLT:DC", "01", NO_DFLT_PREC, }, + { SR_MQ_VOLTAGE, SR_MQFLAG_AC, "VOLT:AC", "02", NO_DFLT_PREC, }, + { SR_MQ_CURRENT, SR_MQFLAG_DC, "CURR:DC", "03", NO_DFLT_PREC, }, + { SR_MQ_CURRENT, SR_MQFLAG_AC, "CURR:AC", "04", NO_DFLT_PREC, }, + { SR_MQ_CURRENT, SR_MQFLAG_DC, "CURR:DC", "05", NO_DFLT_PREC, }, /* mA */ + { SR_MQ_CURRENT, SR_MQFLAG_AC, "CURR:AC", "06", NO_DFLT_PREC, }, /* mA */ + { SR_MQ_RESISTANCE, 0, "RES", "07", NO_DFLT_PREC, }, + { SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE, "FRES", "16", NO_DFLT_PREC, }, + { SR_MQ_CONTINUITY, 0, "CONT", "13", -1, }, + { SR_MQ_VOLTAGE, SR_MQFLAG_DC | SR_MQFLAG_DIODE, "DIOD", "17", -4, }, + { SR_MQ_TEMPERATURE, 0, "TEMP", "09", NO_DFLT_PREC, }, /* Celsius */ + { SR_MQ_TEMPERATURE, 0, "TEMP", "15", NO_DFLT_PREC, }, /* Fahrenheit */ + { SR_MQ_FREQUENCY, 0, "FREQ", "08", NO_DFLT_PREC, }, + { SR_MQ_TIME, 0, "PER", "14", NO_DFLT_PREC, }, +}; + SR_PRIV const struct scpi_dmm_model models[] = { { "Agilent", "34405A", @@ -136,6 +164,20 @@ SR_PRIV const struct scpi_dmm_model models[] = { /* 34401A: typ. 1020ms for AC readings (default is 1000ms). */ 1000 * 1500, }, + { + "GW", "GDM8251A", + 1, 6, cmdset_gwinstek, ARRAY_AND_SIZE(mqopts_gwinstek_gdm8200a), + scpi_dmm_get_meas_gwinstek, + ARRAY_AND_SIZE(devopts_generic), + 1000 * 2500, + }, + { + "GW", "GDM8255A", + 1, 6, cmdset_gwinstek, ARRAY_AND_SIZE(mqopts_gwinstek_gdm8200a), + scpi_dmm_get_meas_gwinstek, + ARRAY_AND_SIZE(devopts_generic), + 1000 * 2500, + }, }; static const struct scpi_dmm_model *is_compatible(const char *vendor, const char *model) @@ -197,6 +239,7 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) devc->num_channels = model->num_channels; devc->cmdset = model->cmdset; devc->model = model; + devc->precision = NULL; for (i = 0; i < devc->num_channels; i++) { channel_name = g_strdup_printf("P%zu", i + 1); @@ -347,6 +390,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) int ret; const struct mqopt_item *item; const char *command; + char *response; scpi = sdi->conn; devc = sdi->priv; @@ -356,6 +400,26 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) if (ret != SR_OK) return ret; + /* + * Query for current precision if DMM supports the command + */ + command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_PREC); + if (command && *command) { + scpi_dmm_cmd_delay(scpi); + ret = sr_scpi_get_string(scpi, command, &response); + if (ret == SR_OK) { + g_strstrip(response); + if (devc->precision) + g_free(devc->precision); + devc->precision=g_strdup(response); + g_free(response); + sr_dbg("%s: Precision: '%s'", __func__, devc->precision); + } else { + sr_info("%s: Precision query ('%s') failed: %d", + __func__, command, ret); + } + } + command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_START_ACQ); if (command && *command) { scpi_dmm_cmd_delay(scpi); @@ -395,6 +459,11 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi) std_session_send_df_end(sdi); + if (devc->precision) { + g_free(devc->precision); + devc->precision = NULL; + } + return SR_OK; } diff --git a/src/hardware/scpi-dmm/protocol.c b/src/hardware/scpi-dmm/protocol.c index d0cafd82..e63a4d12 100644 --- a/src/hardware/scpi-dmm/protocol.c +++ b/src/hardware/scpi-dmm/protocol.c @@ -398,6 +398,215 @@ SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) return SR_OK; } +SR_PRIV int scpi_dmm_get_meas_gwinstek(const struct sr_dev_inst *sdi, size_t ch) +{ + struct sr_scpi_dev_inst *scpi; + struct dev_context *devc; + struct scpi_dmm_acq_info *info; + struct sr_datafeed_analog *analog; + int ret; + enum sr_mq mq; + enum sr_mqflag mqflag; + char *mode_response; + const char *p; + const struct mqopt_item *item; + const char *command; + char *response; + gboolean use_double; + int sig_digits, val_exp; + int digits; + enum sr_unit unit; + int mmode; + + scpi = sdi->conn; + devc = sdi->priv; + info = &devc->run_acq_info; + analog = &info->analog[ch]; + + /* + * Get the meter's current mode, keep the response around. + * Skip the measurement if the mode is uncertain. + */ + ret = scpi_dmm_get_mq(sdi, &mq, &mqflag, &mode_response, &item); + if (ret != SR_OK) { + g_free(mode_response); + return ret; + } + if (!mode_response) + return SR_ERR; + if (!mq) { + g_free(mode_response); + return +1; + } + mmode = atoi(mode_response); + g_free(mode_response); + + /* + * Get the current reading from the meter. + */ + scpi_dmm_cmd_delay(scpi); + command = sr_scpi_cmd_get(devc->cmdset, DMM_CMD_QUERY_VALUE); + if (!command || !*command) + return SR_ERR_NA; + scpi_dmm_cmd_delay(scpi); + ret = sr_scpi_get_string(scpi, command, &response); + if (ret != SR_OK) + return ret; + g_strstrip(response); + use_double = devc->model->digits > 6; + ret = sr_atod_ascii(response, &info->d_value); + if (ret != SR_OK) { + g_free(response); + return ret; + } + if (!response) + return SR_ERR; + if (info->d_value > +9e37) { + info->d_value = +INFINITY; + } else if (info->d_value < -9e37) { + info->d_value = -INFINITY; + } else { + p = response; + while (p && *p && g_ascii_isspace(*p)) + p++; + if (p && *p && (*p == '-' || *p == '+')) + p++; + sig_digits = 0; + while (p && *p && g_ascii_isdigit(*p)) { + sig_digits++; + p++; + } + if (p && *p && *p == '.') + p++; + while (p && *p && g_ascii_isdigit(*p)) + p++; + ret = SR_OK; + if (!p || !*p) + val_exp = 0; + else if (*p != 'e' && *p != 'E') + ret = SR_ERR_DATA; + else + ret = sr_atoi(++p, &val_exp); + } + g_free(response); + if (ret != SR_OK) + return ret; + + /* + * Make sure we report "INFINITY" when meter displays "0L" + */ + switch (mmode) { + case 7: + case 16: + /* in resitance modes 0L reads as 1.20000E8 or 1.99999E8 */ + if (!strncmp(devc->model->model,"GDM8255A",8)) { + if (info->d_value >= 1.99999e8) + info->d_value = +INFINITY; + } else { + if (info->d_value >= 1.2e8) + info->d_value = +INFINITY; + } + break; + case 13: + /* In continuity mode 0L reads as 1.20000E3 */ + if (info->d_value >= 1.2e3) + info->d_value = +INFINITY; + break; + case 17: + /* in diode mode 0L reads as 1.00000E0 */ + if (info->d_value == 1.0e0) + info->d_value = +INFINITY; + break; + } + + /* + * Calculate 'digits' based on the precision reading result + * done during start of acquisition. + * + * GW-Instek manual gives following info regarding resolution: + * + * Type Digit + * -------------------- ------------ + * Slow 5 1/2 + * Medium 4 1/2 + * Fast 3 1/2 + * + */ + + digits = devc->model->digits; + if (devc->precision && *devc->precision) { + if (!strncmp(devc->precision, "Slow", 4)) + digits = 6; + else if (!strncmp(devc->precision, "Mid", 3)) + digits = 5; + else if (!strncmp(devc->precision, "Fast", 4)) + digits = 4; + else + sr_info("%s: Unknown precision: '%s'", + __func__, devc->precision); + } + + /* + * Fill in the 'analog' description: value, encoding, meaning. + * Callers will fill in the sample count, and channel name, + * and will send out the packet. + */ + if (use_double) { + analog->data = &info->d_value; + analog->encoding->unitsize = sizeof(info->d_value); + } else { + info->f_value = info->d_value; + analog->data = &info->f_value; + analog->encoding->unitsize = sizeof(info->f_value); + } + analog->encoding->is_float = TRUE; +#ifdef WORDS_BIGENDIAN + analog->encoding->is_bigendian = TRUE; +#else + analog->encoding->is_bigendian = FALSE; +#endif + analog->encoding->digits = digits; + analog->meaning->mq = mq; + analog->meaning->mqflags = mqflag; + switch (mq) { + case SR_MQ_VOLTAGE: + unit = SR_UNIT_VOLT; + break; + case SR_MQ_CURRENT: + unit = SR_UNIT_AMPERE; + break; + case SR_MQ_RESISTANCE: + case SR_MQ_CONTINUITY: + unit = SR_UNIT_OHM; + break; + case SR_MQ_CAPACITANCE: + unit = SR_UNIT_FARAD; + break; + case SR_MQ_TEMPERATURE: + switch (mmode) { + case 15: + unit = SR_UNIT_FAHRENHEIT; + break; + case 9: + default: + unit = SR_UNIT_CELSIUS; + } + break; + case SR_MQ_FREQUENCY: + unit = SR_UNIT_HERTZ; + break; + case SR_MQ_TIME: + unit = SR_UNIT_SECOND; + break; + default: + return SR_ERR_NA; + } + analog->meaning->unit = unit; + analog->spec->spec_digits = digits; + + return SR_OK; +} + /* Strictly speaking this is a timer controlled poll routine. */ SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data) { diff --git a/src/hardware/scpi-dmm/protocol.h b/src/hardware/scpi-dmm/protocol.h index 0206e77e..9a6b7012 100644 --- a/src/hardware/scpi-dmm/protocol.h +++ b/src/hardware/scpi-dmm/protocol.h @@ -83,6 +83,7 @@ struct dev_context { struct sr_analog_meaning meaning[SCPI_DMM_MAX_CHANNELS]; struct sr_analog_spec spec[SCPI_DMM_MAX_CHANNELS]; } run_acq_info; + gchar *precision; }; SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi); @@ -96,6 +97,7 @@ SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi, SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq, enum sr_mqflag flag); SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch); +SR_PRIV int scpi_dmm_get_meas_gwinstek(const struct sr_dev_inst *sdi, size_t ch); SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data); #endif