X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fhameg-hmo%2Fprotocol.c;h=12e79c47393ec342cd0719fa8634cfd6aea3f911;hb=55fb76b34826a287240bebeeda688e4c465b4751;hp=85712f8de427295ebdeeb85ca4fdb08c2d49c566;hpb=d9251a2c9f1ca4380c27240ccca90c9f9ed46d3f;p=libsigrok.git diff --git a/src/hardware/hameg-hmo/protocol.c b/src/hardware/hameg-hmo/protocol.c index 85712f8d..12e79c47 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?", @@ -53,18 +60,18 @@ static const char *hameg_scpi_dialect[] = { static const uint32_t hmo_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[] = { 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[] = { @@ -123,6 +130,35 @@ static const char *hmo_compact4_trigger_sources[] = { NULL, }; +static const char *hmo_compact4_dig16_trigger_sources[] = { + "CH1", + "CH2", + "CH3", + "CH4", + "LINE", + "EXT", + "PATT", + "BUS1", + "BUS2", + "D0", + "D1", + "D2", + "D3", + "D4", + "D5", + "D6", + "D7", + "D8", + "D9", + "D10", + "D11", + "D12", + "D13", + "D14", + "D15", + NULL, +}; + static const uint64_t hmo_timebases[][2] = { /* nanoseconds */ { 2, 1000000000 }, @@ -242,9 +278,7 @@ static const struct scope_config scope_models[] = { .scpi_dialect = &hameg_scpi_dialect, }, { - /* HMO2524/3034/3044/3054 support 16 digital channels but they're not supported yet. */ - .name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", "HMO2524", - "HMO3034", "HMO3044", "HMO3054", NULL}, + .name = {"HMO724", "HMO1024", "HMO1524", "HMO2024", NULL}, .analog_channels = 4, .digital_channels = 8, .digital_pods = 1, @@ -271,6 +305,36 @@ static const struct scope_config scope_models[] = { .num_xdivs = 12, .num_ydivs = 8, + .scpi_dialect = &hameg_scpi_dialect, + }, + { + .name = {"HMO2524", "HMO3034", "HMO3044", "HMO3054", "HMO3524", NULL}, + .analog_channels = 4, + .digital_channels = 16, + .digital_pods = 2, + + .analog_names = &scope_analog_channel_names, + .digital_names = &scope_digital_channel_names, + + .devopts = &hmo_devopts, + .num_devopts = ARRAY_SIZE(hmo_devopts), + + .analog_devopts = &hmo_analog_devopts, + .num_analog_devopts = ARRAY_SIZE(hmo_analog_devopts), + + .coupling_options = &hmo_coupling_options, + .trigger_sources = &hmo_compact4_dig16_trigger_sources, + .trigger_slopes = &scope_trigger_slopes, + + .timebases = &hmo_timebases, + .num_timebases = ARRAY_SIZE(hmo_timebases), + + .vdivs = &hmo_vdivs, + .num_vdivs = ARRAY_SIZE(hmo_vdivs), + + .num_xdivs = 12, + .num_ydivs = 8, + .scpi_dialect = &hameg_scpi_dialect, }, }; @@ -300,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); @@ -631,7 +695,7 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) { char tmp[25]; int model_index; - unsigned int i, j; + unsigned int i, j, group; struct sr_channel *ch; struct dev_context *devc; @@ -684,7 +748,7 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) devc->digital_groups[i]->name = g_strdup(tmp); sdi->channel_groups = g_slist_append(sdi->channel_groups, - devc->digital_groups[i < 8 ? 0 : 1]); + devc->digital_groups[i]); } /* Add digital channels. */ @@ -692,8 +756,9 @@ SR_PRIV int hmo_init_device(struct sr_dev_inst *sdi) ch = sr_channel_new(sdi, i, SR_CHANNEL_LOGIC, TRUE, (*scope_models[model_index].digital_names)[i]); - devc->digital_groups[i < 8 ? 0 : 1]->channels = g_slist_append( - devc->digital_groups[i < 8 ? 0 : 1]->channels, ch); + group = i / 8; + devc->digital_groups[group]->channels = g_slist_append( + devc->digital_groups[group]->channels, ch); } devc->model_config = &scope_models[model_index]; @@ -705,6 +770,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; @@ -718,6 +866,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; @@ -740,6 +889,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) { @@ -749,9 +910,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; @@ -763,7 +921,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; +#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; @@ -794,15 +956,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; @@ -811,14 +989,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);