From: Timo Kokkonen Date: Sun, 6 Sep 2020 05:06:30 +0000 (-0700) Subject: rigol-dg: Initial Rigol DG1000z driver implementation. X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=commitdiff_plain;h=02feeb30b992a5e8a1115348d3a319731954e564 rigol-dg: Initial Rigol DG1000z driver implementation. This implements support for Rigol DG1000z series digital signal generators. Driver provides basic control via channel groups ("1", and "2"). Acquisition returns data from the built-in frequency counter. Supported models: DG1022Z, DG1032Z, DG1062Z [ gsi: added some coding style adjustment ] --- diff --git a/README.devices b/README.devices index 1183b460..a6ab698b 100644 --- a/README.devices +++ b/README.devices @@ -136,6 +136,7 @@ The following drivers/devices do not need any firmware upload: - pce-322a - pipistrello-ols - rdtech-dps + - rigol-dg - rigol-ds - rohde-schwarz-sme-0x - scpi-dmm diff --git a/src/hardware/rigol-dg/api.c b/src/hardware/rigol-dg/api.c index b3f089ba..3ae40bcf 100644 --- a/src/hardware/rigol-dg/api.c +++ b/src/hardware/rigol-dg/api.c @@ -18,59 +18,341 @@ */ #include +#include +#include "scpi.h" #include "protocol.h" static struct sr_dev_driver rigol_dg_driver_info; -static GSList *scan(struct sr_dev_driver *di, GSList *options) +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t drvopts[] = { + SR_CONF_SIGNAL_GENERATOR, +}; + +static const uint32_t dg1000z_devopts[] = { + SR_CONF_CONTINUOUS, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, +}; + +static const uint32_t dg1000z_devopts_cg[] = { + SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, + SR_CONF_PATTERN_MODE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_AMPLITUDE | SR_CONF_GET | SR_CONF_SET, + SR_CONF_OFFSET | SR_CONF_GET | SR_CONF_SET, + SR_CONF_PHASE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_DUTY_CYCLE | SR_CONF_GET | SR_CONF_SET, +}; + +static const double phase_min_max_step[] = { 0.0, 360.0, 0.001 }; + +#define WAVEFORM_DEFAULT WFO_FREQUENCY | WFO_AMPLITUDE | WFO_OFFSET | WFO_PHASE + +static const struct waveform_spec dg1022z_waveforms[] = { + { "SIN", WF_SINE, 1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT }, + { "SQU", WF_SQUARE, 1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE }, + { "RAMP", WF_RAMP, 1.0E-6, 0.5E+6, 1.0E-6, WAVEFORM_DEFAULT }, + { "PULSE", WF_PULSE, 1.0E-6, 1.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE }, + { "USER", WF_ARB, 1.0E-6, 1.0E+7, 1.0E-6, WAVEFORM_DEFAULT }, + { "NOISE", WF_NOISE, 2.5E+7, 2.5E+7, 0.0E-0, WFO_AMPLITUDE | WFO_OFFSET }, + { "DC", WF_DC, 0.0E-0, 0.0E+0, 0.0E-0, WFO_OFFSET }, +}; + +static const struct channel_spec dg1022z_channels[] = { + { "CH1", ARRAY_AND_SIZE(dg1022z_waveforms) }, + { "CH2", ARRAY_AND_SIZE(dg1022z_waveforms) }, +}; + +static const struct waveform_spec dg1032z_waveforms[] = { + { "SIN", WF_SINE, 1.0E-6, 3.0E+7, 1.0E-6, WAVEFORM_DEFAULT }, + { "SQU", WF_SQUARE, 1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE }, + { "RAMP", WF_RAMP, 1.0E-6, 0.5E+6, 1.0E-6, WAVEFORM_DEFAULT }, + { "PULSE", WF_PULSE, 1.0E-6, 1.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE }, + { "USER", WF_ARB, 1.0E-6, 1.0E+7, 1.0E-6, WAVEFORM_DEFAULT }, + { "NOISE", WF_NOISE, 3.0E+7, 3.0E+7, 0.0E-0, WFO_AMPLITUDE | WFO_OFFSET }, + { "DC", WF_DC, 0.0E-0, 0.0E+0, 0.0E-0, WFO_OFFSET }, +}; + +static const struct channel_spec dg1032z_channels[] = { + { "CH1", ARRAY_AND_SIZE(dg1032z_waveforms) }, + { "CH2", ARRAY_AND_SIZE(dg1032z_waveforms) }, +}; + +static const struct waveform_spec dg1062z_waveforms[] = { + { "SIN", WF_SINE, 1.0E-6, 6.0E+7, 1.0E-6, WAVEFORM_DEFAULT }, + { "SQU", WF_SQUARE, 1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE }, + { "RAMP", WF_RAMP, 1.0E-6, 1.0E+6, 1.0E-6, WAVEFORM_DEFAULT }, + { "PULSE", WF_PULSE, 1.0E-6, 2.5E+7, 1.0E-6, WAVEFORM_DEFAULT | WFO_DUTY_CYCLE }, + { "USER", WF_ARB, 1.0E-6, 2.0E+7, 1.0E-6, WAVEFORM_DEFAULT }, + { "NOISE", WF_NOISE, 6.0E+7, 6.0E+7, 0.0E-0, WFO_AMPLITUDE | WFO_OFFSET }, + { "DC", WF_DC, 0.0E-0, 0.0E+0, 0.0E-0, WFO_OFFSET }, +}; + +static const struct channel_spec dg1062z_channels[] = { + { "CH1", ARRAY_AND_SIZE(dg1062z_waveforms) }, + { "CH2", ARRAY_AND_SIZE(dg1062z_waveforms) }, +}; + +static const struct scpi_command cmdset_dg1000z[] = { + { PSG_CMD_SETUP_LOCAL, "SYST:KLOC:STATE OFF", }, +/* { PSG_CMD_SELECT_CHANNEL, "SYST:CHAN:CUR CH%s", }, */ + { PSG_CMD_GET_CHANNEL, "SYST:CHAN:CUR?", }, + { PSG_CMD_GET_ENABLED, "OUTP%s:STATE?", }, + { PSG_CMD_SET_ENABLE, "OUTP%s:STATE ON", }, + { PSG_CMD_SET_DISABLE, "OUTP%s:STATE OFF", }, + { PSG_CMD_GET_SOURCE, "SOUR%s:APPL?", }, + { PSG_CMD_SET_SOURCE, "SOUR%s:APPL:%s", }, + { PSG_CMD_GET_FREQUENCY, "SOUR%s:FREQ?", }, + { PSG_CMD_SET_FREQUENCY, "SOUR%s:FREQ %f", }, + { PSG_CMD_GET_AMPLITUDE, "SOUR%s:VOLT?", }, + { PSG_CMD_SET_AMPLITUDE, "SOUR%s:VOLT %f", }, + { PSG_CMD_GET_OFFSET, "SOUR%s:VOLT:OFFS?", }, + { PSG_CMD_SET_OFFSET, "SOUR%s:VOLT:OFFS %f", }, + { PSG_CMD_GET_PHASE, "SOUR%s:PHAS?", }, + { PSG_CMD_SET_PHASE, "SOUR%s:PHAS %f", }, + { PSG_CMD_GET_DCYCL_PULSE, "SOUR%s:FUNC:PULS:DCYC?", }, + { PSG_CMD_SET_DCYCL_PULSE, "SOUR%s:FUNC:PULS:DCYC %f", }, + { PSG_CMD_GET_DCYCL_SQUARE, "SOUR%s:FUNC:SQU:DCYC?", }, + { PSG_CMD_SET_DCYCL_SQUARE, "SOUR%s:FUNC:SQU:DCYC %f", }, + { PSG_CMD_COUNTER_GET_ENABLED, "COUN:STAT?", }, + { PSG_CMD_COUNTER_SET_ENABLE, "COUN:STAT ON", }, + { PSG_CMD_COUNTER_SET_DISABLE, "COUN:STAT OFF", }, + { PSG_CMD_COUNTER_MEASURE, "COUN:MEAS?", }, + ALL_ZERO +}; + +static const struct device_spec device_models[] = { + { "Rigol Technologies", "DG1022Z", + ARRAY_AND_SIZE(dg1000z_devopts), + ARRAY_AND_SIZE(dg1000z_devopts_cg), + ARRAY_AND_SIZE(dg1022z_channels), + cmdset_dg1000z, + }, + { "Rigol Technologies", "DG1032Z", + ARRAY_AND_SIZE(dg1000z_devopts), + ARRAY_AND_SIZE(dg1000z_devopts_cg), + ARRAY_AND_SIZE(dg1032z_channels), + cmdset_dg1000z, + }, + { "Rigol Technologies", "DG1062Z", + ARRAY_AND_SIZE(dg1000z_devopts), + ARRAY_AND_SIZE(dg1000z_devopts_cg), + ARRAY_AND_SIZE(dg1062z_channels), + cmdset_dg1000z, + }, +}; + +static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi) { - struct drv_context *drvc; - GSList *devices; + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_scpi_hw_info *hw_info; + const struct device_spec *device; + const struct scpi_command *cmdset; + struct sr_channel *ch; + struct sr_channel_group *cg; + const char *command; + unsigned int i, ch_idx; + char tmp[16]; + + sdi = NULL; + devc = NULL; + hw_info = NULL; + + if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) + goto error; + + device = NULL; + for (i = 0; i < ARRAY_SIZE(device_models); i++) { + if (g_ascii_strcasecmp(hw_info->manufacturer, + device_models[i].vendor) != 0) + continue; + if (g_ascii_strcasecmp(hw_info->model, + device_models[i].model) != 0) + continue; + device = &device_models[i]; + cmdset = device_models[i].cmdset; + break; + } + if (!device) + goto error; + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->vendor = g_strdup(hw_info->manufacturer); + sdi->model = g_strdup(hw_info->model); + sdi->version = g_strdup(hw_info->firmware_version); + sdi->serial_num = g_strdup(hw_info->serial_number); + sdi->conn = scpi; + sdi->driver = &rigol_dg_driver_info; + sdi->inst_type = SR_INST_SCPI; + + devc = g_malloc0(sizeof(struct dev_context)); + devc->cmdset = cmdset; + devc->device = device; + devc->ch_status = g_malloc0(sizeof(struct channel_status) * + (device->num_channels + 1)); + sr_sw_limits_init(&devc->limits); + sdi->priv = devc; + + /* Create channel group and channel for each device channel. */ + ch_idx = 0; + for (i = 0; i < device->num_channels; i++) { + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, + device->channels[i].name); + cg = g_malloc0(sizeof(struct sr_channel_group)); + snprintf(tmp, sizeof(tmp), "%u", i + 1); + cg->name = g_strdup(tmp); + cg->channels = g_slist_append(cg->channels, ch); + + sdi->channel_groups = g_slist_append(sdi->channel_groups, cg); + } - (void)options; + /* Create channels for the frequency counter output. */ + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "FREQ1"); + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "PERIOD1"); + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "DUTY1"); + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, TRUE, "WIDTH1"); + + /* Put device back to "local" mode, in case only a scan was done... */ + command = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_SETUP_LOCAL); + if (command && *command) { + sr_scpi_get_opc(scpi); + sr_scpi_send(scpi, command); + } + + sr_scpi_hw_info_free(hw_info); - devices = NULL; - drvc = di->context; - drvc->instances = NULL; + return sdi; - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ +error: + sr_scpi_hw_info_free(hw_info); + g_free(devc); + sr_dev_inst_free(sdi); - return devices; + return NULL; } -static int dev_open(struct sr_dev_inst *sdi) +static GSList *scan(struct sr_dev_driver *di, GSList *options) { - (void)sdi; - - /* TODO: get handle from sdi->conn and open it. */ + return sr_scpi_scan(di->context, options, probe_device); +} - return SR_OK; +static int dev_open(struct sr_dev_inst *sdi) +{ + return sr_scpi_open(sdi->conn); } static int dev_close(struct sr_dev_inst *sdi) { - (void)sdi; - - /* TODO: get handle from sdi->conn and close it. */ + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + const char *command; + + devc = sdi->priv; + scpi = sdi->conn; + if (!scpi) + return SR_ERR_BUG; + + /* Put unit back to "local" mode. */ + command = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_SETUP_LOCAL); + if (command && *command) { + sr_scpi_get_opc(scpi); + sr_scpi_send(scpi, command); + } - return SR_OK; + return sr_scpi_close(sdi->conn); } static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + struct sr_channel *ch; + struct channel_status *ch_status; + const struct sr_key_info *kinfo; + uint32_t cmd; int ret; - (void)sdi; - (void)data; - (void)cg; + if (!sdi || !data) + return SR_ERR_ARG; + devc = sdi->priv; + scpi = sdi->conn; ret = SR_OK; - switch (key) { - /* TODO */ - default: - return SR_ERR_NA; + kinfo = sr_key_info_get(SR_KEY_CONFIG, key); + + if (!cg) { + switch (key) { + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + ret = sr_sw_limits_config_get(&devc->limits, key, data); + break; + default: + sr_dbg("%s: Unsupported key: %d (%s)", __func__, + (int)key, (kinfo ? kinfo->name : "unknown")); + ret = SR_ERR_NA; + break; + } + } else { + ch = cg->channels->data; + ch_status = &devc->ch_status[ch->index]; + + switch (key) { + case SR_CONF_ENABLED: + sr_scpi_get_opc(scpi); + ret = sr_scpi_cmd_resp(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, data, + G_VARIANT_TYPE_BOOLEAN, PSG_CMD_GET_ENABLED, + cg->name); + break; + case SR_CONF_PATTERN_MODE: + if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK) { + *data = g_variant_new_string( + rigol_dg_waveform_to_string( + ch_status->wf)); + } + break; + case SR_CONF_OUTPUT_FREQUENCY: + if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK) + *data = g_variant_new_double(ch_status->freq); + break; + case SR_CONF_AMPLITUDE: + if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK) + *data = g_variant_new_double(ch_status->ampl); + break; + case SR_CONF_OFFSET: + if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK) + *data = g_variant_new_double(ch_status->offset); + break; + case SR_CONF_PHASE: + if ((ret = rigol_dg_get_channel_state(sdi, cg)) == SR_OK) + *data = g_variant_new_double(ch_status->phase); + break; + case SR_CONF_DUTY_CYCLE: + if (ch_status->wf == WF_SQUARE) { + cmd = PSG_CMD_GET_DCYCL_SQUARE; + } else if (ch_status->wf == WF_PULSE) { + cmd = PSG_CMD_GET_DCYCL_PULSE; + } else { + ret = SR_ERR_NA; + break; + } + sr_scpi_get_opc(scpi); + ret = sr_scpi_cmd_resp(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, data, + G_VARIANT_TYPE_DOUBLE, cmd, cg->name); + break; + default: + sr_dbg("%s: Unsupported (cg) key: %d (%s)", __func__, + (int)key, (kinfo ? kinfo->name : "unknown")); + ret = SR_ERR_NA; + break; + } } return ret; @@ -79,17 +361,128 @@ static int config_get(uint32_t key, GVariant **data, static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + struct sr_channel *ch; + const struct channel_spec *ch_spec; + struct channel_status *ch_status; + const struct sr_key_info *kinfo; int ret; + uint32_t cmd; + const char *mode, *mode_name, *new_mode; + unsigned int i; - (void)sdi; - (void)data; - (void)cg; + if (!data || !sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + scpi = sdi->conn; + kinfo = sr_key_info_get(SR_KEY_CONFIG, key); ret = SR_OK; - switch (key) { - /* TODO */ - default: - ret = SR_ERR_NA; + + if (!cg) { + switch (key) { + case SR_CONF_LIMIT_MSEC: + case SR_CONF_LIMIT_SAMPLES: + ret = sr_sw_limits_config_set(&devc->limits, key, data); + break; + default: + sr_dbg("%s: Unsupported key: %d (%s)", __func__, + (int)key, (kinfo ? kinfo->name : "unknown")); + ret = SR_ERR_NA; + break; + } + } else { + ch = cg->channels->data; + ch_spec = &devc->device->channels[ch->index]; + ch_status = &devc->ch_status[ch->index]; + + if ((ret = rigol_dg_get_channel_state(sdi, cg)) != SR_OK) + return ret; + sr_scpi_get_opc(scpi); + + switch (key) { + case SR_CONF_ENABLED: + if (g_variant_get_boolean(data)) + cmd = PSG_CMD_SET_ENABLE; + else + cmd = PSG_CMD_SET_DISABLE; + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, cmd, cg->name); + break; + case SR_CONF_PATTERN_MODE: + ret = SR_ERR_NA; + new_mode = NULL; + mode = g_variant_get_string(data, NULL); + for (i = 0; i < ch_spec->num_waveforms; i++) { + mode_name = rigol_dg_waveform_to_string( + ch_spec->waveforms[i].waveform); + if (g_ascii_strncasecmp(mode, mode_name, + strlen(mode_name)) == 0) + new_mode = ch_spec->waveforms[i].name; + } + if (new_mode) + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, + PSG_CMD_SET_SOURCE, cg->name, new_mode); + break; + case SR_CONF_OUTPUT_FREQUENCY: + ret = SR_ERR_NA; + if (!(ch_status->wf_spec->opts & WFO_FREQUENCY)) + break; + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, + PSG_CMD_SET_FREQUENCY, cg->name, + g_variant_get_double(data)); + break; + case SR_CONF_AMPLITUDE: + ret = SR_ERR_NA; + if (!(ch_status->wf_spec->opts & WFO_AMPLITUDE)) + break; + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, + PSG_CMD_SET_AMPLITUDE, cg->name, + g_variant_get_double(data)); + break; + case SR_CONF_OFFSET: + ret = SR_ERR_NA; + if (!(ch_status->wf_spec->opts & WFO_OFFSET)) + break; + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, + PSG_CMD_SET_OFFSET, cg->name, + g_variant_get_double(data)); + break; + case SR_CONF_PHASE: + ret = SR_ERR_NA; + if (!(ch_status->wf_spec->opts & WFO_PHASE)) + break; + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, + PSG_CMD_SET_PHASE, cg->name, + g_variant_get_double(data)); + break; + case SR_CONF_DUTY_CYCLE: + ret = SR_ERR_NA; + if (!(ch_status->wf_spec->opts & WFO_DUTY_CYCLE)) + break; + if (ch_status->wf == WF_SQUARE) + cmd = PSG_CMD_SET_DCYCL_SQUARE; + else if (ch_status->wf == WF_PULSE) + cmd = PSG_CMD_SET_DCYCL_PULSE; + else + break; + ret = sr_scpi_cmd(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, + cmd, cg->name, g_variant_get_double(data)); + break; + default: + sr_dbg("%s: Unsupported key: %d (%s)", __func__, + (int)key, (kinfo ? kinfo->name : "unknown")); + ret = SR_ERR_NA; + break; + } } return ret; @@ -98,44 +491,169 @@ static int config_set(uint32_t key, GVariant *data, static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + struct dev_context *devc; + struct sr_channel *ch; + const struct channel_spec *ch_spec; + const struct waveform_spec *wf_spec; + struct channel_status *ch_status; + GVariantBuilder *b; + unsigned int i; + double fspec[3]; + + devc = NULL; + if (sdi) + devc = sdi->priv; + + if (!cg) { + switch (key) { + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + return std_opts_config_list(key, data, sdi, cg, + ARRAY_AND_SIZE(scanopts), + ARRAY_AND_SIZE(drvopts), + (devc && devc->device) ? devc->device->devopts : NULL, + (devc && devc->device) ? devc->device->num_devopts : 0); + default: + return SR_ERR_NA; + } + } else { + if (!devc || !devc->device) + return SR_ERR_ARG; + ch = cg->channels->data; + ch_spec = &devc->device->channels[ch->index]; + ch_status = &devc->ch_status[ch->index]; + + switch(key) { + case SR_CONF_DEVICE_OPTIONS: + *data = std_gvar_array_u32(devc->device->devopts_cg, + devc->device->num_devopts_cg); + break; + case SR_CONF_PATTERN_MODE: + b = g_variant_builder_new(G_VARIANT_TYPE("as")); + for (i = 0; i < ch_spec->num_waveforms; i++) { + g_variant_builder_add(b, "s", + rigol_dg_waveform_to_string( + ch_spec->waveforms[i].waveform)); + } + *data = g_variant_new("as", b); + g_variant_builder_unref(b); + break; + case SR_CONF_OUTPUT_FREQUENCY: + /* + * Frequency range depends on the currently active + * wave form. + */ + if (rigol_dg_get_channel_state(sdi, cg) != SR_OK) + return SR_ERR_NA; + wf_spec = rigol_dg_get_waveform_spec(ch_spec, + ch_status->wf); + if (!wf_spec) + return SR_ERR_BUG; + fspec[0] = wf_spec->freq_min; + fspec[1] = wf_spec->freq_max; + fspec[2] = wf_spec->freq_step; + *data = std_gvar_min_max_step_array(fspec); + break; + case SR_CONF_PHASE: + *data = std_gvar_min_max_step_array(phase_min_max_step); + break; + default: + return SR_ERR_NA; + } + } + + return SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + const char *cmd; + char *response; int ret; - (void)sdi; - (void)data; - (void)cg; + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; + scpi = sdi->conn; + response = NULL; ret = SR_OK; - switch (key) { - /* TODO */ - default: - return SR_ERR_NA; + + if (!scpi) + return SR_ERR_BUG; + + cmd = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_COUNTER_GET_ENABLED); + if (cmd && *cmd) { + /* Check if counter is currently enabled. */ + ret = sr_scpi_get_string(scpi, cmd, &response); + if (ret != SR_OK) + return SR_ERR_NA; + if (g_ascii_strncasecmp(response, "RUN", strlen("RUN")) == 0) + devc->counter_enabled = TRUE; + else + devc->counter_enabled = FALSE; + + if (!devc->counter_enabled) { + /* Enable counter if it was not already running. */ + cmd = sr_scpi_cmd_get(devc->cmdset, + PSG_CMD_COUNTER_SET_ENABLE); + if (!cmd) + return SR_ERR_BUG; + sr_scpi_get_opc(scpi); + ret = sr_scpi_send(scpi, cmd); + } + } + + if (ret == SR_OK) { + sr_sw_limits_acquisition_start(&devc->limits); + ret = std_session_send_df_header(sdi); + if (ret == SR_OK) { + ret = sr_scpi_source_add(sdi->session, scpi, + G_IO_IN, 100, rigol_dg_receive_data, + (void *)sdi); + } } + g_free(response); + return ret; } -static int dev_acquisition_start(const struct sr_dev_inst *sdi) +static int dev_acquisition_stop(struct sr_dev_inst *sdi) { - /* TODO: configure hardware, reset acquisition state, set up - * callbacks and send header packet. */ + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + const char *cmd; + int ret; - (void)sdi; + if (!sdi) + return SR_ERR_ARG; - return SR_OK; -} + devc = sdi->priv; + scpi = sdi->conn; + ret = SR_OK; -static int dev_acquisition_stop(struct sr_dev_inst *sdi) -{ - /* TODO: stop acquisition. */ + cmd = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_COUNTER_SET_DISABLE); + if (cmd && *cmd && !devc->counter_enabled) { + /* + * If counter was not running when acquisiton started, + * turn it off now... + */ + sr_scpi_get_opc(scpi); + ret = sr_scpi_send(scpi, cmd); + } - (void)sdi; + sr_scpi_source_remove(sdi->session, scpi); + std_session_send_df_end(sdi); - return SR_OK; + return ret; } static struct sr_dev_driver rigol_dg_driver_info = { .name = "rigol-dg", - .longname = "rigol-dg", + .longname = "Rigol DG Series", .api_version = 1, .init = std_init, .cleanup = std_cleanup, diff --git a/src/hardware/rigol-dg/protocol.c b/src/hardware/rigol-dg/protocol.c index 01794280..47bf9446 100644 --- a/src/hardware/rigol-dg/protocol.c +++ b/src/hardware/rigol-dg/protocol.c @@ -18,24 +18,260 @@ */ #include +#include +#include "scpi.h" #include "protocol.h" +SR_PRIV const char *rigol_dg_waveform_to_string(enum waveform_type type) +{ + switch (type) { + case WF_DC: + return "DC"; + case WF_SINE: + return "Sine"; + case WF_SQUARE: + return "Square"; + case WF_RAMP: + return "Ramp"; + case WF_PULSE: + return "Pulse"; + case WF_NOISE: + return "Noise"; + case WF_ARB: + return "Arb"; + } + + return "Unknown"; +} + +SR_PRIV const struct waveform_spec *rigol_dg_get_waveform_spec( + const struct channel_spec *ch, enum waveform_type wf) +{ + const struct waveform_spec *spec; + unsigned int i; + + spec = NULL; + for (i = 0; i < ch->num_waveforms; i++) { + if (ch->waveforms[i].waveform == wf) { + spec = &ch->waveforms[i]; + break; + } + } + + return spec; +} + +SR_PRIV int rigol_dg_get_channel_state(const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg) +{ + struct dev_context *devc; + struct sr_scpi_dev_inst *scpi; + struct sr_channel *ch; + struct channel_status *ch_status; + const char *command; + GVariant *data; + gchar *response, **params; + const gchar *s; + enum waveform_type wf; + double freq, ampl, offset, phase; + int ret; + + devc = sdi->priv; + scpi = sdi->conn; + data = NULL; + params = NULL; + response = NULL; + ret = SR_ERR_NA; + + if (!sdi || !cg) + return SR_ERR_BUG; + + ch = cg->channels->data; + ch_status = &devc->ch_status[ch->index]; + + command = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_GET_SOURCE); + if (command && *command) { + sr_scpi_get_opc(scpi); + ret = sr_scpi_cmd_resp(sdi, devc->cmdset, + PSG_CMD_SELECT_CHANNEL, cg->name, &data, + G_VARIANT_TYPE_STRING, PSG_CMD_GET_SOURCE, cg->name); + if (ret != SR_OK) + goto done; + response = g_variant_dup_string(data, NULL); + g_strstrip(response); + s = sr_scpi_unquote_string(response); + sr_spew("Channel state: '%s'", s); + params = g_strsplit(s, ",", 0); + if (!params[0]) + goto done; + + /* First parameter is the waveform type */ + if (!(s = params[0])) + goto done; + if (g_ascii_strncasecmp(s, "SIN", strlen("SIN")) == 0) + wf = WF_SINE; + else if (g_ascii_strncasecmp(s, "SQU", strlen("SQU")) == 0) + wf = WF_SQUARE; + else if (g_ascii_strncasecmp(s, "RAMP", strlen("RAMP")) == 0) + wf = WF_RAMP; + else if (g_ascii_strncasecmp(s, "PULSE", strlen("PULSE")) == 0) + wf = WF_PULSE; + else if (g_ascii_strncasecmp(s, "NOISE", strlen("NOISE")) == 0) + wf = WF_NOISE; + else if (g_ascii_strncasecmp(s, "USER", strlen("USER")) == 0) + wf = WF_ARB; + else if (g_ascii_strncasecmp(s, "DC", strlen("DC")) == 0) + wf = WF_DC; + else + goto done; + ch_status->wf = wf; + ch_status->wf_spec = rigol_dg_get_waveform_spec( + &devc->device->channels[ch->index], wf); + + /* Second parameter if the frequency (or "DEF" if not applicable) */ + if (!(s = params[1])) + goto done; + freq = g_ascii_strtod(s, NULL); + ch_status->freq = freq; + + /* Third parameter if the amplitude (or "DEF" if not applicable) */ + if (!(s = params[2])) + goto done; + ampl = g_ascii_strtod(s, NULL); + ch_status->ampl = ampl; + + /* Fourth parameter if the offset (or "DEF" if not applicable) */ + if (!(s = params[3])) + goto done; + offset = g_ascii_strtod(s, NULL); + ch_status->offset = offset; + + /* Fifth parameter if the phase (or "DEF" if not applicable) */ + if (!(s = params[4])) + goto done; + phase = g_ascii_strtod(s, NULL); + ch_status->phase = phase; + + ret = SR_OK; + } + +done: + g_variant_unref(data); + g_free(response); + g_strfreev(params); + return ret; +} + +static void rigol_dg_send_channel_value(const struct sr_dev_inst *sdi, + struct sr_channel *ch, double value, enum sr_mq mq, + enum sr_unit unit, int digits) +{ + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + double val; + + val = value; + sr_analog_init(&analog, &encoding, &meaning, &spec, digits); + analog.meaning->channels = g_slist_append(NULL, ch); + analog.num_samples = 1; + analog.data = &val; + analog.encoding->unitsize = sizeof(val); + analog.encoding->is_float = TRUE; + analog.encoding->digits = digits; + analog.meaning->mq = mq; + analog.meaning->unit = unit; + + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(sdi, &packet); + g_slist_free(analog.meaning->channels); +} + SR_PRIV int rigol_dg_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; + const char *cmd, *s; + char *response, **params; + double meas[5]; + GSList *l; + int i, start_idx, ret; (void)fd; + (void)revents; + response = NULL; + params = NULL; - if (!(sdi = cb_data)) + sdi = cb_data; + if (!sdi) + return TRUE; + scpi = sdi->conn; + devc = sdi->priv; + if (!scpi || !devc) return TRUE; - if (!(devc = sdi->priv)) + cmd = sr_scpi_cmd_get(devc->cmdset, PSG_CMD_COUNTER_MEASURE); + if (!cmd || !*cmd) return TRUE; - if (revents == G_IO_IN) { - /* TODO */ + sr_scpi_get_opc(scpi); + ret = sr_scpi_get_string(scpi, cmd, &response); + if (ret != SR_OK) { + sr_info("Error getting measurement from counter: %d", ret); + sr_dev_acquisition_stop(sdi); + return TRUE; + } + g_strstrip(response); + + /* + * Parse measurement string: + * frequency, period, duty cycle, width+, width- + */ + params = g_strsplit(response, ",", 0); + for (i = 0; i < 5; i++) { + if (!(s = params[i])) + goto done; + meas[i] = g_ascii_strtod(s, NULL); } + sr_spew("%s: freq=%.10E, period=%.10E, duty=%.10E, width+=%.10E," + "width-=%.10E", __func__, + meas[0], meas[1], meas[2], meas[3], meas[4]); + + std_session_send_df_frame_begin(sdi); + start_idx = devc->device->num_channels; + + /* Frequency */ + l = g_slist_nth(sdi->channels, start_idx++); + rigol_dg_send_channel_value(sdi, l->data, meas[0], SR_MQ_FREQUENCY, + SR_UNIT_HERTZ, 10); + + /* Period */ + l = g_slist_nth(sdi->channels, start_idx++); + rigol_dg_send_channel_value(sdi, l->data, meas[1], SR_MQ_TIME, + SR_UNIT_SECOND, 10); + + /* Duty Cycle */ + l = g_slist_nth(sdi->channels, start_idx++); + rigol_dg_send_channel_value(sdi, l->data, meas[2], SR_MQ_DUTY_CYCLE, + SR_UNIT_PERCENTAGE, 3); + + /* Pulse Width */ + l = g_slist_nth(sdi->channels, start_idx++); + rigol_dg_send_channel_value(sdi, l->data, meas[3], SR_MQ_PULSE_WIDTH, + SR_UNIT_SECOND, 10); + + std_session_send_df_frame_end(sdi); + sr_sw_limits_update_samples_read(&devc->limits, 1); + + if (sr_sw_limits_check(&devc->limits)) + sr_dev_acquisition_stop(sdi); +done: + g_free(response); + g_strfreev(params); return TRUE; } diff --git a/src/hardware/rigol-dg/protocol.h b/src/hardware/rigol-dg/protocol.h index db8b463a..bff7c208 100644 --- a/src/hardware/rigol-dg/protocol.h +++ b/src/hardware/rigol-dg/protocol.h @@ -27,9 +27,101 @@ #define LOG_PREFIX "rigol-dg" +enum psg_commands { + PSG_CMD_SETUP_REMOTE, + PSG_CMD_SETUP_LOCAL, + PSG_CMD_SELECT_CHANNEL, + PSG_CMD_GET_CHANNEL, + PSG_CMD_GET_ENABLED, + PSG_CMD_SET_ENABLE, + PSG_CMD_SET_DISABLE, + PSG_CMD_GET_SOURCE, + PSG_CMD_SET_SOURCE, + PSG_CMD_SET_FREQUENCY, + PSG_CMD_GET_FREQUENCY, + PSG_CMD_SET_AMPLITUDE, + PSG_CMD_GET_AMPLITUDE, + PSG_CMD_GET_OFFSET, + PSG_CMD_SET_OFFSET, + PSG_CMD_GET_PHASE, + PSG_CMD_SET_PHASE, + PSG_CMD_GET_DCYCL_PULSE, + PSG_CMD_SET_DCYCL_PULSE, + PSG_CMD_GET_DCYCL_SQUARE, + PSG_CMD_SET_DCYCL_SQUARE, + PSG_CMD_COUNTER_GET_ENABLED, + PSG_CMD_COUNTER_SET_ENABLE, + PSG_CMD_COUNTER_SET_DISABLE, + PSG_CMD_COUNTER_MEASURE, +}; + +enum waveform_type { + WF_DC = 0, + WF_SINE, + WF_SQUARE, + WF_RAMP, + WF_PULSE, + WF_NOISE, + WF_ARB, +}; + +enum waveform_options { + WFO_FREQUENCY = 1, + WFO_AMPLITUDE = 2, + WFO_OFFSET = 4, + WFO_PHASE = 8, + WFO_DUTY_CYCLE = 16, +}; + +struct waveform_spec { + const char *name; + enum waveform_type waveform; + double freq_min; + double freq_max; + double freq_step; + uint32_t opts; +}; + +struct channel_spec { + const char *name; + const struct waveform_spec *waveforms; + uint32_t num_waveforms; +}; + +struct channel_status { + enum waveform_type wf; + const struct waveform_spec *wf_spec; + double freq; + double ampl; + double offset; + double phase; +}; + +struct device_spec { + const char *vendor; + const char *model; + const uint32_t *devopts; + const uint32_t num_devopts; + const uint32_t *devopts_cg; + const uint32_t num_devopts_cg; + const struct channel_spec *channels; + const uint32_t num_channels; + const struct scpi_command *cmdset; +}; + struct dev_context { + const struct scpi_command *cmdset; + const struct device_spec *device; + struct channel_status *ch_status; + struct sr_sw_limits limits; + gboolean counter_enabled; }; +SR_PRIV const char *rigol_dg_waveform_to_string(enum waveform_type type); +SR_PRIV const struct waveform_spec *rigol_dg_get_waveform_spec( + const struct channel_spec *ch, enum waveform_type wf); +SR_PRIV int rigol_dg_get_channel_state(const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg); SR_PRIV int rigol_dg_receive_data(int fd, int revents, void *cb_data); #endif