From: Martin Lederhilger Date: Sun, 16 Aug 2015 17:52:02 +0000 (+0200) Subject: gwinstek-gds-800: Initial driver implementation. X-Git-Tag: libsigrok-0.4.0~248 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=b11afbb142a9ddc3fd2c99f3d3be39683cdec4ac;p=libsigrok.git gwinstek-gds-800: Initial driver implementation. --- diff --git a/configure.ac b/configure.ac index 3eb04860..ba98f3e8 100644 --- a/configure.ac +++ b/configure.ac @@ -229,7 +229,7 @@ SR_DRIVER([demo], [demo]) SR_DRIVER([Fluke DMM], [fluke-dmm], [libserialport]) SR_DRIVER([fx2lafw], [fx2lafw], [libusb]) SR_DRIVER([GMC MH 1x/2x], [gmc-mh-1x-2x], [libserialport]) -SR_DRIVER([gwinstek gds-800], [gwinstek-gds-800], [libserialport]) +SR_DRIVER([GW Instek GDS-800], [gwinstek-gds-800], [libserialport]) SR_DRIVER([Hameg HMO], [hameg-hmo], [libserialport]) SR_DRIVER([Hantek DSO], [hantek-dso], [libusb]) SR_DRIVER([Hung-Chang DSO-2100], [hung-chang-dso-2100], [libieee1284]) diff --git a/src/hardware/gwinstek-gds-800/api.c b/src/hardware/gwinstek-gds-800/api.c index 1a444b08..4f384aa2 100644 --- a/src/hardware/gwinstek-gds-800/api.c +++ b/src/hardware/gwinstek-gds-800/api.c @@ -17,8 +17,20 @@ * along with this program. If not, see . */ +#include #include "protocol.h" +static const uint32_t scanopts[] = { + SR_CONF_CONN, + SR_CONF_SERIALCOMM, +}; + +static const uint32_t devopts[] = { + SR_CONF_OSCILLOSCOPE, + SR_CONF_LIMIT_FRAMES | SR_CONF_SET, + SR_CONF_SAMPLERATE | SR_CONF_GET, +}; + SR_PRIV struct sr_dev_driver gwinstek_gds_800_driver_info; static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx) @@ -26,21 +38,60 @@ static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx) return std_init(sr_ctx, di, LOG_PREFIX); } -static GSList *scan(struct sr_dev_driver *di, GSList *options) +static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) { - struct drv_context *drvc; - GSList *devices; - - (void)options; + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_scpi_hw_info *hw_info; + struct sr_channel_group *cg; + + if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) { + sr_info("Couldn't get IDN response."); + return NULL; + } - devices = NULL; - drvc = di->context; - drvc->instances = NULL; + if (strcmp(hw_info->manufacturer, "GW") != 0 || + strncmp(hw_info->model, "GDS-8", 5) != 0) { + sr_scpi_hw_info_free(hw_info); + return NULL; + } - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_ACTIVE; + sdi->vendor = g_strdup(hw_info->manufacturer); + sdi->model = g_strdup(hw_info->model); + sdi->version = g_strdup(hw_info->firmware_version); + sdi->conn = scpi; + sdi->driver = &gwinstek_gds_800_driver_info; + sdi->inst_type = SR_INST_SCPI; + sdi->serial_num = g_strdup(hw_info->serial_number); + sdi->channels = NULL; + sdi->channel_groups = NULL; + + sr_scpi_hw_info_free(hw_info); + + devc = g_malloc0(sizeof(struct dev_context)); + devc->frame_limit = 1; + devc->sample_rate = 0.; + devc->df_started = FALSE; + sdi->priv = devc; + + sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1"); + sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "CH2"); + + cg = g_malloc0(sizeof(struct sr_channel_group)); + cg->name = g_strdup(""); + cg->channels = g_slist_append(cg->channels, g_slist_nth_data(sdi->channels, 0)); + cg->channels = g_slist_append(cg->channels, g_slist_nth_data(sdi->channels, 1)); + cg->priv = NULL; + sdi->channel_groups = g_slist_append(NULL, cg); + + return sdi; +} - return devices; +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + return sr_scpi_scan(di->context, options, probe_device); } static GSList *dev_list(const struct sr_dev_driver *di) @@ -55,9 +106,13 @@ static int dev_clear(const struct sr_dev_driver *di) static int dev_open(struct sr_dev_inst *sdi) { - (void)sdi; + int ret; + struct sr_scpi_dev_inst *scpi = sdi->conn; - /* TODO: get handle from sdi->conn and open it. */ + if ((ret = sr_scpi_open(scpi)) < 0) { + sr_err("Failed to open SCPI device: %s.", sr_strerror(ret)); + return SR_ERR; + } sdi->status = SR_ST_ACTIVE; @@ -66,11 +121,17 @@ static int dev_open(struct sr_dev_inst *sdi) static int dev_close(struct sr_dev_inst *sdi) { - (void)sdi; + struct sr_scpi_dev_inst *scpi; - /* TODO: get handle from sdi->conn and close it. */ + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR_DEV_CLOSED; - sdi->status = SR_ST_INACTIVE; + scpi = sdi->conn; + if (scpi) { + if (sr_scpi_close(scpi) < 0) + return SR_ERR; + sdi->status = SR_ST_INACTIVE; + } return SR_OK; } @@ -79,100 +140,132 @@ static int cleanup(const struct sr_dev_driver *di) { dev_clear(di); - /* TODO: free other driver resources, if any. */ - return SR_OK; } static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + struct dev_context *devc; - (void)sdi; - (void)data; (void)cg; - ret = SR_OK; + if (!sdi || !(devc = sdi->priv)) + return SR_ERR_ARG; + switch (key) { - /* TODO */ + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->sample_rate); + break; default: return SR_ERR_NA; } - return ret; + return SR_OK; } static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + struct dev_context *devc; - (void)data; (void)cg; + if (!sdi || !(devc = sdi->priv)) + return SR_ERR_ARG; + if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_LIMIT_FRAMES: + devc->frame_limit = g_variant_get_uint64(data); + break; default: - ret = SR_ERR_NA; + return SR_ERR_NA; } - return ret; + return SR_OK; } static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; - (void)sdi; - (void)data; (void)cg; - ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_SCAN_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t)); + return SR_OK; + case SR_CONF_DEVICE_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts, ARRAY_SIZE(devopts), sizeof(uint32_t)); + return SR_OK; default: return SR_ERR_NA; } - return ret; + return SR_OK; } -static int dev_acquisition_start(const struct sr_dev_inst *sdi, - void *cb_data) +static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) { - (void)sdi; + struct sr_scpi_dev_inst *scpi; + struct dev_context *devc; + (void)cb_data; + scpi = sdi->conn; + devc = sdi->priv; + if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - /* TODO: configure hardware, reset acquisition state, set up - * callbacks and send header packet. */ + devc->state = START_ACQUISITION; + devc->cur_acq_frame = 0; + + sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 50, + gwinstek_gds_800_receive_data, (void *)sdi); return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) { + struct sr_scpi_dev_inst *scpi; + struct dev_context *devc; + struct sr_datafeed_packet packet; + (void)cb_data; - if (sdi->status != SR_ST_ACTIVE) - return SR_ERR_DEV_CLOSED; + scpi = sdi->conn; + devc = sdi->priv; + + if (sdi->status != SR_ST_ACTIVE) { + sr_err("Device inactive, can't stop acquisition."); + return SR_ERR; + } + + if (devc->df_started) { + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + + packet.type = SR_DF_END; + sr_session_send(sdi, &packet); + + devc->df_started = FALSE; + } - /* TODO: stop acquisition. */ + sr_scpi_source_remove(sdi->session, scpi); return SR_OK; } SR_PRIV struct sr_dev_driver gwinstek_gds_800_driver_info = { .name = "gwinstek-gds-800", - .longname = "gwinstek gds-800", + .longname = "GW Instek GDS-800 series", .api_version = 1, .init = init, .cleanup = cleanup, diff --git a/src/hardware/gwinstek-gds-800/protocol.c b/src/hardware/gwinstek-gds-800/protocol.c index 8c5a1020..05328df9 100644 --- a/src/hardware/gwinstek-gds-800/protocol.c +++ b/src/hardware/gwinstek-gds-800/protocol.c @@ -18,11 +18,62 @@ */ #include "protocol.h" +#include + +#define ANALOG_CHANNELS 2 +#define VERTICAL_DIVISIONS 10 + +static int read_data(struct sr_dev_inst *sdi, void *cb_data, + struct sr_scpi_dev_inst *scpi, struct dev_context *devc, + int data_size) +{ + int len; + + len = sr_scpi_read_data(scpi, + &devc->rcv_buffer[devc->cur_rcv_buffer_position], + data_size - devc->cur_rcv_buffer_position); + if (len < 0) { + sr_err("Read data error."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + devc->cur_rcv_buffer_position = 0; + return SR_ERR; + } + + devc->cur_rcv_buffer_position += len; + + /* Handle the case where sr_scpi_read_data stopped at the newline. */ + if (len < data_size && sr_scpi_read_complete(scpi)) { + devc->rcv_buffer[devc->cur_rcv_buffer_position] = '\n'; + devc->cur_rcv_buffer_position++; + } + + if (devc->cur_rcv_buffer_position < data_size) + return SR_ERR; /* Not finished yet. */ + else if (devc->cur_rcv_buffer_position == data_size) { + devc->cur_rcv_buffer_position = 0; + return SR_OK; + } else { + sr_err("Too many bytes read."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + devc->cur_rcv_buffer_position = 0; + return SR_ERR; + } +} SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data) { - const struct sr_dev_inst *sdi; + struct sr_dev_inst *sdi; + struct sr_scpi_dev_inst *scpi; struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + char command[32]; + char *response; + float volts_per_division; + int num_samples, i; + float samples[MAX_SAMPLES]; + uint32_t sample_rate; + char *end_ptr; (void)fd; @@ -32,8 +83,200 @@ SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data) if (!(devc = sdi->priv)) return TRUE; - if (revents == G_IO_IN) { - /* TODO */ + scpi = sdi->conn; + + if (!(revents == G_IO_IN || revents == 0)) + return TRUE; + + switch (devc->state) { + case START_ACQUISITION: + if (sr_scpi_send(scpi, ":TRIG:MOD 3") != SR_OK) { + sr_err("Failed to set trigger mode to SINGLE."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + if (sr_scpi_send(scpi, ":STOP") != SR_OK) { + sr_err("Failed to put the trigger system into STOP state."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + if (sr_scpi_send(scpi, ":RUN") != SR_OK) { + sr_err("Failed to put the trigger system into RUN state."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + + devc->cur_acq_channel = 0; + devc->state = START_TRANSFER_OF_CHANNEL_DATA; + break; + case START_TRANSFER_OF_CHANNEL_DATA: + if (((struct sr_channel *)g_slist_nth_data(sdi->channels, devc->cur_acq_channel))->enabled) { + if (sr_scpi_send(scpi, ":ACQ%d:MEM?", devc->cur_acq_channel+1) != SR_OK) { + sr_err("Failed to acquire memory."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + if (sr_scpi_read_begin(scpi) != SR_OK) { + sr_err("Could not begin reading SCPI response."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + devc->state = WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE; + devc->cur_rcv_buffer_position = 0; + } else { + /* All channels acquired. */ + if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) { + sr_spew("All channels acquired."); + + if (devc->cur_acq_frame == devc->frame_limit - 1) { + /* All frames accquired. */ + sr_spew("All frames acquired."); + + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } else { + /* Start acquiring next frame. */ + if (devc->df_started) { + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + + packet.type = SR_DF_FRAME_BEGIN; + sr_session_send(sdi, &packet); + } + + devc->cur_acq_frame++; + devc->state = START_ACQUISITION; + } + } else { + /* Start acquiring next channel. */ + devc->cur_acq_channel++; + } + } + break; + case WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, 1) == SR_OK) { + if (devc->rcv_buffer[0] == '#') + devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE; + } + break; + case WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, 1) == SR_OK) { + if (devc->rcv_buffer[0] != '4' && + devc->rcv_buffer[0] != '5' && + devc->rcv_buffer[0] != '6') { + sr_err("Data size digits is not 4, 5 or 6 but " + "'%c'.", devc->rcv_buffer[0]); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } else { + devc->data_size_digits = devc->rcv_buffer[0] - '0'; + devc->state = WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE; + } + } + break; + case WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, devc->data_size_digits) == SR_OK) { + devc->rcv_buffer[devc->data_size_digits] = 0; + if (sr_atoi(devc->rcv_buffer, &devc->data_size) != SR_OK) { + sr_err("Could not parse data size '%s'", devc->rcv_buffer); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } else + devc->state = WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE; + } + break; + case WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, sizeof(float)) == SR_OK) { + /* + * Contrary to the documentation, this field is + * transfered with most significant byte first! + */ + sample_rate = RB32(devc->rcv_buffer); + memcpy(&devc->sample_rate, &sample_rate, sizeof(float)); + devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE; + + if (!devc->df_started) { + std_session_send_df_header(sdi, LOG_PREFIX); + + packet.type = SR_DF_FRAME_BEGIN; + sr_session_send(sdi, &packet); + + devc->df_started = TRUE; + } + } + break; + case WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, 1) == SR_OK) + devc->state = WAIT_FOR_TRANSFER_OF_RESERVED_DATA_COMPLETE; + break; + case WAIT_FOR_TRANSFER_OF_RESERVED_DATA_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, 3) == SR_OK) + devc->state = WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE; + break; + case WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE: + if (read_data(sdi, cb_data, scpi, devc, devc->data_size - 8) == SR_OK) { + /* Fetch data needed for conversion from device. */ + snprintf(command, sizeof(command), ":CHAN%d:SCAL?", + devc->cur_acq_channel + 1); + if (sr_scpi_get_string(scpi, command, &response) != SR_OK) { + sr_err("Failed to get volts per division."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } + volts_per_division = g_ascii_strtod(response, &end_ptr); + if (!strcmp(end_ptr, "mV")) + volts_per_division *= 1.e-3; + g_free(response); + + num_samples = (devc->data_size - 8) / 2; + sr_spew("Received %d number of samples from channel " + "%d.", num_samples, devc->cur_acq_channel + 1); + + /* Convert data. */ + for (i = 0; i < num_samples; i++) + samples[i] = ((float) ((int16_t) (RB16(&devc->rcv_buffer[i*2])))) / 256. * VERTICAL_DIVISIONS * volts_per_division; + + /* Fill frame. */ + analog.channels = g_slist_append(NULL, g_slist_nth_data(sdi->channels, devc->cur_acq_channel)); + analog.num_samples = num_samples; + analog.data = samples; + analog.mq = SR_MQ_VOLTAGE; + analog.unit = SR_UNIT_VOLT; + analog.mqflags = 0; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(cb_data, &packet); + g_slist_free(analog.channels); + + /* All channels acquired. */ + if (devc->cur_acq_channel == ANALOG_CHANNELS - 1) { + sr_spew("All channels acquired."); + + if (devc->cur_acq_frame == devc->frame_limit - 1) { + /* All frames acquired. */ + sr_spew("All frames acquired."); + sdi->driver->dev_acquisition_stop(sdi, cb_data); + return TRUE; + } else { + /* Start acquiring next frame. */ + if (devc->df_started) { + packet.type = SR_DF_FRAME_END; + sr_session_send(sdi, &packet); + + packet.type = SR_DF_FRAME_BEGIN; + sr_session_send(sdi, &packet); + } + devc->cur_acq_frame++; + devc->state = START_ACQUISITION; + } + } else { + /* Start acquiring next channel. */ + devc->state = START_TRANSFER_OF_CHANNEL_DATA; + devc->cur_acq_channel++; + return TRUE; + } + } + break; } return TRUE; diff --git a/src/hardware/gwinstek-gds-800/protocol.h b/src/hardware/gwinstek-gds-800/protocol.h index 5ee467fa..382e87b8 100644 --- a/src/hardware/gwinstek-gds-800/protocol.h +++ b/src/hardware/gwinstek-gds-800/protocol.h @@ -22,21 +22,40 @@ #include #include -#include "libsigrok.h" +#include #include "libsigrok-internal.h" +#include "scpi.h" #define LOG_PREFIX "gwinstek-gds-800" +#define MAX_SAMPLES 125000 +#define MAX_RCV_BUFFER_SIZE (MAX_SAMPLES * 2) + +enum gds_state +{ + START_ACQUISITION, + START_TRANSFER_OF_CHANNEL_DATA, + WAIT_FOR_TRANSFER_OF_BEGIN_TRANSMISSION_COMPLETE, + WAIT_FOR_TRANSFER_OF_DATA_SIZE_DIGIT_COMPLETE, + WAIT_FOR_TRANSFER_OF_DATA_SIZE_COMPLETE, + WAIT_FOR_TRANSFER_OF_SAMPLE_RATE_COMPLETE, + WAIT_FOR_TRANSFER_OF_CHANNEL_INDICATOR_COMPLETE, + WAIT_FOR_TRANSFER_OF_RESERVED_DATA_COMPLETE, + WAIT_FOR_TRANSFER_OF_CHANNEL_DATA_COMPLETE, +}; + /** Private, per-device-instance driver context. */ struct dev_context { - /* Model-specific information */ - - /* Acquisition settings */ - - /* Operational state */ - - /* Temporary state across callbacks */ - + enum gds_state state; + uint64_t cur_acq_frame; + uint64_t frame_limit; + int cur_acq_channel; + int cur_rcv_buffer_position; + char rcv_buffer[MAX_RCV_BUFFER_SIZE]; + int data_size_digits; + int data_size; + float sample_rate; + gboolean df_started; }; SR_PRIV int gwinstek_gds_800_receive_data(int fd, int revents, void *cb_data);