scpi-dmm: add support to get/set range on Agilent protocol using meters

Add support to get and set the auto/manual range on Agilent protocol
speaking devices (34405A, 34465A). The range values are mere numbers
without a unit or MQ associated with them. Support to list available
ranges is prepared but not used (not needed on these meters). Common
logic could open code the lists of ranges from the model description
if desired in a future implementation.
This commit is contained in:
Gerhard Sittig 2021-05-20 22:00:47 +02:00
parent 7fcdc35e17
commit a0418c20d8
3 changed files with 165 additions and 2 deletions

View File

@ -39,6 +39,15 @@ static const uint32_t devopts_generic[] = {
SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
}; };
static const uint32_t devopts_generic_range[] = {
SR_CONF_CONTINUOUS,
SR_CONF_CONN | SR_CONF_GET,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_RANGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
static const struct scpi_command cmdset_agilent[] = { static const struct scpi_command cmdset_agilent[] = {
{ DMM_CMD_SETUP_REMOTE, "\n", }, { DMM_CMD_SETUP_REMOTE, "\n", },
{ DMM_CMD_SETUP_FUNC, "CONF:%s", }, { DMM_CMD_SETUP_FUNC, "CONF:%s", },
@ -47,6 +56,10 @@ static const struct scpi_command cmdset_agilent[] = {
{ DMM_CMD_STOP_ACQ, "ABORT", }, { DMM_CMD_STOP_ACQ, "ABORT", },
{ DMM_CMD_QUERY_VALUE, "READ?", }, { DMM_CMD_QUERY_VALUE, "READ?", },
{ DMM_CMD_QUERY_PREC, "CONF?", }, { DMM_CMD_QUERY_PREC, "CONF?", },
{ DMM_CMD_QUERY_RANGE_AUTO, "%s:RANGE:AUTO?", },
{ DMM_CMD_QUERY_RANGE, "%s:RANGE?", },
{ DMM_CMD_SETUP_RANGE_AUTO, "%s:RANGE:AUTO ON", },
{ DMM_CMD_SETUP_RANGE, "%s:RANGE %s", },
ALL_ZERO, ALL_ZERO,
}; };
@ -190,8 +203,9 @@ SR_PRIV const struct scpi_dmm_model models[] = {
"Agilent", "34405A", "Agilent", "34405A",
1, 5, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_34405a), 1, 5, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_34405a),
scpi_dmm_get_meas_agilent, scpi_dmm_get_meas_agilent,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic_range),
0, 0, FALSE, 0, 0, FALSE,
scpi_dmm_get_range_text, scpi_dmm_set_range_from_text, NULL,
}, },
{ {
"Agilent", "34410A", "Agilent", "34410A",
@ -199,6 +213,7 @@ SR_PRIV const struct scpi_dmm_model models[] = {
scpi_dmm_get_meas_agilent, scpi_dmm_get_meas_agilent,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
0, 0, FALSE, 0, 0, FALSE,
NULL, NULL, NULL,
}, },
{ {
"GW", "GDM8251A", "GW", "GDM8251A",
@ -206,6 +221,7 @@ SR_PRIV const struct scpi_dmm_model models[] = {
scpi_dmm_get_meas_gwinstek, scpi_dmm_get_meas_gwinstek,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
1000 * 2500, 0, FALSE, 1000 * 2500, 0, FALSE,
NULL, NULL, NULL,
}, },
{ {
"GW", "GDM8255A", "GW", "GDM8255A",
@ -213,6 +229,7 @@ SR_PRIV const struct scpi_dmm_model models[] = {
scpi_dmm_get_meas_gwinstek, scpi_dmm_get_meas_gwinstek,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
1000 * 2500, 0, FALSE, 1000 * 2500, 0, FALSE,
NULL, NULL, NULL,
}, },
{ {
"GWInstek", "GDM9060", "GWInstek", "GDM9060",
@ -220,6 +237,7 @@ SR_PRIV const struct scpi_dmm_model models[] = {
scpi_dmm_get_meas_agilent, scpi_dmm_get_meas_agilent,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
0, 0, FALSE, 0, 0, FALSE,
NULL, NULL, NULL,
}, },
{ {
"GWInstek", "GDM9061", "GWInstek", "GDM9061",
@ -227,6 +245,7 @@ SR_PRIV const struct scpi_dmm_model models[] = {
scpi_dmm_get_meas_agilent, scpi_dmm_get_meas_agilent,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
0, 0, FALSE, 0, 0, FALSE,
NULL, NULL, NULL,
}, },
{ {
"HP", "34401A", "HP", "34401A",
@ -235,13 +254,15 @@ SR_PRIV const struct scpi_dmm_model models[] = {
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
/* 34401A: typ. 1020ms for AC readings (default is 1000ms). */ /* 34401A: typ. 1020ms for AC readings (default is 1000ms). */
1000 * 1500, 0, FALSE, 1000 * 1500, 0, FALSE,
NULL, NULL, NULL,
}, },
{ {
"Keysight", "34465A", "Keysight", "34465A",
1, 6, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_34405a), 1, 6, cmdset_agilent, ARRAY_AND_SIZE(mqopts_agilent_34405a),
scpi_dmm_get_meas_agilent, scpi_dmm_get_meas_agilent,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic_range),
0, 0, FALSE, 0, 0, FALSE,
scpi_dmm_get_range_text, scpi_dmm_set_range_from_text, NULL,
}, },
{ {
"OWON", "XDM2041", "OWON", "XDM2041",
@ -249,6 +270,7 @@ SR_PRIV const struct scpi_dmm_model models[] = {
scpi_dmm_get_meas_gwinstek, scpi_dmm_get_meas_gwinstek,
ARRAY_AND_SIZE(devopts_generic), ARRAY_AND_SIZE(devopts_generic),
0, 1e9, TRUE, 0, 1e9, TRUE,
NULL, NULL, NULL,
}, },
}; };
@ -413,6 +435,7 @@ static int config_get(uint32_t key, GVariant **data,
enum sr_mqflag mqflag; enum sr_mqflag mqflag;
GVariant *arr[2]; GVariant *arr[2];
int ret; int ret;
const char *range;
(void)cg; (void)cg;
@ -435,6 +458,14 @@ static int config_get(uint32_t key, GVariant **data,
arr[1] = g_variant_new_uint64(mqflag); arr[1] = g_variant_new_uint64(mqflag);
*data = g_variant_new_tuple(arr, ARRAY_SIZE(arr)); *data = g_variant_new_tuple(arr, ARRAY_SIZE(arr));
return SR_OK; return SR_OK;
case SR_CONF_RANGE:
if (!devc || !devc->model->get_range_text)
return SR_ERR_NA;
range = devc->model->get_range_text(sdi);
if (!range || !*range)
return SR_ERR_NA;
*data = g_variant_new_string(range);
return SR_OK;
default: default:
return SR_ERR_NA; return SR_ERR_NA;
} }
@ -447,6 +478,7 @@ static int config_set(uint32_t key, GVariant *data,
enum sr_mq mq; enum sr_mq mq;
enum sr_mqflag mqflag; enum sr_mqflag mqflag;
GVariant *tuple_child; GVariant *tuple_child;
const char *range;
(void)cg; (void)cg;
@ -464,6 +496,11 @@ static int config_set(uint32_t key, GVariant *data,
mqflag = g_variant_get_uint64(tuple_child); mqflag = g_variant_get_uint64(tuple_child);
g_variant_unref(tuple_child); g_variant_unref(tuple_child);
return scpi_dmm_set_mq(sdi, mq, mqflag); return scpi_dmm_set_mq(sdi, mq, mqflag);
case SR_CONF_RANGE:
if (!devc || !devc->model->set_range_from_text)
return SR_ERR_NA;
range = g_variant_get_string(data, NULL);
return devc->model->set_range_from_text(sdi, range);
default: default:
return SR_ERR_NA; return SR_ERR_NA;
} }
@ -503,6 +540,11 @@ static int config_list(uint32_t key, GVariant **data,
} }
*data = g_variant_builder_end(&gvb); *data = g_variant_builder_end(&gvb);
return SR_OK; return SR_OK;
case SR_CONF_RANGE:
if (!devc || !devc->model->get_range_text_list)
return SR_ERR_NA;
*data = devc->model->get_range_text_list(sdi);
return SR_OK;
default: default:
(void)devc; (void)devc;
return SR_ERR_NA; return SR_ERR_NA;

View File

@ -154,6 +154,114 @@ SR_PRIV int scpi_dmm_set_mq(const struct sr_dev_inst *sdi,
return SR_OK; return SR_OK;
} }
SR_PRIV const char *scpi_dmm_get_range_text(const struct sr_dev_inst *sdi)
{
struct dev_context *devc;
int ret;
const struct mqopt_item *mqitem;
gboolean is_auto;
char *response, *pos;
double range;
int digits;
devc = sdi->priv;
ret = scpi_dmm_get_mq(sdi, NULL, NULL, NULL, &mqitem);
if (ret != SR_OK)
return NULL;
if (!mqitem || !mqitem->scpi_func_setup)
return NULL;
scpi_dmm_cmd_delay(sdi->conn);
ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL,
DMM_CMD_QUERY_RANGE_AUTO, mqitem->scpi_func_setup);
if (ret != SR_OK)
return NULL;
ret = sr_scpi_get_bool(sdi->conn, NULL, &is_auto);
if (ret != SR_OK)
return NULL;
if (is_auto)
return "auto";
/*
* Get the response into a text buffer. The range value may be
* followed by a precision value separated by comma. Common text
* to number conversion support code may assume that the input
* text spans to the end of the text, need not accept trailing
* text which is not part of a number.
*/
scpi_dmm_cmd_delay(sdi->conn);
ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL,
DMM_CMD_QUERY_RANGE, mqitem->scpi_func_setup);
if (ret != SR_OK)
return NULL;
response = NULL;
ret = sr_scpi_get_string(sdi->conn, NULL, &response);
if (ret != SR_OK) {
g_free(response);
return NULL;
}
pos = strchr(response, ',');
if (pos)
*pos = '\0';
ret = sr_atod_ascii_digits(response, &range, &digits);
g_free(response);
if (ret != SR_OK)
return NULL;
snprintf(devc->range_text, sizeof(devc->range_text), "%lf", range);
return devc->range_text;
}
SR_PRIV int scpi_dmm_set_range_from_text(const struct sr_dev_inst *sdi,
const char *range)
{
struct dev_context *devc;
int ret;
const struct mqopt_item *item;
gboolean is_auto;
devc = sdi->priv;
if (!range || !*range)
return SR_ERR_ARG;
ret = scpi_dmm_get_mq(sdi, NULL, NULL, NULL, &item);
if (ret != SR_OK)
return ret;
if (!item || !item->scpi_func_setup)
return SR_ERR_ARG;
is_auto = g_ascii_strcasecmp(range, "auto") == 0;
scpi_dmm_cmd_delay(sdi->conn);
ret = sr_scpi_cmd(sdi, devc->cmdset, 0, NULL,
is_auto ? DMM_CMD_SETUP_RANGE_AUTO : DMM_CMD_SETUP_RANGE,
item->scpi_func_setup, is_auto ? "" : range);
if (ret != SR_OK)
return ret;
return SR_OK;
}
SR_PRIV GVariant *scpi_dmm_get_range_text_list(const struct sr_dev_inst *sdi)
{
GVariantBuilder gvb;
GVariant *list;
(void)sdi;
g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
/* TODO
* Add more items _when_ the connected device supports a fixed
* or known set of ranges. The Agilent protocol is flexible and
* tolerant, set requests accept any value, and the device will
* use an upper limit which is at least the specified value.
* The values are communicated as mere numbers without units.
*/
list = g_variant_builder_end(&gvb);
return list;
}
SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch) SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch)
{ {
struct sr_scpi_dev_inst *scpi; struct sr_scpi_dev_inst *scpi;

View File

@ -41,6 +41,10 @@ enum scpi_dmm_cmdcode {
DMM_CMD_QUERY_VALUE, DMM_CMD_QUERY_VALUE,
DMM_CMD_QUERY_PREC, DMM_CMD_QUERY_PREC,
DMM_CMD_SETUP_LOCAL, DMM_CMD_SETUP_LOCAL,
DMM_CMD_QUERY_RANGE_AUTO,
DMM_CMD_QUERY_RANGE,
DMM_CMD_SETUP_RANGE_AUTO,
DMM_CMD_SETUP_RANGE,
}; };
struct mqopt_item { struct mqopt_item {
@ -66,6 +70,10 @@ struct scpi_dmm_model {
unsigned int read_timeout_us; /* If zero, use default from src/scpi/scpi.c. */ unsigned int read_timeout_us; /* If zero, use default from src/scpi/scpi.c. */
float infinity_limit; /* If zero, use default from protocol.c */ float infinity_limit; /* If zero, use default from protocol.c */
gboolean check_opc; gboolean check_opc;
const char *(*get_range_text)(const struct sr_dev_inst *sdi);
int (*set_range_from_text)(const struct sr_dev_inst *sdi,
const char *range);
GVariant *(*get_range_text_list)(const struct sr_dev_inst *sdi);
}; };
struct dev_context { struct dev_context {
@ -87,6 +95,7 @@ struct dev_context {
struct sr_analog_spec spec[SCPI_DMM_MAX_CHANNELS]; struct sr_analog_spec spec[SCPI_DMM_MAX_CHANNELS];
} run_acq_info; } run_acq_info;
gchar *precision; gchar *precision;
char range_text[32];
}; };
SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi); SR_PRIV void scpi_dmm_cmd_delay(struct sr_scpi_dev_inst *scpi);
@ -99,6 +108,10 @@ SR_PRIV int scpi_dmm_get_mq(const struct sr_dev_inst *sdi,
const struct mqopt_item **mqitem); const struct mqopt_item **mqitem);
SR_PRIV int scpi_dmm_set_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); enum sr_mq mq, enum sr_mqflag flag);
SR_PRIV const char *scpi_dmm_get_range_text(const struct sr_dev_inst *sdi);
SR_PRIV int scpi_dmm_set_range_from_text(const struct sr_dev_inst *sdi,
const char *range);
SR_PRIV GVariant *scpi_dmm_get_range_text_list(const struct sr_dev_inst *sdi);
SR_PRIV int scpi_dmm_get_meas_agilent(const struct sr_dev_inst *sdi, size_t ch); 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_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); SR_PRIV int scpi_dmm_receive_data(int fd, int revents, void *cb_data);