]> sigrok.org Git - libsigrok.git/commitdiff
hp-3457a: Implement support for rear terminals and plug-in cards
authorAlexandru Gagniuc <redacted>
Mon, 4 Apr 2016 01:45:25 +0000 (18:45 -0700)
committerUwe Hermann <redacted>
Sat, 23 Apr 2016 15:44:26 +0000 (17:44 +0200)
src/hardware/hp-3457a/api.c
src/hardware/hp-3457a/protocol.c
src/hardware/hp-3457a/protocol.h

index 1b652bdd9a0deaf13462da67fd7352c023b4690b..eae8195ce48a716b2d8b8159425caa2f9de6f592 100644 (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;
 }
 
index bfc7902575bc92699a59f2ea7991968ed3b35435..d9aa83383ac6ac3a492b8ad4d68ebcd995bc902a 100644 (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;
index 64abadfd9fe55b4447dc4c6f36a820f06b0ae659..dda19a23610e7e75e7df82f579da648bd059cb80 100644 (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