hp-3457a: Implement support for rear terminals and plug-in cards

This commit is contained in:
Alexandru Gagniuc 2016-04-03 18:45:25 -07:00 committed by Uwe Hermann
parent 2c04f943ef
commit 9a093be9f5
3 changed files with 231 additions and 5 deletions

View File

@ -43,13 +43,19 @@ static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
{
struct sr_channel *channel;
struct sr_channel_group *front;
struct channel_context *chanc;
chanc = g_malloc(sizeof(*chanc));
chanc->location = CONN_FRONT;
channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG,
TRUE, "Front");
channel->priv = chanc;
front = g_malloc0(sizeof(*front));
front->name = g_strdup("Front");
front->channels = g_slist_append(front->channels, channel);
sdi->channel_groups = g_slist_append(sdi->channel_groups, front);
return chan_idx;
@ -58,13 +64,40 @@ static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
static int create_rear_channels(struct sr_dev_inst *sdi, int chan_idx,
const struct rear_card_info *card)
{
(void) sdi;
unsigned int i;
struct sr_channel *channel;
struct sr_channel_group *group;
struct channel_context *chanc;
char name[16];
/* When card is NULL, we couldn't identify the type of card. */
if (!card)
return chan_idx;
/* TODO: Create channel descriptor for plug-in cards here. */
group = g_malloc0(sizeof(*group));
group->priv = NULL;
group->name = g_strdup(card->cg_name);
sdi->channel_groups = g_slist_append(sdi->channel_groups, group);
for (i = 0; i < card->num_channels; i++) {
chanc = g_malloc(sizeof(*chanc));
chanc->location = CONN_REAR;
if (card->type == REAR_TERMINALS) {
chanc->index = -1;
g_snprintf(name, sizeof(name), "%s", card->cg_name);
} else {
chanc->index = i;
g_snprintf(name, sizeof(name), "%s%u", card->cg_name, i);
}
channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG,
FALSE, name);
channel->priv = chanc;
group->channels = g_slist_append(group->channels, channel);
}
return chan_idx;
}
@ -199,6 +232,8 @@ static int dev_close(struct sr_dev_inst *sdi)
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED;
/* Disable scan-advance (preserve relay life). */
sr_scpi_send(scpi, "SADV HOLD");
/* Switch back to auto-triggering. */
sr_scpi_send(scpi, "TRIG AUTO");
@ -313,6 +348,21 @@ static int config_list(uint32_t key, GVariant **data,
return ret;
}
static void create_channel_index_list(GSList *channels, GArray **arr)
{
struct sr_channel *channel;
struct channel_context *chanc;
GSList *list_elem;
*arr = g_array_new(FALSE, FALSE, sizeof(unsigned int));
for (list_elem = channels; list_elem; list_elem = list_elem->next) {
channel = list_elem->data;
chanc = channel->priv;
g_array_append_val(*arr, chanc->index);
}
}
/*
* TRIG SGL
* Trigger the first measurement, then hold. We can't let the instrument
@ -321,12 +371,21 @@ static int config_list(uint32_t key, GVariant **data,
* reading for sample N, but a new measurement is made and when we read the
* HIRES register, it contains data for sample N+1. This would produce
* wrong readings.
* SADV AUTO
* Activate the scan-advance feature. This automatically connects the next
* channel in the scan list to the A/D converter. This way, we do not need to
* occupy the HP-IB bus to send channel select commands.
*/
static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
{
int ret;
gboolean front_selected, rear_selected;
struct sr_scpi_dev_inst *scpi;
struct sr_channel *channel;
struct dev_context *devc;
struct channel_context *chanc;
GArray *ch_list;
GSList *channels;
(void)cb_data;
@ -343,6 +402,44 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
std_session_send_df_header(sdi, LOG_PREFIX);
front_selected = rear_selected = FALSE;
devc->active_channels = NULL;
for (channels = sdi->channels; channels; channels = channels->next) {
channel = channels->data;
if (!channel->enabled)
continue;
chanc = channel->priv;
if (chanc->location == CONN_FRONT)
front_selected = TRUE;
if (chanc->location == CONN_REAR)
rear_selected = TRUE;
devc->active_channels = g_slist_append(devc->active_channels, channel);
}
if (front_selected && rear_selected) {
sr_err("Can not use front and rear channels at the same time!");
g_slist_free(devc->active_channels);
return SR_ERR_ARG;
}
devc->current_channel = devc->active_channels->data;
devc->num_active_channels = g_slist_length(devc->active_channels);
hp_3457a_select_input(sdi, front_selected ? CONN_FRONT : CONN_REAR);
/* For plug-in cards, use the scan-advance features to scan channels. */
if (rear_selected && (devc->rear_card->card_id != REAR_TERMINALS)) {
create_channel_index_list(devc->active_channels, &ch_list);
hp_3457a_send_scan_list(sdi, (void *)ch_list->data, ch_list->len);
sr_scpi_send(scpi, "SADV AUTO");
g_array_free(ch_list, TRUE);
}
/* Start first measurement. */
sr_scpi_send(scpi, "TRIG SGL");
devc->acq_state = ACQ_TRIGGERED_MEASUREMENT;
@ -353,11 +450,16 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
{
struct dev_context *devc;
(void)cb_data;
devc = sdi->priv;
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED;
g_slist_free(devc->active_channels);
return SR_OK;
}

View File

@ -50,16 +50,19 @@ static const struct rear_card_info rear_card_parameters[] = {
.card_id = 0,
.name = "Rear terminals",
.cg_name = "rear",
.num_channels = 1,
}, {
.type = HP_44491A,
.card_id = 44491,
.name = "44491A Armature Relay Multiplexer",
.cg_name = "44491a",
.num_channels = 14,
}, {
.type = HP_44492A,
.card_id = 44492,
.name = "44492A Reed Relay Multiplexer",
.cg_name = "44492a",
.num_channels = 10,
}
};
@ -109,6 +112,10 @@ SR_PRIV int hp_3457a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
struct sr_scpi_dev_inst *scpi = sdi->conn;
struct dev_context *devc = sdi->priv;
/* No need to send command if we're not changing measurement type. */
if (devc->measurement_mq == mq)
return SR_OK;
for (i = 0; i < ARRAY_SIZE(sr_mq_to_cmd_map); i++) {
if (sr_mq_to_cmd_map[i].mq != mq)
continue;
@ -176,12 +183,59 @@ SR_PRIV int hp_3457a_set_nplc(const struct sr_dev_inst *sdi, float nplc)
return ret;
}
SR_PRIV int hp_3457a_select_input(const struct sr_dev_inst *sdi,
enum channel_conn loc)
{
int ret;
struct sr_scpi_dev_inst *scpi = sdi->conn;
struct dev_context *devc = sdi->priv;
if (devc->input_loc == loc)
return SR_OK;
ret = sr_scpi_send(scpi, "TERM %s", (loc == CONN_FRONT) ? "FRONT": "REAR");
if (ret == SR_OK)
devc->input_loc = loc;
return ret;
}
SR_PRIV int hp_3457a_send_scan_list(const struct sr_dev_inst *sdi,
unsigned int *channels, size_t len)
{
size_t i;
char chan[16], list_str[64] = "";
for (i = 0; i < len; i++) {
g_snprintf(chan, sizeof(chan), ",%u", channels[i]);
g_strlcat(list_str, chan, sizeof(list_str));
}
return sr_scpi_send(sdi->conn, "SLIST %s", list_str);
}
/* HIRES register only contains valid data with 10 or more powerline cycles. */
static int is_highres_enabled(struct dev_context *devc)
{
return (devc->nplc >= 10.0);
}
static void activate_next_channel(struct dev_context *devc)
{
GSList *list_elem;
struct sr_channel *chan;
list_elem = g_slist_find(devc->active_channels, devc->current_channel);
if (list_elem)
list_elem = list_elem->next;
if (!list_elem)
list_elem = devc->active_channels;
chan = list_elem->data;
devc->current_channel = chan;
}
static void retrigger_measurement(struct sr_scpi_dev_inst *scpi,
struct dev_context *devc)
{
@ -203,6 +257,13 @@ static void request_range(struct sr_scpi_dev_inst *scpi,
devc->acq_state = ACQ_REQUESTED_RANGE;
}
static void request_current_channel(struct sr_scpi_dev_inst *scpi,
struct dev_context *devc)
{
sr_scpi_send(scpi, "CHAN?");
devc->acq_state = ACQ_REQUESTED_CHANNEL_SYNC;
}
/*
* Calculate the number of leading zeroes in the measurement.
*
@ -298,7 +359,7 @@ static void acq_send_measurement(struct sr_dev_inst *sdi)
sr_analog_init(&analog, &encoding, &meaning, &spec, num_digits);
encoding.unitsize = sizeof(float);
meaning.channels = sdi->channels;
meaning.channels = g_slist_append(NULL, devc->current_channel);
measurement_workaround = hires_measurement;
analog.num_samples = 1;
@ -309,13 +370,24 @@ static void acq_send_measurement(struct sr_dev_inst *sdi)
meaning.unit = devc->measurement_unit;
sr_session_send(sdi, &packet);
g_slist_free(meaning.channels);
}
/*
* The scan-advance channel sync -- call to request_current_channel() -- is not
* necessarily needed. It is done in case we have a communication error and the
* DMM advances the channel without having sent the reading. The DMM only
* advances the channel when it thinks it sent the reading over HP-IB. Thus, on
* most errors we can retrigger the measurement and still be in sync. This
* check is done to make sure we don't fall out of sync due to obscure errors.
*/
SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
{
int ret;
struct sr_scpi_dev_inst *scpi;
struct dev_context *devc;
struct channel_context *chanc;
struct sr_dev_inst *sdi = cb_data;
(void)fd;
@ -359,6 +431,15 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
}
devc->acq_state = ACQ_GOT_MEASUREMENT;
break;
case ACQ_REQUESTED_CHANNEL_SYNC:
ret = sr_scpi_get_double(scpi, NULL, &devc->last_channel_sync);
if (ret != SR_OK) {
sr_err("Cannot check channel synchronization.");
sdi->driver->dev_acquisition_stop(sdi, cb_data);
return FALSE;
}
devc->acq_state = ACQ_GOT_CHANNEL_SYNC;
break;
default:
return FALSE;
}
@ -368,6 +449,20 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
devc->num_samples++;
}
if (devc->acq_state == ACQ_GOT_CHANNEL_SYNC) {
chanc = devc->current_channel->priv;
if (chanc->index != devc->last_channel_sync) {
sr_err("Current channel and scan advance out of sync.");
sr_err("Expected channel %u, but device says %u",
chanc->index,
(unsigned int)devc->last_channel_sync);
sdi->driver->dev_acquisition_stop(sdi, cb_data);
return FALSE;
}
/* All is good. Back to business. */
retrigger_measurement(scpi, devc);
}
if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
sdi->driver->dev_acquisition_stop(sdi, cb_data);
return FALSE;
@ -375,8 +470,14 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data)
/* Got more to go. */
if (devc->acq_state == ACQ_GOT_MEASUREMENT) {
/* Retrigger */
retrigger_measurement(scpi, devc);
activate_next_channel(devc);
/* Retrigger, or check if scan-advance is in sync. */
if (((devc->num_samples % 10) == 9)
&& (devc->num_active_channels > 1)) {
request_current_channel(scpi, devc);
} else {
retrigger_measurement(scpi, devc);
}
}
return TRUE;

View File

@ -38,6 +38,7 @@ struct rear_card_info {
enum card_type type;
const char *name;
const char *cg_name;
unsigned int num_channels;
};
/* Possible states in an acquisition. */
@ -46,6 +47,14 @@ enum acquisition_state {
ACQ_REQUESTED_HIRES,
ACQ_REQUESTED_RANGE,
ACQ_GOT_MEASUREMENT,
ACQ_REQUESTED_CHANNEL_SYNC,
ACQ_GOT_CHANNEL_SYNC,
};
/* Channel connector (front terminals, or rear card. */
enum channel_conn {
CONN_FRONT,
CONN_REAR,
};
/** Private, per-device-instance driver context. */
@ -60,13 +69,23 @@ struct dev_context {
enum sr_unit measurement_unit;
uint64_t limit_samples;
float nplc;
GSList *active_channels;
unsigned int num_active_channels;
struct sr_channel *current_channel;
/* Operational state */
enum acquisition_state acq_state;
enum channel_conn input_loc;
uint64_t num_samples;
double base_measurement;
double hires_register;
double measurement_range;
double last_channel_sync;
};
struct channel_context {
enum channel_conn location;
int index;
};
SR_PRIV const struct rear_card_info *hp_3457a_probe_rear_card(struct sr_scpi_dev_inst *scpi);
@ -74,5 +93,9 @@ SR_PRIV int hp_3457a_receive_data(int fd, int revents, void *cb_data);
SR_PRIV int hp_3457a_set_mq(const struct sr_dev_inst *sdi, enum sr_mq mq,
enum sr_mqflag mq_flags);
SR_PRIV int hp_3457a_set_nplc(const struct sr_dev_inst *sdi, float nplc);
SR_PRIV int hp_3457a_select_input(const struct sr_dev_inst *sdi,
enum channel_conn loc);
SR_PRIV int hp_3457a_send_scan_list(const struct sr_dev_inst *sdi,
unsigned int *channels, size_t len);
#endif