Hi everybody,
I cobbled together a first version of hardware support for the Rigol
DS2xx2 series oscilloscopes (models DS2072, DS2102, DS2202).
It is heavily based on the rigol-ds1xx2 code. Because of this it shares
its quirks.
Grabbing the displayed samples (1400 per channel) works when the scope
is running, when stopped this only works if the horizontal settings
haven't been changed.
Enjoy,
MGri
diff -urN libsigrok.org/configure.ac libsigrok/configure.ac
--- libsigrok.org/configure.ac 2013-10-18 22:18:36.000000000 +0200
+++ libsigrok/configure.ac 2013-10-20 19:09:30.884799042 +0200
@@ -184,6 +184,11 @@
[HW_RIGOL_DS1XX2="$enableval"],
[HW_RIGOL_DS1XX2=$HW_ENABLED_DEFAULT])
+AC_ARG_ENABLE(rigol-ds2xx2, AC_HELP_STRING([--enable-rigol-ds2xx2],
+ [enable Rigol DS2xx2 support [default=yes]]),
+ [HW_RIGOL_DS2XX2="$enableval"],
+ [HW_RIGOL_DS2XX2=$HW_ENABLED_DEFAULT])
+
AC_ARG_ENABLE(saleae-logic16, AC_HELP_STRING([--enable-saleae-logic16],
[enable Saleae Logic16 support [default=yes]]),
[HW_SALEAE_LOGIC16="$enableval"],
@@ -417,6 +422,11 @@
AC_DEFINE(HAVE_HW_RIGOL_DS1XX2, 1, [Rigol DS1xx2 support])
fi
+AM_CONDITIONAL(HW_RIGOL_DS2XX2, test x$HW_RIGOL_DS2XX2 = xyes)
+if test "x$HW_RIGOL_DS2XX2" = "xyes"; then
+ AC_DEFINE(HAVE_HW_RIGOL_DS2XX2, 1, [Rigol DS2xx2 support])
+fi
+
AM_CONDITIONAL(HW_SERIAL_DMM, test x$HW_SERIAL_DMM = xyes)
if test "x$HW_SERIAL_DMM" = "xyes"; then
AC_DEFINE(HAVE_HW_SERIAL_DMM, 1, [Serial DMM support])
@@ -502,6 +512,7 @@
hardware/lascar-el-usb/Makefile
hardware/mic-985xx/Makefile
hardware/rigol-ds1xx2/Makefile
+ hardware/rigol-ds2xx2/Makefile
hardware/saleae-logic16/Makefile
hardware/tondaj-sl-814/Makefile
hardware/victor-dmm/Makefile
@@ -570,6 +581,7 @@
echo " - mic-985xx....................... $HW_MIC_985XX"
echo " - openbench-logic-sniffer......... $HW_OLS"
echo " - rigol-ds1xx2.................... $HW_RIGOL_DS1XX2"
+echo " - rigol-ds2xx2.................... $HW_RIGOL_DS2XX2"
echo " - saleae-logic16.................. $HW_SALEAE_LOGIC16"
echo " - serial-dmm...................... $HW_SERIAL_DMM"
echo " - tondaj-sl-814................... $HW_TONDAJ_SL_814"
diff -urN libsigrok.org/hardware/Makefile.am libsigrok/hardware/Makefile.am
--- libsigrok.org/hardware/Makefile.am 2013-09-14 01:11:11.000000000 +0200
+++ libsigrok/hardware/Makefile.am 2013-10-19 01:14:35.089165697 +0200
@@ -40,6 +40,7 @@
mic-985xx \
openbench-logic-sniffer \
rigol-ds1xx2 \
+ rigol-ds2xx2 \
saleae-logic16 \
serial-dmm \
tondaj-sl-814 \
@@ -135,6 +136,10 @@
libsigrokhardware_la_LIBADD += rigol-ds1xx2/libsigrok_hw_rigol_ds1xx2.la
endif
+if HW_RIGOL_DS2XX2
+libsigrokhardware_la_LIBADD += rigol-ds2xx2/libsigrok_hw_rigol_ds2xx2.la
+endif
+
if HW_SALEAE_LOGIC16
libsigrokhardware_la_LIBADD += saleae-logic16/libsigrok_hw_saleae_logic16.la
endif
diff -urN libsigrok.org/hardware/rigol-ds2xx2/api.c
libsigrok/hardware/rigol-ds2xx2/api.c
--- libsigrok.org/hardware/rigol-ds2xx2/api.c 1970-01-01 01:00:00.000000000
+0100
+++ libsigrok/hardware/rigol-ds2xx2/api.c 2013-10-20 16:30:44.966487179
+0200
@@ -0,0 +1,626 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <[email protected]>
+ * Copyright (C) 2013 Bert Vermeulen <[email protected]>
+ * Copyright (C) 2013 Mathias Grimmberger <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Based on api.c from Rigol DS1xx2 driver. */
+
+static const int32_t hwopts[] = {
+ SR_CONF_CONN,
+};
+
+static const int32_t hwcaps[] = {
+ SR_CONF_OSCILLOSCOPE,
+ SR_CONF_TIMEBASE,
+ SR_CONF_TRIGGER_SOURCE,
+ SR_CONF_TRIGGER_SLOPE,
+ SR_CONF_HORIZ_TRIGGERPOS,
+ SR_CONF_VDIV,
+ SR_CONF_COUPLING,
+ SR_CONF_NUM_TIMEBASE,
+ SR_CONF_NUM_VDIV,
+};
+
+static const uint64_t timebases[][2] = {
+ /* nanoseconds */
+ { 2, 1000000000 },
+ { 5, 1000000000 },
+ { 10, 1000000000 },
+ { 20, 1000000000 },
+ { 50, 1000000000 },
+ { 100, 1000000000 },
+ { 500, 1000000000 },
+ /* microseconds */
+ { 1, 1000000 },
+ { 2, 1000000 },
+ { 5, 1000000 },
+ { 10, 1000000 },
+ { 20, 1000000 },
+ { 50, 1000000 },
+ { 100, 1000000 },
+ { 200, 1000000 },
+ { 500, 1000000 },
+ /* milliseconds */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 5, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* seconds */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+ { 20, 1 },
+ { 50, 1 },
+ { 100, 1 },
+ { 200, 1 },
+ { 500, 1 },
+/* { 1000, 1 }, Confuses other code? */
+};
+
+static const uint64_t vdivs[][2] = {
+ /* microvolts */
+ { 500, 1000000 },
+ /* millivolts */
+ { 1, 1000 },
+ { 2, 1000 },
+ { 5, 1000 },
+ { 10, 1000 },
+ { 20, 1000 },
+ { 50, 1000 },
+ { 100, 1000 },
+ { 200, 1000 },
+ { 500, 1000 },
+ /* volts */
+ { 1, 1 },
+ { 2, 1 },
+ { 5, 1 },
+ { 10, 1 },
+};
+
+#define NUM_TIMEBASE ARRAY_SIZE(timebases)
+#define NUM_VDIV ARRAY_SIZE(vdivs)
+
+static const char *trigger_sources[] = {
+ "CH1",
+ "CH2",
+ "EXT",
+ "AC Line",
+};
+
+static const char *coupling[] = {
+ "AC",
+ "DC",
+ "GND",
+};
+
+static const char *supported_models[] = {
+ "DS2072",
+ "DS2102",
+ "DS2202",
+};
+
+SR_PRIV struct sr_dev_driver rigol_ds2xx2_driver_info;
+static struct sr_dev_driver *di = &rigol_ds2xx2_driver_info;
+
+static void clear_helper(void *priv)
+{
+ struct dev_context *devc;
+
+ devc = priv;
+
+ g_free(devc->coupling[0]);
+ g_free(devc->coupling[1]);
+ g_free(devc->trigger_source);
+ g_free(devc->trigger_slope);
+}
+
+static int dev_clear(void)
+{
+ return std_dev_clear(di, clear_helper);
+}
+
+static int set_cfg(const struct sr_dev_inst *sdi, const char *format, ...)
+{
+ va_list args;
+ char buf[256];
+
+ va_start(args, format);
+ vsnprintf(buf, 255, format, args);
+ va_end(args);
+ if (rigol_ds2xx2_send(sdi, buf) != SR_OK)
+ return SR_ERR;
+
+ /* When setting a bunch of parameters in a row, the DS1052E scrambles
+ * some of them unless there is at least 100ms delay in between. */
+ /* Not sure whether this applies to DS2xx2 too - left in for now */
+ sr_spew("delay %dms", 100);
+ g_usleep(100000);
+
+ return SR_OK;
+}
+
+static int init(struct sr_context *sr_ctx)
+{
+ return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static int probe_port(const char *port, GSList **devices)
+{
+ struct dev_context *devc;
+ struct sr_dev_inst *sdi;
+ struct sr_serial_dev_inst *serial;
+ struct sr_probe *probe;
+ unsigned int i;
+ int len, num_tokens;
+ gboolean matched;
+ const char *manufacturer, *model, *version;
+ char buf[256];
+ gchar **tokens;
+
+ *devices = NULL;
+ if (!(serial = sr_serial_dev_inst_new(port, NULL)))
+ return SR_ERR_MALLOC;
+
+ if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+ return SR_ERR;
+ len = serial_write(serial, "*IDN?", 5);
+ len = serial_read(serial, buf, sizeof(buf));
+ if (serial_close(serial) != SR_OK)
+ return SR_ERR;
+
+ sr_serial_dev_inst_free(serial);
+
+ if (len == 0)
+ return SR_ERR_NA;
+
+ buf[len] = 0;
+ tokens = g_strsplit(buf, ",", 0);
+ sr_dbg("response: %s [%s]", port, buf);
+
+ for (num_tokens = 0; tokens[num_tokens] != NULL; num_tokens++);
+
+ if (num_tokens < 4) {
+ g_strfreev(tokens);
+ return SR_ERR_NA;
+ }
+
+ manufacturer = tokens[0];
+ model = tokens[1];
+ version = tokens[3];
+
+ if (strcasecmp(manufacturer, "Rigol Technologies")) {
+ g_strfreev(tokens);
+ return SR_ERR_NA;
+ }
+
+ matched = FALSE;
+ for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
+ if (!strcmp(model, supported_models[i])) {
+ matched = TRUE;
+ break;
+ }
+ }
+
+ if (!matched || !(sdi = sr_dev_inst_new(0, SR_ST_ACTIVE,
+ manufacturer, model, version))) {
+ g_strfreev(tokens);
+ return SR_ERR_NA;
+ }
+
+ g_strfreev(tokens);
+
+ if (!(sdi->conn = sr_serial_dev_inst_new(port, NULL)))
+ return SR_ERR_MALLOC;
+ sdi->driver = di;
+ sdi->inst_type = SR_INST_SERIAL;
+
+ if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
+ return SR_ERR_MALLOC;
+ devc->limit_frames = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (!(probe = sr_probe_new(i, SR_PROBE_ANALOG, TRUE,
+ i == 0 ? "CH1" : "CH2")))
+ return SR_ERR_MALLOC;
+ sdi->probes = g_slist_append(sdi->probes, probe);
+ }
+
+ sdi->priv = devc;
+
+ *devices = g_slist_append(NULL, sdi);
+
+ return SR_OK;
+}
+
+static GSList *scan(GSList *options)
+{
+ struct drv_context *drvc;
+ struct sr_config *src;
+ GSList *l, *devices;
+ GDir *dir;
+ int ret;
+ const gchar *dev_name;
+ gchar *port = NULL;
+
+ drvc = di->priv;
+
+ for (l = options; l; l = l->next) {
+ src = l->data;
+ if (src->key == SR_CONF_CONN) {
+ port = (char *)g_variant_get_string(src->data, NULL);
+ break;
+ }
+ }
+
+ devices = NULL;
+ if (port) {
+ if (probe_port(port, &devices) == SR_ERR_MALLOC)
+ return NULL;
+ } else {
+ if (!(dir = g_dir_open("/sys/class/usbmisc/", 0, NULL)))
+ if (!(dir = g_dir_open("/sys/class/usb/", 0, NULL)))
+ return NULL;
+ while ((dev_name = g_dir_read_name(dir))) {
+ if (strncmp(dev_name, "usbtmc", 6))
+ continue;
+ port = g_strconcat("/dev/", dev_name, NULL);
+ ret = probe_port(port, &devices);
+ g_free(port);
+ if (ret == SR_ERR_MALLOC) {
+ g_dir_close(dir);
+ return NULL;
+ }
+ }
+ g_dir_close(dir);
+ }
+
+ /* Tack a copy of the newly found devices onto the driver list. */
+ l = g_slist_copy(devices);
+ drvc->instances = g_slist_concat(drvc->instances, l);
+
+ return devices;
+}
+
+static GSList *dev_list(void)
+{
+ return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+
+ if (serial_open(sdi->conn, SERIAL_RDWR) != SR_OK)
+ return SR_ERR;
+
+ if (rigol_ds2xx2_get_dev_cfg(sdi) != SR_OK)
+ return SR_ERR;
+
+ sdi->status = SR_ST_ACTIVE;
+
+ return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+
+ serial = sdi->conn;
+ if (serial && serial->fd != -1) {
+ serial_close(serial);
+ sdi->status = SR_ST_INACTIVE;
+ }
+
+ return SR_OK;
+}
+
+static int cleanup(void)
+{
+ return dev_clear();
+}
+
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi)
+{
+
+ (void)sdi;
+
+ switch (id) {
+ case SR_CONF_NUM_TIMEBASE:
+ *data = g_variant_new_int32(NUM_TIMEBASE);
+ break;
+ case SR_CONF_NUM_VDIV:
+ *data = g_variant_new_int32(NUM_VDIV);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ uint64_t tmp_u64, p, q;
+ double t_dbl;
+ unsigned int i;
+ int ret;
+ const char *tmp_str;
+
+ devc = sdi->priv;
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ ret = SR_OK;
+ switch (id) {
+ case SR_CONF_LIMIT_FRAMES:
+ devc->limit_frames = g_variant_get_uint64(data);
+ break;
+ case SR_CONF_TRIGGER_SLOPE:
+ tmp_u64 = g_variant_get_uint64(data);
+ if (tmp_u64 != 0 && tmp_u64 != 1)
+ return SR_ERR;
+ g_free(devc->trigger_slope);
+ devc->trigger_slope = g_strdup(tmp_u64 ? "POS" : "NEG");
+ ret = set_cfg(sdi, ":TRIG:EDGE:SLOP %s", devc->trigger_slope);
+ break;
+ case SR_CONF_HORIZ_TRIGGERPOS:
+ t_dbl = g_variant_get_double(data);
+ if (t_dbl < 0.0 || t_dbl > 1.0)
+ return SR_ERR;
+ devc->horiz_triggerpos = t_dbl;
+ /* We have the trigger offset as a percentage of the frame, but
+ * need to express this in seconds. */
+ t_dbl = -(devc->horiz_triggerpos - 0.5) * devc->timebase *
NUM_TIMEBASE;
+ ret = set_cfg(sdi, ":TIM:OFFS %.6f", t_dbl);
+ break;
+ case SR_CONF_TIMEBASE:
+ g_variant_get(data, "(tt)", &p, &q);
+ for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+ if (timebases[i][0] == p && timebases[i][1] == q) {
+ devc->timebase = (float)p / q;
+ ret = set_cfg(sdi, ":TIM:SCAL %.9f",
devc->timebase);
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(timebases))
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ tmp_str = g_variant_get_string(data, NULL);
+ for (i = 0; i < ARRAY_SIZE(trigger_sources); i++) {
+ if (!strcmp(trigger_sources[i], tmp_str)) {
+ g_free(devc->trigger_source);
+ devc->trigger_source =
g_strdup(trigger_sources[i]);
+ if (!strcmp(devc->trigger_source, "AC Line"))
+ tmp_str = "ACL";
+ else if (!strcmp(devc->trigger_source, "CH1"))
+ tmp_str = "CHAN1";
+ else if (!strcmp(devc->trigger_source, "CH2"))
+ tmp_str = "CHAN2";
+ ret = set_cfg(sdi, ":TRIG:EDGE:SOUR %s",
tmp_str);
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(trigger_sources))
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_VDIV:
+ g_variant_get(data, "(tt)", &p, &q);
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+ if (vdivs[i][0] != p || vdivs[i][1] != q)
+ continue;
+ devc->vdiv[0] = devc->vdiv[1] = (float)p / q;
+ set_cfg(sdi, ":CHAN1:SCAL %.3f", devc->vdiv[0]);
+ ret = set_cfg(sdi, ":CHAN2:SCAL %.3f", devc->vdiv[1]);
+ break;
+ }
+ if (i == ARRAY_SIZE(vdivs))
+ ret = SR_ERR_ARG;
+ break;
+ case SR_CONF_COUPLING:
+ /* TODO: Not supporting coupling per channel yet. */
+ tmp_str = g_variant_get_string(data, NULL);
+ for (i = 0; i < ARRAY_SIZE(coupling); i++) {
+ if (!strcmp(tmp_str, coupling[i])) {
+ g_free(devc->coupling[0]);
+ g_free(devc->coupling[1]);
+ devc->coupling[0] = g_strdup(coupling[i]);
+ devc->coupling[1] = g_strdup(coupling[i]);
+ set_cfg(sdi, ":CHAN1:COUP %s",
devc->coupling[0]);
+ ret = set_cfg(sdi, ":CHAN2:COUP %s",
devc->coupling[1]);
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(coupling))
+ ret = SR_ERR_ARG;
+ break;
+ default:
+ ret = SR_ERR_NA;
+ break;
+ }
+
+ return ret;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi)
+{
+ GVariant *tuple, *rational[2];
+ GVariantBuilder gvb;
+ unsigned int i;
+
+ switch (key) {
+ case SR_CONF_SCAN_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+ break;
+ case SR_CONF_DEVICE_OPTIONS:
+ *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+ hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+ break;
+ case SR_CONF_COUPLING:
+ *data = g_variant_new_strv(coupling, ARRAY_SIZE(coupling));
+ break;
+ case SR_CONF_VDIV:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(vdivs); i++) {
+ rational[0] = g_variant_new_uint64(vdivs[i][0]);
+ rational[1] = g_variant_new_uint64(vdivs[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TIMEBASE:
+ g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+ for (i = 0; i < ARRAY_SIZE(timebases); i++) {
+ rational[0] = g_variant_new_uint64(timebases[i][0]);
+ rational[1] = g_variant_new_uint64(timebases[i][1]);
+ tuple = g_variant_new_tuple(rational, 2);
+ g_variant_builder_add_value(&gvb, tuple);
+ }
+ *data = g_variant_builder_end(&gvb);
+ break;
+ case SR_CONF_TRIGGER_SOURCE:
+ if (!sdi || !sdi->priv)
+ /* Can't know this until we have the exact model. */
+ return SR_ERR_ARG;
+ *data = g_variant_new_strv(trigger_sources, 4);
+ break;
+ default:
+ return SR_ERR_NA;
+ }
+
+ return SR_OK;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+ struct sr_probe *probe;
+ GSList *l;
+ char cmd[256];
+
+ if (sdi->status != SR_ST_ACTIVE)
+ return SR_ERR_DEV_CLOSED;
+
+ serial = sdi->conn;
+ devc = sdi->priv;
+
+ for (l = sdi->probes; l; l = l->next) {
+ probe = l->data;
+ sr_dbg("handling probe %s", probe->name);
+ if (probe->type == SR_PROBE_ANALOG) {
+ if (probe->enabled)
+ devc->enabled_analog_probes = g_slist_append(
+ devc->enabled_analog_probes,
probe);
+ if (probe->enabled !=
devc->analog_channels[probe->index]) {
+ /* Enabled channel is currently disabled, or
vice versa. */
+ sprintf(cmd, ":CHAN%d:DISP %s", probe->index +
1,
+ probe->enabled ? "ON" : "OFF");
+ if (rigol_ds2xx2_send(sdi, cmd) != SR_OK)
+ return SR_ERR;
+ }
+ }
+ }
+ if (!devc->enabled_analog_probes)
+ return SR_ERR;
+
+ sr_source_add(serial->fd, G_IO_IN, 50, rigol_ds2xx2_receive, (void
*)sdi);
+
+ /* Send header packet to the session bus. */
+ std_session_send_df_header(cb_data, LOG_PREFIX);
+
+ /* Fetch the first frame. */
+ if (devc->enabled_analog_probes) {
+ devc->channel_frame = devc->enabled_analog_probes->data;
+ if (rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN%d",
+ devc->channel_frame->index + 1) != SR_OK)
+ return SR_ERR;
+ if (rigol_ds2xx2_send(sdi, ":WAV:MODE NORM") != SR_OK)
+ return SR_ERR;
+ if (rigol_ds2xx2_send(sdi, ":WAV:DATA?") != SR_OK)
+ return SR_ERR;
+ }
+
+ devc->num_frame_bytes = 0;
+
+ return SR_OK;
+}
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
+
+ (void)cb_data;
+
+ devc = sdi->priv;
+
+ if (sdi->status != SR_ST_ACTIVE) {
+ sr_err("Device inactive, can't stop acquisition.");
+ return SR_ERR;
+ }
+
+ g_slist_free(devc->enabled_analog_probes);
+ devc->enabled_analog_probes = NULL;
+ serial = sdi->conn;
+ sr_source_remove(serial->fd);
+
+ return SR_OK;
+}
+
+SR_PRIV struct sr_dev_driver rigol_ds2xx2_driver_info = {
+ .name = "rigol-ds2xx2",
+ .longname = "Rigol DS2xx2",
+ .api_version = 1,
+ .init = init,
+ .cleanup = cleanup,
+ .scan = scan,
+ .dev_list = dev_list,
+ .dev_clear = dev_clear,
+ .config_get = config_get,
+ .config_set = config_set,
+ .config_list = config_list,
+ .dev_open = dev_open,
+ .dev_close = dev_close,
+ .dev_acquisition_start = dev_acquisition_start,
+ .dev_acquisition_stop = dev_acquisition_stop,
+ .priv = NULL,
+};
+
+/* Local Variables: */
+/* c-basic-offset: 8 */
+/* End: */
diff -urN libsigrok.org/hardware/rigol-ds2xx2/Makefile.am
libsigrok/hardware/rigol-ds2xx2/Makefile.am
--- libsigrok.org/hardware/rigol-ds2xx2/Makefile.am 1970-01-01
01:00:00.000000000 +0100
+++ libsigrok/hardware/rigol-ds2xx2/Makefile.am 2013-10-20 19:18:46.677498253
+0200
@@ -0,0 +1,33 @@
+##
+## This file is part of the libsigrok project.
+##
+## Copyright (C) 2012 Martin Ling <[email protected]>,
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+if HW_RIGOL_DS2XX2
+
+# Local lib, this is NOT meant to be installed!
+noinst_LTLIBRARIES = libsigrok_hw_rigol_ds2xx2.la
+
+libsigrok_hw_rigol_ds2xx2_la_SOURCES = \
+ api.c \
+ protocol.c \
+ protocol.h
+
+libsigrok_hw_rigol_ds2xx2_la_CFLAGS = \
+ -I$(top_srcdir)
+
+endif
diff -urN libsigrok.org/hardware/rigol-ds2xx2/protocol.c
libsigrok/hardware/rigol-ds2xx2/protocol.c
--- libsigrok.org/hardware/rigol-ds2xx2/protocol.c 1970-01-01
01:00:00.000000000 +0100
+++ libsigrok/hardware/rigol-ds2xx2/protocol.c 2013-10-20 18:41:34.326888083
+0200
@@ -0,0 +1,335 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <[email protected]>
+ * Copyright (C) 2013 Bert Vermeulen <[email protected]>
+ * Copyright (C) 2013 Mathias Grimmberger <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+#include "protocol.h"
+
+/* Based on protocol.c from Rigol DS1xx2 driver. */
+
+/*
+ * Each waveform block from the scope has a header of 11 bytes.
+ * Format: #9xxxxxxxxxxx (example: #900000001400)
+ * Where "#9" is constant and the rest gives the sample count
+ * as a decimal number.
+ *
+ * Each waveform block also has a trailing linefeed, need
+ * to discard that when implementing raw memory read.
+ */
+#define WAVEFORM_HEADER_SIZE 11
+
+SR_PRIV void rigol_ds2xx2_receive_header(struct sr_serial_dev_inst *serial)
+{
+ char buf[WAVEFORM_HEADER_SIZE + 1];
+ int len;
+
+ /*
+ * For now just consume the header. Later this needs to properly
+ * handle the returned sample count and do error handling.
+ */
+ len = serial_read(serial, buf, WAVEFORM_HEADER_SIZE);
+ buf[WAVEFORM_HEADER_SIZE] = '\0';
+ sr_dbg("Received %d bytes of waveform header: %s.", len, buf);
+}
+
+SR_PRIV int rigol_ds2xx2_receive(int fd, int revents, void *cb_data)
+{
+ struct sr_dev_inst *sdi;
+ struct sr_serial_dev_inst *serial;
+ struct dev_context *devc;
+ struct sr_datafeed_packet packet;
+ struct sr_datafeed_analog analog;
+ unsigned char buf[ANALOG_WAVEFORM_SIZE];
+ double vdiv, offset;
+ float data[ANALOG_WAVEFORM_SIZE];
+ int len, i, vref;
+ struct sr_probe *probe;
+
+ (void)fd;
+
+ if (!(sdi = cb_data))
+ return TRUE;
+
+ if (!(devc = sdi->priv))
+ return TRUE;
+
+ serial = sdi->conn;
+
+ if (revents == G_IO_IN) {
+ if (devc->num_frame_bytes == 0) {
+ /* Consume waveform header. */
+ rigol_ds2xx2_receive_header(serial);
+ }
+
+ probe = devc->channel_frame;
+ len = serial_read(serial, buf, ANALOG_WAVEFORM_SIZE -
devc->num_frame_bytes);
+ sr_dbg("Received %d bytes.", len);
+ if (len == -1)
+ return TRUE;
+
+ if (devc->num_frame_bytes == 0) {
+ /* Start of a new frame. */
+ packet.type = SR_DF_FRAME_BEGIN;
+ sr_session_send(sdi, &packet);
+ }
+
+ vdiv = devc->vdiv[probe->index] / 25.6;
+ vref = devc->vert_reference[probe->index];
+ offset = devc->vert_offset[probe->index];
+ for (i = 0; i < len; i++) {
+ data[i] = ((int)buf[i] - vref) * vdiv - offset;
+ }
+ analog.probes = g_slist_append(NULL, probe);
+ analog.num_samples = len;
+ analog.data = data;
+ 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.probes);
+
+ if (len != ANALOG_WAVEFORM_SIZE)
+ /* Don't have the whole frame yet. */
+ return TRUE;
+
+ /* End of the frame. */
+ packet.type = SR_DF_FRAME_END;
+ sr_session_send(sdi, &packet);
+ devc->num_frame_bytes = 0;
+
+ if (devc->enabled_analog_probes
+ && devc->channel_frame ==
devc->enabled_analog_probes->data
+ && devc->enabled_analog_probes->next != NULL) {
+ /* We got the frame for the first analog channel, but
+ * there's a second analog channel. */
+ devc->channel_frame =
devc->enabled_analog_probes->next->data;
+ rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN%d",
+ devc->channel_frame->index + 1);
+ rigol_ds2xx2_send(sdi, ":WAV:MODE NORM");
+ rigol_ds2xx2_send(sdi, ":WAV:DATA?");
+ } else {
+ /* Done with both analog channels in this frame. */
+ if (++devc->num_frames == devc->limit_frames) {
+ /* End of last frame. */
+ sdi->driver->dev_acquisition_stop(sdi, cb_data);
+ } else {
+ /* Get the next frame, starting with the first
analog channel. */
+ if (devc->enabled_analog_probes) {
+ devc->channel_frame =
devc->enabled_analog_probes->data;
+ rigol_ds2xx2_send(sdi, ":WAV:SOUR
CHAN%d",
+
devc->channel_frame->index + 1);
+ rigol_ds2xx2_send(sdi, ":WAV:MODE
NORM");
+ rigol_ds2xx2_send(sdi, ":WAV:DATA?");
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+SR_PRIV int rigol_ds2xx2_send(const struct sr_dev_inst *sdi, const char
*format, ...)
+{
+ va_list args;
+ char buf[256];
+ int len, out, ret;
+
+ va_start(args, format);
+ len = vsnprintf(buf, 255, format, args);
+ va_end(args);
+ strcat(buf, "\n");
+ len++;
+ out = serial_write(sdi->conn, buf, len);
+ buf[len - 1] = '\0';
+ if (out != len) {
+ sr_dbg("Only sent %d/%d bytes of '%s'.", out, len, buf);
+ ret = SR_ERR;
+ } else {
+ sr_spew("Sent '%s'.", buf);
+ ret = SR_OK;
+ }
+
+ return ret;
+}
+
+static int get_cfg(const struct sr_dev_inst *sdi, char *cmd, char *reply)
+{
+ int len;
+
+ if (rigol_ds2xx2_send(sdi, cmd) != SR_OK)
+ return SR_ERR;
+
+ if ((len = serial_read(sdi->conn, reply, 255)) < 0)
+ return SR_ERR;
+ reply[len] = '\0';
+ /* get rid of trailing linefeed */
+ if (len >= 1 && reply[len-1] == '\n')
+ {
+ reply[len-1] = '\0';
+ }
+ sr_spew("Received '%s'.", reply);
+
+ return SR_OK;
+}
+
+static int get_cfg_int(const struct sr_dev_inst *sdi, char *cmd, int *i)
+{
+ char buf[256], *e;
+ long tmp;
+
+ if (get_cfg(sdi, cmd, buf) != SR_OK)
+ return SR_ERR;
+ errno = 0;
+ tmp = strtol(buf, &e, 10);
+ if (e == buf) {
+ sr_dbg("failed to parse response to '%s': '%s'", cmd, buf);
+ return SR_ERR;
+ }
+ if (errno) {
+ sr_dbg("failed to parse response to '%s': '%s', numerical
overflow", cmd, buf);
+ return SR_ERR;
+ }
+ if (tmp > INT_MAX || tmp < INT_MIN) {
+ sr_dbg("failed to parse response to '%s': '%s', value to
large/small", cmd, buf);
+ return SR_ERR;
+ }
+
+ *i =(int)tmp;
+ return SR_OK;
+}
+
+static int get_cfg_float(const struct sr_dev_inst *sdi, char *cmd, float *f)
+{
+ char buf[256], *e;
+
+ if (get_cfg(sdi, cmd, buf) != SR_OK)
+ return SR_ERR;
+ *f = strtof(buf, &e);
+ if (e == buf || (fpclassify(*f) & (FP_ZERO | FP_NORMAL)) == 0) {
+ sr_dbg("failed to parse response to '%s': '%s'", cmd, buf);
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+static int get_cfg_string(const struct sr_dev_inst *sdi, char *cmd, char **buf)
+{
+
+ if (!(*buf = g_try_malloc0(256)))
+ return SR_ERR_MALLOC;
+
+ if (get_cfg(sdi, cmd, *buf) != SR_OK)
+ return SR_ERR;
+
+ return SR_OK;
+}
+
+SR_PRIV int rigol_ds2xx2_get_dev_cfg(const struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ char *t_s;
+
+ devc = sdi->priv;
+
+ /* Analog channel state. */
+ if (get_cfg_string(sdi, ":CHAN1:DISP?", &t_s) != SR_OK)
+ return SR_ERR;
+ devc->analog_channels[0] = !strcmp(t_s, "1") ? TRUE : FALSE;
+ g_free(t_s);
+ if (get_cfg_string(sdi, ":CHAN2:DISP?", &t_s) != SR_OK)
+ return SR_ERR;
+ devc->analog_channels[1] = !strcmp(t_s, "1") ? TRUE : FALSE;
+ g_free(t_s);
+ sr_dbg("Current analog channel state CH1 %s CH2 %s",
+ devc->analog_channels[0] ? "on" : "off",
+ devc->analog_channels[1] ? "on" : "off");
+
+ /* Timebase. */
+ if (get_cfg_float(sdi, ":TIM:SCAL?", &devc->timebase) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current timebase %g", devc->timebase);
+
+ /* Vertical gain. */
+ if (get_cfg_float(sdi, ":CHAN1:SCAL?", &devc->vdiv[0]) != SR_OK)
+ return SR_ERR;
+ if (get_cfg_float(sdi, ":CHAN2:SCAL?", &devc->vdiv[1]) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current vertical gain CH1 %g CH2 %g", devc->vdiv[0],
devc->vdiv[1]);
+
+ /* Vertical reference - not certain if this is the place to read it. */
+ if (rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN1") != SR_OK)
+ return SR_ERR;
+ if (get_cfg_int(sdi, ":WAV:YREF?", &devc->vert_reference[0]) != SR_OK)
+ return SR_ERR;
+ if (rigol_ds2xx2_send(sdi, ":WAV:SOUR CHAN2") != SR_OK)
+ return SR_ERR;
+ if (get_cfg_int(sdi, ":WAV:YREF?", &devc->vert_reference[1]) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current vertical reference CH1 %d CH2 %d",
devc->vert_reference[0],
+ devc->vert_reference[1]);
+
+ /* Vertical offset. */
+ if (get_cfg_float(sdi, ":CHAN1:OFFS?", &devc->vert_offset[0]) != SR_OK)
+ return SR_ERR;
+ if (get_cfg_float(sdi, ":CHAN2:OFFS?", &devc->vert_offset[1]) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current vertical offset CH1 %g CH2 %g", devc->vert_offset[0],
+ devc->vert_offset[1]);
+
+ /* Coupling. */
+ if (get_cfg_string(sdi, ":CHAN1:COUP?", &devc->coupling[0]) != SR_OK)
+ return SR_ERR;
+ if (get_cfg_string(sdi, ":CHAN2:COUP?", &devc->coupling[1]) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current coupling CH1 %s CH2 %s", devc->coupling[0],
+ devc->coupling[1]);
+
+ /* Trigger source. */
+ if (get_cfg_string(sdi, ":TRIG:EDGE:SOUR?", &devc->trigger_source) !=
SR_OK)
+ return SR_ERR;
+ sr_dbg("Current trigger source %s", devc->trigger_source);
+
+ /* Horizontal trigger position. */
+ if (get_cfg_float(sdi, ":TIM:OFFS?", &devc->horiz_triggerpos) != SR_OK)
+ return SR_ERR;
+ sr_dbg("Current horizontal trigger position %g",
devc->horiz_triggerpos);
+
+ /* Trigger slope. */
+ if (get_cfg_string(sdi, ":TRIG:EDGE:SLOP?", &devc->trigger_slope) !=
SR_OK)
+ return SR_ERR;
+ sr_dbg("Current trigger slope %s", devc->trigger_slope);
+
+ return SR_OK;
+}
+
+/* Local Variables: */
+/* c-basic-offset: 8 */
+/* End: */
diff -urN libsigrok.org/hardware/rigol-ds2xx2/protocol.h
libsigrok/hardware/rigol-ds2xx2/protocol.h
--- libsigrok.org/hardware/rigol-ds2xx2/protocol.h 1970-01-01
01:00:00.000000000 +0100
+++ libsigrok/hardware/rigol-ds2xx2/protocol.h 2013-10-20 19:19:16.767886421
+0200
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2012 Martin Ling <[email protected]>
+ * Copyright (C) 2013 Bert Vermeulen <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_RIGOL_DS2XX2_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_RIGOL_DS2XX2_PROTOCOL_H
+
+#include <stdint.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/* Message logging helpers with subsystem-specific prefix string. */
+#define LOG_PREFIX "rigol-ds2xx2: "
+#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
+#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
+#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
+#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
+#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
+#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
+
+#define ANALOG_WAVEFORM_SIZE 1400
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+ /* Acquisition settings */
+ GSList *enabled_analog_probes;
+ uint64_t limit_frames;
+ void *cb_data;
+
+ /* Device settings */
+ gboolean analog_channels[2];
+ float timebase;
+ float vdiv[2];
+ int vert_reference[2];
+ float vert_offset[2];
+ char *trigger_source;
+ float horiz_triggerpos;
+ char *trigger_slope;
+ char *coupling[2];
+
+ /* Operational state */
+ uint64_t num_frames;
+ uint64_t num_frame_bytes;
+ struct sr_probe *channel_frame;
+};
+
+SR_PRIV int rigol_ds2xx2_receive(int fd, int revents, void *cb_data);
+SR_PRIV int rigol_ds2xx2_send(const struct sr_dev_inst *sdi, const char
*format, ...);
+SR_PRIV int rigol_ds2xx2_get_dev_cfg(const struct sr_dev_inst *sdi);
+
+#endif
diff -urN libsigrok.org/hwdriver.c libsigrok/hwdriver.c
--- libsigrok.org/hwdriver.c 2013-10-18 22:18:36.000000000 +0200
+++ libsigrok/hwdriver.c 2013-10-19 00:21:12.116947533 +0200
@@ -145,6 +145,9 @@
#ifdef HAVE_HW_RIGOL_DS1XX2
extern SR_PRIV struct sr_dev_driver rigol_ds1xx2_driver_info;
#endif
+#ifdef HAVE_HW_RIGOL_DS2XX2
+extern SR_PRIV struct sr_dev_driver rigol_ds2xx2_driver_info;
+#endif
#ifdef HAVE_HW_SALEAE_LOGIC16
extern SR_PRIV struct sr_dev_driver saleae_logic16_driver_info;
#endif
@@ -259,6 +262,9 @@
#ifdef HAVE_HW_RIGOL_DS1XX2
&rigol_ds1xx2_driver_info,
#endif
+#ifdef HAVE_HW_RIGOL_DS2XX2
+ &rigol_ds2xx2_driver_info,
+#endif
#ifdef HAVE_HW_SALEAE_LOGIC16
&saleae_logic16_driver_info,
#endif
diff -urN libsigrok.org/README.devices libsigrok/README.devices
--- libsigrok.org/README.devices 2013-10-09 23:51:06.000000000 +0200
+++ libsigrok/README.devices 2013-10-20 18:58:03.034784924 +0200
@@ -66,6 +66,7 @@
- mic-985xx
- openbench-logic-sniffer
- rigol-ds1xx2
+ - rigol-ds2xx2
- serial-dmm
- tondaj-sl-814
- uni-t-dmm
@@ -115,6 +116,7 @@
- kecheng-kc-330b
- lascar-el-usb
- rigol-ds1xx2
+ - rigol-ds2xx2
- saleae-logic16
- uni-t-dmm
- uni-t-ut32x
@@ -353,3 +355,9 @@
library (which uses libusb) soon, which will fix all these issues and make
the driver portable at the same time.
+
+Rigol DS2xx2 oscilloscopes
+--------------------------
+
+Because the 'rigol-ds2xx2' driver is based on the 'rigol-ds1xx2' driver it
+shares its dependency on the Linux usbtmc kernel driver.
------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135031&iu=/4140/ostg.clktrk
_______________________________________________
sigrok-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sigrok-devel