X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fhameg-hmo%2Fprotocol.c;h=437b5f37dfef445d2502379132a2e7590f3c9dc8;hb=4b25cbffa17c9b8c9c30f98ffe6918a15b664b2a;hp=99a30acd75dd8ec21af14235be879732d87ea87b;hpb=d431e4ec28d5d168bc0d98d5545a585b89d52e8d;p=libsigrok.git diff --git a/src/hardware/hameg-hmo/protocol.c b/src/hardware/hameg-hmo/protocol.c index 99a30acd..437b5f37 100644 --- a/src/hardware/hameg-hmo/protocol.c +++ b/src/hardware/hameg-hmo/protocol.c @@ -23,6 +23,12 @@ #include "scpi.h" #include "protocol.h" +SR_PRIV void hmo_queue_logic_data(struct dev_context *devc, + size_t group, GByteArray *pod_data); +SR_PRIV void hmo_send_logic_packet(struct sr_dev_inst *sdi, + struct dev_context *devc); +SR_PRIV void hmo_cleanup_logic_data(struct dev_context *devc); + static const char *hameg_scpi_dialect[] = { [SCPI_CMD_GET_DIG_DATA] = ":FORM UINT,8;:POD%d:DATA?", [SCPI_CMD_GET_TIMEBASE] = ":TIM:SCAL?", @@ -31,7 +37,8 @@ static const char *hameg_scpi_dialect[] = { [SCPI_CMD_SET_COUPLING] = ":CHAN%d:COUP %s", [SCPI_CMD_GET_SAMPLE_RATE] = ":ACQ:SRAT?", [SCPI_CMD_GET_SAMPLE_RATE_LIVE] = ":%s:DATA:POINTS?", - [SCPI_CMD_GET_ANALOG_DATA] = ":FORM REAL,32;:CHAN%d:DATA?", + [SCPI_CMD_GET_ANALOG_DATA] = ":FORM:BORD %s;" \ + ":FORM REAL,32;:CHAN%d:DATA?", [SCPI_CMD_GET_VERTICAL_DIV] = ":CHAN%d:SCAL?", [SCPI_CMD_SET_VERTICAL_DIV] = ":CHAN%d:SCAL %s", [SCPI_CMD_GET_DIG_POD_STATE] = ":POD%d:STAT?", @@ -48,27 +55,26 @@ static const char *hameg_scpi_dialect[] = { [SCPI_CMD_GET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT?", [SCPI_CMD_SET_ANALOG_CHAN_STATE] = ":CHAN%d:STAT %d", [SCPI_CMD_GET_PROBE_UNIT] = ":PROB%d:SET:ATT:UNIT?", - [SCPI_CMD_GET_BYTE_ORDER] = ":FORM:BORD?", }; -static const uint32_t hmo_devopts[] = { +static const uint32_t devopts[] = { SR_CONF_OSCILLOSCOPE, SR_CONF_LIMIT_FRAMES | SR_CONF_GET | SR_CONF_SET, - SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_SAMPLERATE | SR_CONF_GET, SR_CONF_TIMEBASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_NUM_HDIV | SR_CONF_GET, - SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_HORIZ_TRIGGERPOS | SR_CONF_GET | SR_CONF_SET, - SR_CONF_SAMPLERATE | SR_CONF_GET, + SR_CONF_TRIGGER_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_SLOPE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; -static const uint32_t hmo_analog_devopts[] = { +static const uint32_t analog_devopts[] = { SR_CONF_NUM_VDIV | SR_CONF_GET, - SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_VDIV | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_COUPLING | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; -static const char *hmo_coupling_options[] = { +static const char *coupling_options[] = { "AC", // AC with 50 Ohm termination (152x, 202x, 30xx, 1202) "ACL", // AC with 1 MOhm termination "DC", // DC with 50 Ohm termination @@ -84,7 +90,7 @@ static const char *scope_trigger_slopes[] = { NULL, }; -static const char *hmo_compact2_trigger_sources[] = { +static const char *compact2_trigger_sources[] = { "CH1", "CH2", "LINE", @@ -103,7 +109,7 @@ static const char *hmo_compact2_trigger_sources[] = { NULL, }; -static const char *hmo_compact4_trigger_sources[] = { +static const char *compact4_trigger_sources[] = { "CH1", "CH2", "CH3", @@ -124,7 +130,7 @@ static const char *hmo_compact4_trigger_sources[] = { NULL, }; -static const char *hmo_compact4_dig16_trigger_sources[] = { +static const char *compact4_dig16_trigger_sources[] = { "CH1", "CH2", "CH3", @@ -153,7 +159,7 @@ static const char *hmo_compact4_dig16_trigger_sources[] = { NULL, }; -static const uint64_t hmo_timebases[][2] = { +static const uint64_t timebases[][2] = { /* nanoseconds */ { 2, 1000000000 }, { 5, 1000000000 }, @@ -192,7 +198,7 @@ static const uint64_t hmo_timebases[][2] = { { 50, 1 }, }; -static const uint64_t hmo_vdivs[][2] = { +static const uint64_t vdivs[][2] = { /* millivolts */ { 1, 1000 }, { 2, 1000 }, @@ -250,21 +256,21 @@ static const struct scope_config scope_models[] = { .analog_names = &scope_analog_channel_names, .digital_names = &scope_digital_channel_names, - .devopts = &hmo_devopts, - .num_devopts = ARRAY_SIZE(hmo_devopts), + .devopts = &devopts, + .num_devopts = ARRAY_SIZE(devopts), - .analog_devopts = &hmo_analog_devopts, - .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts), + .analog_devopts = &analog_devopts, + .num_analog_devopts = ARRAY_SIZE(analog_devopts), - .coupling_options = &hmo_coupling_options, - .trigger_sources = &hmo_compact2_trigger_sources, + .coupling_options = &coupling_options, + .trigger_sources = &compact2_trigger_sources, .trigger_slopes = &scope_trigger_slopes, - .timebases = &hmo_timebases, - .num_timebases = ARRAY_SIZE(hmo_timebases), + .timebases = &timebases, + .num_timebases = ARRAY_SIZE(timebases), - .vdivs = &hmo_vdivs, - .num_vdivs = ARRAY_SIZE(hmo_vdivs), + .vdivs = &vdivs, + .num_vdivs = ARRAY_SIZE(vdivs), .num_xdivs = 12, .num_ydivs = 8, @@ -280,21 +286,21 @@ static const struct scope_config scope_models[] = { .analog_names = &scope_analog_channel_names, .digital_names = &scope_digital_channel_names, - .devopts = &hmo_devopts, - .num_devopts = ARRAY_SIZE(hmo_devopts), + .devopts = &devopts, + .num_devopts = ARRAY_SIZE(devopts), - .analog_devopts = &hmo_analog_devopts, - .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts), + .analog_devopts = &analog_devopts, + .num_analog_devopts = ARRAY_SIZE(analog_devopts), - .coupling_options = &hmo_coupling_options, - .trigger_sources = &hmo_compact4_trigger_sources, + .coupling_options = &coupling_options, + .trigger_sources = &compact4_trigger_sources, .trigger_slopes = &scope_trigger_slopes, - .timebases = &hmo_timebases, - .num_timebases = ARRAY_SIZE(hmo_timebases), + .timebases = &timebases, + .num_timebases = ARRAY_SIZE(timebases), - .vdivs = &hmo_vdivs, - .num_vdivs = ARRAY_SIZE(hmo_vdivs), + .vdivs = &vdivs, + .num_vdivs = ARRAY_SIZE(vdivs), .num_xdivs = 12, .num_ydivs = 8, @@ -302,7 +308,7 @@ static const struct scope_config scope_models[] = { .scpi_dialect = &hameg_scpi_dialect, }, { - .name = {"HMO2524", "HMO3034", "HMO3044", "HMO3054", NULL}, + .name = {"HMO2524", "HMO3034", "HMO3044", "HMO3054", "HMO3524", NULL}, .analog_channels = 4, .digital_channels = 16, .digital_pods = 2, @@ -310,21 +316,21 @@ static const struct scope_config scope_models[] = { .analog_names = &scope_analog_channel_names, .digital_names = &scope_digital_channel_names, - .devopts = &hmo_devopts, - .num_devopts = ARRAY_SIZE(hmo_devopts), + .devopts = &devopts, + .num_devopts = ARRAY_SIZE(devopts), - .analog_devopts = &hmo_analog_devopts, - .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts), + .analog_devopts = &analog_devopts, + .num_analog_devopts = ARRAY_SIZE(analog_devopts), - .coupling_options = &hmo_coupling_options, - .trigger_sources = &hmo_compact4_dig16_trigger_sources, + .coupling_options = &coupling_options, + .trigger_sources = &compact4_dig16_trigger_sources, .trigger_slopes = &scope_trigger_slopes, - .timebases = &hmo_timebases, - .num_timebases = ARRAY_SIZE(hmo_timebases), + .timebases = &timebases, + .num_timebases = ARRAY_SIZE(timebases), - .vdivs = &hmo_vdivs, - .num_vdivs = ARRAY_SIZE(hmo_vdivs), + .vdivs = &vdivs, + .num_vdivs = ARRAY_SIZE(vdivs), .num_xdivs = 12, .num_ydivs = 8, @@ -358,7 +364,7 @@ static void scope_state_dump(const struct scope_config *config, state->digital_pods[i] ? "On" : "Off"); } - tmp = sr_period_string((*config->timebases)[state->timebase][0] * + tmp = sr_period_string((*config->timebases)[state->timebase][0], (*config->timebases)[state->timebase][1]); sr_info("Current timebase: %s", tmp); g_free(tmp); @@ -456,8 +462,7 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi, if (sr_scpi_get_string(scpi, command, &tmp_str) != SR_OK) return SR_ERR; - if (array_float_get(tmp_str, hmo_vdivs, ARRAY_SIZE(hmo_vdivs), - &j) != SR_OK) { + if (array_float_get(tmp_str, vdivs, ARRAY_SIZE(vdivs), &j) != SR_OK) { g_free(tmp_str); sr_err("Could not determine array index for vertical div scale."); return SR_ERR; @@ -496,22 +501,6 @@ static int analog_channel_state_get(struct sr_scpi_dev_inst *scpi, g_free(tmp_str); } - /* - * Determine the byte order which will be used for data blocks. - * A ":FORM:BORD?" request will yield either an "MSBF" or "LSBF" - * response. - */ - state->byteorder = '?'; - if (sr_scpi_get_string(scpi, - (*config->scpi_dialect)[SCPI_CMD_GET_BYTE_ORDER], - &tmp_str) != SR_OK) - return SR_ERR; - if (tmp_str[0] == 'M') - state->byteorder = 'b'; - else if (tmp_str[0] == 'L') - state->byteorder = 'l'; - g_free(tmp_str); - return SR_OK; } @@ -638,8 +627,7 @@ SR_PRIV int hmo_scope_state_get(struct sr_dev_inst *sdi) &tmp_str) != SR_OK) return SR_ERR; - if (array_float_get(tmp_str, hmo_timebases, ARRAY_SIZE(hmo_timebases), - &i) != SR_OK) { + if (array_float_get(tmp_str, timebases, ARRAY_SIZE(timebases), &i) != SR_OK) { g_free(tmp_str); sr_err("Could not determine array index for time base."); return SR_ERR; @@ -780,6 +768,89 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) return SR_OK; } +/* Queue data of one channel group, for later submission. */ +SR_PRIV void hmo_queue_logic_data(struct dev_context *devc, + size_t group, GByteArray *pod_data) +{ + size_t size; + GByteArray *store; + uint8_t *logic_data; + size_t idx, logic_step; + + /* + * Upon first invocation, allocate the array which can hold the + * combined logic data for all channels. Assume that each channel + * will yield an identical number of samples per receive call. + * + * As a poor man's safety measure: (Silently) skip processing + * for unexpected sample counts, and ignore samples for + * unexpected channel groups. Don't bother with complicated + * resize logic, considering that many models only support one + * pod, and the most capable supported models have two pods of + * identical size. We haven't yet seen any "odd" configuration. + */ + if (!devc->logic_data) { + size = pod_data->len * devc->pod_count; + store = g_byte_array_sized_new(size); + memset(store->data, 0, size); + store = g_byte_array_set_size(store, size); + devc->logic_data = store; + } else { + store = devc->logic_data; + size = store->len / devc->pod_count; + if (size != pod_data->len) + return; + if (group >= devc->pod_count) + return; + } + + /* + * Fold the data of the most recently received channel group into + * the storage, where data resides for all channels combined. + */ + logic_data = store->data; + logic_data += group; + logic_step = devc->pod_count; + for (idx = 0; idx < pod_data->len; idx++) { + *logic_data = pod_data->data[idx]; + logic_data += logic_step; + } +} + +/* Submit data for all channels, after the individual groups got collected. */ +SR_PRIV void hmo_send_logic_packet(struct sr_dev_inst *sdi, + struct dev_context *devc) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_logic logic; + + if (!devc->logic_data) + return; + + logic.data = devc->logic_data->data; + logic.length = devc->logic_data->len; + logic.unitsize = devc->pod_count; + + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + + sr_session_send(sdi, &packet); +} + +/* Undo previous resource allocation. */ +SR_PRIV void hmo_cleanup_logic_data(struct dev_context *devc) +{ + + if (devc->logic_data) { + g_byte_array_free(devc->logic_data, TRUE); + devc->logic_data = NULL; + } + /* + * Keep 'pod_count'! It's required when more frames will be + * received, and does not harm when kept after acquisition. + */ +} + SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) { struct sr_channel *ch; @@ -793,6 +864,7 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) struct sr_analog_meaning meaning; struct sr_analog_spec spec; struct sr_datafeed_logic logic; + size_t group; (void)fd; (void)revents; @@ -815,6 +887,18 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) ch = devc->current_channel->data; state = devc->model_state; + /* + * Send "frame begin" packet upon reception of data for the + * first enabled channel. + */ + if (devc->current_channel == devc->enabled_channels) { + packet.type = SR_DF_FRAME_BEGIN; + sr_session_send(sdi, &packet); + } + + /* + * Pass on the received data of the channel(s). + */ switch (ch->type) { case SR_CHANNEL_ANALOG: if (sr_scpi_get_block(sdi->conn, NULL, &data) != SR_OK) { @@ -824,9 +908,6 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) return TRUE; } - packet.type = SR_DF_FRAME_BEGIN; - sr_session_send(sdi, &packet); - packet.type = SR_DF_ANALOG; analog.data = data->data; @@ -838,8 +919,11 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) encoding.unitsize = sizeof(float); encoding.is_signed = TRUE; encoding.is_float = TRUE; - /* Assume LE format when unknown for backwards compat. */ - encoding.is_bigendian = (state->byteorder == 'b') ? TRUE : FALSE; +#ifdef WORDS_BIGENDIAN + encoding.is_bigendian = TRUE; +#else + encoding.is_bigendian = FALSE; +#endif /* TODO: Use proper 'digits' value for this device (and its modes). */ encoding.digits = 2; encoding.is_digits_decimal = FALSE; @@ -870,15 +954,31 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) return TRUE; } - packet.type = SR_DF_FRAME_BEGIN; - sr_session_send(sdi, &packet); + /* + * If only data from the first pod is involved in the + * acquisition, then the raw input bytes can get passed + * forward for performance reasons. When the second pod + * is involved (either alone, or in combination with the + * first pod), then the received bytes need to be put + * into memory in such a layout that all channel groups + * get combined, and a unitsize larger than a single byte + * applies. The "queue" logic transparently copes with + * any such configuration. This works around the lack + * of support for "meaning" to logic data, which is used + * above for analog data. + */ + if (devc->pod_count == 1) { + packet.type = SR_DF_LOGIC; + logic.data = data->data; + logic.length = data->len; + logic.unitsize = 1; + packet.payload = &logic; + sr_session_send(sdi, &packet); + } else { + group = ch->index / 8; + hmo_queue_logic_data(devc, group, data); + } - logic.length = data->len; - logic.unitsize = 1; - logic.data = data->data; - packet.type = SR_DF_LOGIC; - packet.payload = &logic; - sr_session_send(sdi, &packet); g_byte_array_free(data, TRUE); data = NULL; break; @@ -887,14 +987,37 @@ SR_PRIV int hmo_receive_data(int fd, int revents, void *cb_data) break; } - packet.type = SR_DF_FRAME_END; - sr_session_send(sdi, &packet); - + /* + * Advance to the next enabled channel. When data for all enabled + * channels was received, then flush potentially queued logic data, + * and send the "frame end" packet. + */ if (devc->current_channel->next) { devc->current_channel = devc->current_channel->next; hmo_request_data(sdi); - } else if (++devc->num_frames == devc->frame_limit) { - sdi->driver->dev_acquisition_stop(sdi); + return TRUE; + } + hmo_send_logic_packet(sdi, devc); + + /* + * Release the logic data storage after each frame. This copes + * with sample counts that differ in length per frame. -- Is + * this a real constraint when acquiring multiple frames with + * identical device settings? + */ + hmo_cleanup_logic_data(devc); + + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + + /* + * End of frame was reached. Stop acquisition after the specified + * number of frames, or continue reception by starting over at + * the first enabled channel. + */ + if (++devc->num_frames == devc->frame_limit) { + sr_dev_acquisition_stop(sdi); + hmo_cleanup_logic_data(devc); } else { devc->current_channel = devc->enabled_channels; hmo_request_data(sdi);