{
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;
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;
}
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");
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
* 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;
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;
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;
}
.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,
}
};
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;
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)
{
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.
*
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;
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;
}
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;
}
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;
/* 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;
enum card_type type;
const char *name;
const char *cg_name;
+ unsigned int num_channels;
};
/* Possible states in an acquisition. */
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. */
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);
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