]> sigrok.org Git - libsigrok.git/commitdiff
bkprecision-1856d: Add implementation
authorDaniel Anselmi <redacted>
Sun, 29 Aug 2021 18:54:41 +0000 (20:54 +0200)
committerSoeren Apel <redacted>
Wed, 16 Oct 2024 22:36:24 +0000 (00:36 +0200)
configure.ac
src/hardware/bkprecision-1856d/api.c
src/hardware/bkprecision-1856d/protocol.c
src/hardware/bkprecision-1856d/protocol.h

index dd72931c8a00447f4c64e39850dba7add4825fdd..cbde1480ea87ad3d4ca0538551b700c84c8ca331 100644 (file)
@@ -313,7 +313,7 @@ SR_DRIVER([Atorch], [atorch], [serial_comm])
 SR_DRIVER([Atten PPS3xxx], [atten-pps3xxx], [serial_comm])
 SR_DRIVER([BayLibre ACME], [baylibre-acme], [sys_timerfd_h])
 SR_DRIVER([BeagleLogic], [beaglelogic], [sys_mman_h sys_ioctl_h])
-SR_DRIVER([BK Precision 1856D], [bkprecision-1856d])
+SR_DRIVER([BK Precision 1856D], [bkprecision-1856d], [serial_comm])
 SR_DRIVER([CEM DT-885x], [cem-dt-885x], [serial_comm])
 SR_DRIVER([Center 3xx], [center-3xx], [serial_comm])
 SR_DRIVER([ChronoVu LA], [chronovu-la], [libusb libftdi])
index c121528373ed7afbc2bb8053dd8814c4360a92fe..fb98807e556dd195243bcc31ddf10078bd07683b 100644 (file)
 #include <config.h>
 #include "protocol.h"
 
-static struct sr_dev_driver bkprecision_1856d_driver_info;
-
-static GSList *scan(struct sr_dev_driver *di, GSList *options)
-{
-       struct drv_context *drvc;
-       GSList *devices;
-
-       (void)options;
+#define SERIALCOMM "9600/8n1/dtr=1/rts=0"
 
-       devices = NULL;
-       drvc = di->context;
-       drvc->instances = NULL;
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+};
 
-       /* TODO: scan for devices, either based on a SR_CONF_CONN option
-        * or on a USB scan. */
+static const uint32_t drvopts[] = {
+       SR_CONF_FREQUENCY_COUNTER,
+};
 
-       return devices;
-}
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS,
+       SR_CONF_GATE_TIME     | SR_CONF_SET |SR_CONF_GET| SR_CONF_LIST,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
+       SR_CONF_DATA_SOURCE   | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+};
 
-static int dev_open(struct sr_dev_inst *sdi)
-{
-       (void)sdi;
+static struct sr_dev_driver bkprecision_1856d_driver_info;
 
-       /* TODO: get handle from sdi->conn and open it. */
+const uint64_t timebases[][2] = {
+       /*miliseconds*/
+       { 10, 1000 },
+       { 100, 1000 },
+       /* seconds */
+       { 1, 1 },
+       { 10, 1 },
+};
 
-       return SR_OK;
-}
+static const char *data_sources[] = {
+       "A", "C",
+};
 
-static int dev_close(struct sr_dev_inst *sdi)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       (void)sdi;
-
-       /* TODO: get handle from sdi->conn and close it. */
-
-       return SR_OK;
+       struct dev_context *devc;
+       struct sr_dev_inst *sdi;
+       GSList *opt;
+       const char *conn;
+
+       conn = NULL;
+       for (opt = options; opt; opt = opt->next) {
+               struct sr_config *src = opt->data;
+               switch (src->key) {
+               case SR_CONF_CONN:
+                       conn = g_variant_get_string(src->data, NULL);
+                       break;
+               }
+       }
+       if (!conn)
+               return NULL;
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("BK Precision");
+       sdi->model = g_strdup("bk-1856d");
+       devc = g_malloc0(sizeof(struct dev_context));
+       sr_sw_limits_init(&(devc->sw_limits));
+       sdi->conn = sr_serial_dev_inst_new(conn, SERIALCOMM);
+       sdi->inst_type = SR_INST_SERIAL;
+       sdi->priv = devc;
+       sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P1");
+       devc->sel_input = InputC;
+       devc->curr_sel_input = InputC;
+       devc->gate_time = 0;
+
+       return std_scan_complete(di, g_slist_append(NULL, sdi));
 }
 
 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 (!(devc = sdi->priv))
+               return SR_ERR;
+
        switch (key) {
-       /* TODO */
+       case SR_CONF_GATE_TIME:
+               *data = g_variant_new("(tt)",
+                               timebases[devc->gate_time][0],
+                               timebases[devc->gate_time][1]);
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               sr_sw_limits_config_get(&(devc->sw_limits), key, data);
+               break;
+       case SR_CONF_DATA_SOURCE:
+               if (devc->sel_input == InputA)
+                       *data = g_variant_new_string(data_sources[0]);
+               else
+                       *data = g_variant_new_string(data_sources[1]);
+               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;
+       int idx;
+       struct dev_context *devc;
 
-       (void)sdi;
-       (void)data;
        (void)cg;
 
-       ret = SR_OK;
+       if (!(devc = sdi->priv))
+               return SR_ERR;
+
        switch (key) {
-       /* TODO */
+       case SR_CONF_GATE_TIME:
+               {
+                       uint64_t p, q;
+                       g_variant_get(data, "(tt)", &p, &q);
+                       if (p == 10 && q == 1000)
+                bkprecision_1856d_set_gate_time(devc, 0);
+                       else if (p == 100 && q == 1000)
+                bkprecision_1856d_set_gate_time(devc, 1);
+                       else if (p == 1 && q == 1)
+                               bkprecision_1856d_set_gate_time(devc, 2);
+                       else if (p == 10 && q == 1)
+                               bkprecision_1856d_set_gate_time(devc, 3);
+                       else
+                               return SR_ERR_NA;
+               }
+               break;
+       case SR_CONF_LIMIT_SAMPLES:
+               sr_sw_limits_config_set(&(devc->sw_limits),key, data);
+               break;
+       case SR_CONF_DATA_SOURCE:
+               idx = std_str_idx(data, ARRAY_AND_SIZE(data_sources));
+               if (idx < 0)
+                       return SR_ERR_ARG;
+               bkprecision_1856d_select_input(devc, idx);
+               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)
+static GVariant *build_tuples(const uint64_t (*array)[][2], unsigned int n)
 {
-       int ret;
+       unsigned int i;
+       GVariant *rational[2];
+       GVariantBuilder gvb;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+
+       for (i = 0; i < n; i++) {
+               rational[0] = g_variant_new_uint64((*array)[i][0]);
+               rational[1] = g_variant_new_uint64((*array)[i][1]);
+               g_variant_builder_add_value(&gvb, g_variant_new_tuple(rational, 2));
+       }
+
+       return g_variant_builder_end(&gvb);
+}
 
-       ret = SR_OK;
+static int config_list(uint32_t key, GVariant **data,
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
+{
        switch (key) {
-       /* TODO */
+       case SR_CONF_SCAN_OPTIONS:
+       case SR_CONF_DEVICE_OPTIONS:
+               return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+       case SR_CONF_GATE_TIME:
+               *data = build_tuples(&timebases, ARRAY_SIZE(timebases));
+               break;
+       case SR_CONF_DATA_SOURCE:
+               *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources));
+               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 sr_serial_dev_inst *serial;
 
-       (void)sdi;
+       std_session_send_df_header(sdi);
 
-       return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi)
-{
-       /* TODO: stop acquisition. */
+       serial = sdi->conn;
+       serial_source_add(sdi->session, serial, G_IO_IN, 100,
+                       bkprecision_1856d_receive_data, (void *)sdi);
 
-       (void)sdi;
+       bkprecision_1856d_init(sdi);
 
        return SR_OK;
 }
@@ -146,10 +228,10 @@ static struct sr_dev_driver bkprecision_1856d_driver_info = {
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
-       .dev_open = dev_open,
-       .dev_close = dev_close,
+       .dev_open = std_serial_dev_open,
+       .dev_close = std_serial_dev_close,
        .dev_acquisition_start = dev_acquisition_start,
-       .dev_acquisition_stop = dev_acquisition_stop,
+       .dev_acquisition_stop = std_serial_dev_acquisition_stop,
        .context = NULL,
 };
 SR_REGISTER_DEV_DRIVER(bkprecision_1856d_driver_info);
index 67e6a381078d699da46be7ab0832a1d41fe903f5..a7d1a8504ed08f7ec7d0de0d3685c5bdc81136f3 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/* https://bkpmedia.s3.us-west-1.amazonaws.com/downloads/manuals/en-us/1856D_manual.pdf */
+
 #include <config.h>
 #include "protocol.h"
 
+#define GATE_TIME_0     "G0\xD"
+#define GATE_TIME_1     "G1\xD"
+#define GATE_TIME_2     "G2\xD"
+#define GATE_TIME_3     "G3\xD"
+
+#define DATA_REQ        "D0\xD"
+
+#define FUNCTION_A      "F0\xD"
+#define FUNCTION_C      "F2\xD"
+
+#define LENGHT_OF_CMD  3
+
+struct gate_time_config_command
+{
+    const char *cmd;
+    const char *info;
+    gulong sleep_time;
+};
+
+static struct gate_time_config_command gate_time_config_commands[] = {
+    {
+        .cmd = GATE_TIME_0,
+        .info = "sending gate time 0 (10ms)",
+        .sleep_time = 40000
+    }, {
+        .cmd = GATE_TIME_1,
+        .info = "sending gate time 1 (100ms)",
+        .sleep_time = 80000
+    }, {
+        .cmd = GATE_TIME_2,
+        .info = "sending gate time 2 (1s)",
+        .sleep_time = 80000
+    }, {
+        .cmd = GATE_TIME_3,
+        .info = "sending gate time 3 (10s)",
+        .sleep_time = 800000
+    }
+};
+
+static void bkprecision_1856d_send_input_sel(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       char *cmd;
+
+       if (!sdi)
+               return;
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+       if (!devc || !serial)
+               return;
+
+       if (devc->sel_input == InputA) {
+               sr_spew("selecting input A");
+               cmd = FUNCTION_A;
+       } else {
+               sr_spew("selecting input C");
+               cmd = FUNCTION_C;
+       }
+
+       if (serial_write_blocking(serial, cmd, LENGHT_OF_CMD,
+                       serial_timeout(serial, LENGHT_OF_CMD)) < 1) {
+               sr_err("unable to send function %c command",
+                               devc->sel_input == InputA ? 'A' : 'C' );
+       }
+
+       devc->curr_sel_input = devc->sel_input;
+}
+
+static void bkprecision_1856d_chk_select_input(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+
+       if (!sdi)
+               return;
+
+       if (!(devc = sdi->priv))
+               return;
+
+       if (devc->sel_input != devc->curr_sel_input)
+               bkprecision_1856d_send_input_sel(sdi);
+}
+
+static void bkprecision_1856d_send_gate_time(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       struct gate_time_config_command *cfg;
+
+       if (!sdi)
+               return;
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+       if (!devc || !serial)
+               return;
+
+       cfg = &(gate_time_config_commands[devc->gate_time]);
+
+       sr_info("%s", cfg->info);
+
+       if (serial_write_blocking(serial, cfg->cmd, LENGHT_OF_CMD,
+                       serial_timeout(serial, LENGHT_OF_CMD)) < 1) {
+               sr_err("unable to send gate time command");
+       }
+       g_usleep(cfg->sleep_time);
+}
+
+static void bkprecision_1856d_request_data(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       if (!sdi)
+               return;
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+       if (!devc || !serial)
+               return;
+
+       sr_spew("requesting data");
+
+       if (serial_write_blocking(serial, DATA_REQ, LENGHT_OF_CMD,
+                       serial_timeout(serial, LENGHT_OF_CMD)) < 1) {
+               sr_err("unable to send request data command");
+       }
+}
+
+SR_PRIV void bkprecision_1856d_init(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       if (!sdi)
+               return;
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+       if (!devc || !serial)
+               return;
+
+       devc->buffer_level = 0;
+       sr_sw_limits_acquisition_start(&(devc->sw_limits));
+       serial_flush(serial);
+
+       bkprecision_1856d_send_input_sel(sdi);
+
+       bkprecision_1856d_send_gate_time(sdi);
+       bkprecision_1856d_request_data(sdi);
+}
+
+static int bkprecision_1856d_check_for_zero_message(struct dev_context *devc)
+{
+       int zero_found;
+       int has_data;
+       int idx;
+
+       zero_found = 0;
+       has_data = 0;
+
+       for (idx = 0; idx < BKPRECISION1856D_MSG_SIZE - 1; ++idx) {
+               if (devc->buffer[idx] == ' ') {
+                       continue;
+               } else if (devc->buffer[idx] == '0' && zero_found == 0) {
+                       zero_found = 1;
+                       continue;
+               } else {
+                       has_data = 1;
+                       break;
+               }
+       }
+       return !has_data;
+}
+
+static void bkprecision_1856d_send_packet(
+       const struct sr_dev_inst *sdi, double freq_value, 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;
+
+       sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
+       analog.meaning->mq = SR_MQ_FREQUENCY;
+       analog.meaning->unit = SR_UNIT_HERTZ;
+       analog.meaning->channels = sdi->channels;
+       analog.num_samples = 1;
+       analog.data = &freq_value;
+       analog.encoding->unitsize = sizeof(freq_value);
+       analog.encoding->is_float = TRUE;
+       analog.encoding->digits = digits;
+
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       sr_session_send(sdi, &packet);
+}
+
+static void bkprecision_1856d_parse_message(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       double freq_value;
+       int digits;
+       char *endPtr, *dotPtr;
+
+       if (!sdi->priv || !sdi->conn)
+               return;
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+
+       /* check for cr at end of message */
+       if (devc->buffer[BKPRECISION1856D_MSG_SIZE - 1] != '\xD') {
+               sr_err("expected cr at end of message.");
+               devc->buffer_level = 0;
+               serial_flush(serial);
+               bkprecision_1856d_send_input_sel(sdi);
+               bkprecision_1856d_send_gate_time(sdi);
+               bkprecision_1856d_request_data(sdi);
+               return;
+       }
+
+       devc->buffer[BKPRECISION1856D_MSG_SIZE - 1] = 0; /* set trailing zero */
+
+       if (bkprecision_1856d_check_for_zero_message(devc)) {
+               sr_spew("received an empty packet");
+               devc->buffer_level = 0;
+               bkprecision_1856d_request_data(sdi);
+               return;
+       }
+
+       freq_value = strtod(devc->buffer, &endPtr);
+
+       if (strcmp(devc->buffer + BKPRECISION1856D_MSG_NUMBER_SIZE + 1, "Hz ")) {
+               sr_err("not a frequency returned");
+               devc->buffer_level = 0;
+               bkprecision_1856d_send_input_sel(sdi);
+               bkprecision_1856d_send_gate_time(sdi);
+               bkprecision_1856d_request_data(sdi);
+               return;
+       }
+
+       dotPtr = strchr(devc->buffer, '.');
+       if (dotPtr)
+               digits = endPtr - (dotPtr+1);
+       else
+               digits = endPtr - devc->buffer;
+
+       if (devc->buffer[BKPRECISION1856D_MSG_NUMBER_SIZE] == 'M') {
+               freq_value *= 1e6;
+               digits -= 6;
+       }
+       if (devc->buffer[BKPRECISION1856D_MSG_NUMBER_SIZE] == 'k') {
+               freq_value *= 1e3;
+               digits -= 3;
+       }
+
+       bkprecision_1856d_send_packet(sdi, freq_value, digits);
+
+       sr_sw_limits_update_samples_read(&(devc->sw_limits), 1);
+
+       if (!sr_sw_limits_check(&(devc->sw_limits))) {
+               devc->buffer_level = 0;
+               bkprecision_1856d_chk_select_input(sdi);
+               bkprecision_1856d_send_gate_time(sdi);
+               bkprecision_1856d_request_data(sdi);
+       }
+       else
+               sr_dev_acquisition_stop(sdi);
+}
+
 SR_PRIV int bkprecision_1856d_receive_data(int fd, int revents, void *cb_data)
 {
-       const struct sr_dev_inst *sdi;
+       struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       int len;
 
        (void)fd;
 
@@ -34,9 +313,39 @@ SR_PRIV int bkprecision_1856d_receive_data(int fd, int revents, void *cb_data)
        if (!(devc = sdi->priv))
                return TRUE;
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       if (revents != G_IO_IN) {
+               /* Timeout, in rare cases the bk1856 doesn't respond anymore
+                  (probably timing on rs232).
+                  For now, just restart the measurement.*/
+               bkprecision_1856d_send_gate_time(sdi);
+               bkprecision_1856d_request_data(sdi);
+
+               return TRUE;
        }
 
+       if (!sdi->conn)
+               return TRUE;
+       serial = sdi->conn;
+
+       len = serial_read_nonblocking(serial, devc->buffer + devc->buffer_level,
+                       BKPRECISION1856D_MSG_SIZE - devc->buffer_level );
+
+       if (len < 1)
+               return TRUE;
+       devc->buffer_level += len;
+       if (devc->buffer_level == BKPRECISION1856D_MSG_SIZE)
+               bkprecision_1856d_parse_message(sdi);
+
        return TRUE;
 }
+
+SR_PRIV void bkprecision_1856d_set_gate_time(struct dev_context *devc, int time)
+{
+       devc->gate_time = time;
+}
+
+SR_PRIV void bkprecision_1856d_select_input(struct dev_context *devc,
+                                                                                       int intput)
+{
+       devc->sel_input = intput;
+}
index eec717752a53e789b79ad4ad9e3d194194b60114..6dd276f0390b7238b02acf8fd3d75cd6a2fe332e 100644 (file)
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "bkprecision-1856d"
+#define BKPRECISION1856D_MSG_SIZE 15
+#define BKPRECISION1856D_MSG_NUMBER_SIZE 10
+
+
+enum {
+       InputA = 0,
+       InputC = 1,
+};
 
 struct dev_context {
+       struct sr_sw_limits sw_limits;
+       unsigned int sel_input;
+       unsigned int curr_sel_input;
+       unsigned int gate_time;
+
+       char buffer[BKPRECISION1856D_MSG_SIZE];
+       unsigned int buffer_level;
 };
 
 SR_PRIV int bkprecision_1856d_receive_data(int fd, int revents, void *cb_data);
+SR_PRIV void bkprecision_1856d_init(const struct sr_dev_inst *sdi);
+SR_PRIV void bkprecision_1856d_set_gate_time(struct dev_context *devc,
+                                                                                        int time);
+SR_PRIV void bkprecision_1856d_select_input(struct dev_context *devc,
+                                                                                       int intput);
 
 #endif