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 ]
This commit is contained in:
Timo Kokkonen 2020-09-05 22:06:30 -07:00 committed by Gerhard Sittig
parent 068db0fbe6
commit 02feeb30b9
4 changed files with 906 additions and 59 deletions

View File

@ -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

View File

@ -18,59 +18,341 @@
*/
#include <config.h>
#include <string.h>
#include "scpi.h"
#include "protocol.h"
static struct sr_dev_driver rigol_dg_driver_info;
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 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);
}
/* 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);
return sdi;
error:
sr_scpi_hw_info_free(hw_info);
g_free(devc);
sr_dev_inst_free(sdi);
return NULL;
}
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
struct drv_context *drvc;
GSList *devices;
(void)options;
devices = NULL;
drvc = di->context;
drvc->instances = NULL;
/* TODO: scan for devices, either based on a SR_CONF_CONN option
* or on a USB scan. */
return devices;
return sr_scpi_scan(di->context, options, probe_device);
}
static int dev_open(struct sr_dev_inst *sdi)
{
(void)sdi;
/* TODO: get handle from sdi->conn and open it. */
return SR_OK;
return sr_scpi_open(sdi->conn);
}
static int dev_close(struct sr_dev_inst *sdi)
{
(void)sdi;
struct dev_context *devc;
struct sr_scpi_dev_inst *scpi;
const char *command;
/* TODO: get handle from sdi->conn and close it. */
devc = sdi->priv;
scpi = sdi->conn;
if (!scpi)
return SR_ERR_BUG;
return SR_OK;
/* 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_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)
{
int ret;
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];
(void)sdi;
(void)data;
(void)cg;
devc = NULL;
if (sdi)
devc = sdi->priv;
ret = SR_OK;
switch (key) {
/* TODO */
default:
return SR_ERR_NA;
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 ret;
return SR_OK;
}
static int dev_acquisition_start(const 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;
char *response;
int ret;
(void)sdi;
if (!sdi)
return SR_ERR_ARG;
return SR_OK;
devc = sdi->priv;
scpi = sdi->conn;
response = NULL;
ret = SR_OK;
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_stop(struct sr_dev_inst *sdi)
{
/* TODO: stop acquisition. */
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;
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);
}
sr_scpi_source_remove(sdi->session, scpi);
std_session_send_df_end(sdi);
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,

View File

@ -18,24 +18,260 @@
*/
#include <config.h>
#include <string.h>
#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;
}

View File

@ -27,9 +27,101 @@
#define LOG_PREFIX "rigol-dg"
struct dev_context {
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