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])
#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;
}
.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);
* 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;
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;
+}
#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