hp-3457a: Implement support for rear terminals and plug-in cards
This commit is contained in:
parent
2c04f943ef
commit
9a093be9f5
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue