]> sigrok.org Git - libsigrok.git/commitdiff
Actual implementation for the maynuo-m97 driver.
authorAurelien Jacobs <redacted>
Sun, 22 Feb 2015 23:28:06 +0000 (00:28 +0100)
committerUwe Hermann <redacted>
Sun, 7 Jun 2015 18:15:08 +0000 (20:15 +0200)
src/hardware/maynuo-m97/api.c
src/hardware/maynuo-m97/protocol.c
src/hardware/maynuo-m97/protocol.h

index eb92c263980dd3162abd2f97b1f0fd86b044b103..f1afdfe6cb886158bb92a8d0b7b58f52c0ff185f 100644 (file)
 
 #include "protocol.h"
 
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+       SR_CONF_MODBUSADDR
+};
+
+static const uint32_t drvopts[] = {
+       SR_CONF_ELECTRONIC_LOAD,
+};
+
+static const uint32_t devopts[] = {
+       SR_CONF_CONTINUOUS | SR_CONF_SET,
+       SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
+};
+
+static const uint32_t devopts_cg[] = {
+       SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_REGULATION | SR_CONF_GET,
+       SR_CONF_VOLTAGE | SR_CONF_GET,
+       SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_CURRENT | SR_CONF_GET,
+       SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
+       SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET,
+       SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
+       SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
+       SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET,
+       SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET,
+};
+
+/* The IDs in this list are only guessed and needs to be verified
+   against some real hardware. If at least a few of them matches,
+   it will probably be safe to enable the others. */
+static const struct maynuo_m97_model supported_models[] = {
+//     {  53, "M9711"     ,   30, 150,    150 },
+//     {  54, "M9712"     ,   30, 150,    300 },
+//     {  55, "M9712C"    ,   60, 150,    300 },
+//     {  56, "M9713"     ,  120, 150,    600 },
+//     {  57, "M9712B"    ,   15, 500,    300 },
+//     {  58, "M9713B"    ,   30, 500,    600 },
+//     {  59, "M9714"     ,  240, 150,   1200 },
+//     {  60, "M9714B"    ,   60, 500,   1200 },
+//     {  61, "M9715"     ,  240, 150,   1800 },
+//     {  62, "M9715B"    ,  120, 500,   1800 },
+//     {  63, "M9716"     ,  240, 150,   2400 },
+//     {  64, "M9716B"    ,  120, 500,   2400 },
+//     {  65, "M9717C"    ,  480, 150,   3600 },
+//     {  66, "M9717"     ,  240, 150,   3600 },
+//     {  67, "M9717B"    ,  120, 500,   3600 },
+//     {  68, "M9718"     ,  240, 150,   6000 },
+//     {  69, "M9718B"    ,  120, 500,   6000 },
+//     {  70, "M9718D"    ,  240, 500,   6000 },
+//     {  71, "M9836"     ,  500, 150,  20000 },
+//     {  72, "M9836B"    ,  240, 500,  20000 },
+//     {  73, "M9838B"    ,  240, 500,  50000 },
+//     {  74, "M9839B"    ,  240, 500, 100000 },
+//     {  75, "M9840B"    ,  500, 500, 200000 },
+//     {  76, "M9840"     , 1500, 150, 200000 },
+//     {  77, "M9712B30"  ,   30, 500,    300 },
+//     {  78, "M9718E"    ,  120, 600,   6000 },
+//     {  79, "M9718F"    ,  480, 150,   6000 },
+//     {  80, "M9716E"    ,  480, 150,   3000 },
+//     {  81, "M9710"     ,   30, 150,    150 },
+//     {  82, "M9834"     ,  500, 150,  10000 },
+//     {  83, "M9835"     ,  500, 150,  15000 },
+//     {  84, "M9835B"    ,  240, 500,  15000 },
+//     {  85, "M9837"     ,  500, 150,  35000 },
+//     {  86, "M9837B"    ,  240, 500,  35000 },
+//     {  87, "M9838"     ,  500, 150,  50000 },
+//     {  88, "M9839"     ,  500, 150, 100000 },
+//     {  89, "M9835C"    , 1000, 150,  15000 }, /* ?? */
+//     {  90, "M9836C"    , 1000, 150,  20000 }, /* ?? */
+//     {  91, "M9718F-300",  480, 300,   6000 }, /* ?? */
+//     {  92, "M9836F"    , 1000, 150,  20000 }, /* ?? */
+//     {  93, "M9836E"    ,  240, 600,  20000 }, /* ?? */
+//     {  94, "M9717D"    ,  240, 500,   3600 }, /* ?? */
+//     {  95, "M9836B-720",  240, 720,  20000 }, /* ?? */
+//     {  96, "M9834H"    ,  500, 150,  10000 }, /* ?? */
+//     {  97, "M9836H"    ,  500, 150,  20000 }, /* ?? */
+//     {  98, "M9718F-500",  480, 500,   6000 }, /* ?? */
+//     {  99, "M9834B"    ,  240, 500,  10000 }, /* ?? */
+//     { 100, "M9811"     ,   30, 150,    200 },
+       { 101, "M9812"     ,   30, 150,    300 },
+//     { 102, "M9812B"    ,   15, 500,    300 },
+};
+
 SR_PRIV struct sr_dev_driver maynuo_m97_driver_info;
 
 static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
@@ -26,19 +115,85 @@ static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
        return std_init(sr_ctx, di, LOG_PREFIX);
 }
 
-static GSList *scan(struct sr_dev_driver *di, GSList *options)
+static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
 {
-       struct drv_context *drvc;
-       GSList *devices;
+       const struct maynuo_m97_model *model = NULL;
+       struct dev_context *devc;
+       struct sr_dev_inst *sdi;
+       struct sr_channel_group *cg;
+       struct sr_channel *ch;
+       uint16_t id, version;
+       unsigned int i;
+
+       int ret = maynuo_m97_get_model_version(modbus, &id, &version);
+       if (ret != SR_OK)
+               return NULL;
+       for (i=0; i<ARRAY_SIZE(supported_models); i++)
+               if (id == supported_models[i].id) {
+                       model = &supported_models[i];
+                       break;
+               }
+       if (model == NULL) {
+               sr_err("Unkown model: %d.", id);
+               return NULL;
+       }
+
+       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       sdi->status = SR_ST_ACTIVE;
+       sdi->vendor = g_strdup("Maynuo");
+       sdi->model = g_strdup(model->name);
+       sdi->version = g_strdup_printf("v%d.%d", version/10, version%10);
+       sdi->conn = modbus;
+       sdi->driver = &maynuo_m97_driver_info;
+       sdi->inst_type = SR_INST_MODBUS;
 
-       (void)options;
+       cg = g_malloc0(sizeof(struct sr_channel_group));
+       cg->name = g_strdup("1");
+       sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
 
-       devices = NULL;
-       drvc = di->priv;
-       drvc->instances = NULL;
+       ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V1");
+       cg->channels = g_slist_append(cg->channels, ch);
 
-       /* TODO: scan for devices, either based on a SR_CONF_CONN option
-        * or on a USB scan. */
+       ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I1");
+       cg->channels = g_slist_append(cg->channels, ch);
+
+       devc = g_malloc0(sizeof(struct dev_context));
+       devc->model = model;
+
+       sdi->priv = devc;
+
+       return sdi;
+}
+
+static int config_compare(gconstpointer a, gconstpointer b)
+{
+       const struct sr_config *ac = a, *bc = b;
+       return ac->key != bc->key;
+}
+
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
+{
+       struct sr_config default_serialcomm = {
+           .key = SR_CONF_SERIALCOMM,
+           .data = g_variant_new_string("9600/8n1"),
+       };
+       struct sr_config default_modbusaddr = {
+           .key = SR_CONF_MODBUSADDR,
+           .data = g_variant_new_uint64(1),
+       };
+       GSList *opts = options, *devices;
+
+       if (!g_slist_find_custom(options, &default_serialcomm, config_compare))
+               opts = g_slist_prepend(opts, &default_serialcomm);
+       if (!g_slist_find_custom(options, &default_modbusaddr, config_compare))
+               opts = g_slist_prepend(opts, &default_modbusaddr);
+
+       devices = sr_modbus_scan(di->priv, opts, probe_device);
+
+       while (opts != options)
+               opts = g_slist_delete_link(opts, opts);
+       g_variant_unref(default_serialcomm.data);
+       g_variant_unref(default_modbusaddr.data);
 
        return devices;
 }
@@ -50,52 +205,137 @@ static GSList *dev_list(const struct sr_dev_driver *di)
 
 static int dev_clear(const struct sr_dev_driver *di)
 {
-       return std_dev_clear(di, NULL);
+       return std_dev_clear(di, g_free);
 }
 
 static int dev_open(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
+       struct sr_modbus_dev_inst *modbus = sdi->conn;
 
-       /* TODO: get handle from sdi->conn and open it. */
+       if (sr_modbus_open(modbus) < 0)
+               return SR_ERR;
 
        sdi->status = SR_ST_ACTIVE;
 
+       maynuo_m97_set_bit(modbus, PC1, 1);
+
        return SR_OK;
 }
 
 static int dev_close(struct sr_dev_inst *sdi)
 {
-       (void)sdi;
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
 
-       /* TODO: get handle from sdi->conn and close it. */
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
 
-       sdi->status = SR_ST_INACTIVE;
+       modbus = sdi->conn;
+
+       if (modbus) {
+               devc = sdi->priv;
+               if (devc->expecting_registers) {
+                       /* Wait for the last data that was requested from the device. */
+                       uint16_t registers[devc->expecting_registers];
+                       sr_modbus_read_holding_registers(modbus, -1,
+                                                        devc->expecting_registers,
+                                                        registers);
+               }
+
+               maynuo_m97_set_bit(modbus, PC1, 0);
+
+               if (sr_modbus_close(modbus) < 0)
+                       return SR_ERR;
+               sdi->status = SR_ST_INACTIVE;
+       }
 
        return SR_OK;
 }
 
 static int cleanup(const struct sr_dev_driver *di)
 {
-       dev_clear(di);
-
-       /* TODO: free other driver resources, if any. */
-
-       return SR_OK;
+       return dev_clear(di);
 }
 
 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;
+       struct sr_modbus_dev_inst *modbus;
+       enum maynuo_m97_mode mode;
+       int ret, ivalue;
+       float fvalue;
 
-       (void)sdi;
-       (void)data;
        (void)cg;
 
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
        ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_LIMIT_SAMPLES:
+               *data = g_variant_new_uint64(devc->limit_samples);
+               break;
+       case SR_CONF_LIMIT_MSEC:
+               *data = g_variant_new_uint64(devc->limit_msec);
+               break;
+       case SR_CONF_ENABLED:
+               if ((ret = maynuo_m97_get_bit(modbus, ISTATE, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue);
+               break;
+       case SR_CONF_REGULATION:
+               if ((ret = maynuo_m97_get_bit(modbus, UNREG, &ivalue)) != SR_OK)
+                       break;
+               if (ivalue)
+                       *data = g_variant_new_string("UR");
+               else if ((ret = maynuo_m97_get_mode(modbus, &mode)) == SR_OK)
+                       *data = g_variant_new_string(maynuo_m97_mode_to_str(mode));
+               break;
+       case SR_CONF_VOLTAGE:
+               if ((ret = maynuo_m97_get_float(modbus, U, &fvalue)) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               if ((ret = maynuo_m97_get_float(modbus, UFIX, &fvalue)) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
+       case SR_CONF_CURRENT:
+               if ((ret = maynuo_m97_get_float(modbus, I, &fvalue)) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               if ((ret = maynuo_m97_get_float(modbus, IFIX, &fvalue)) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
+               *data = g_variant_new_boolean(TRUE);
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
+               if ((ret = maynuo_m97_get_bit(modbus, UOVER, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue);
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+               if ((ret = maynuo_m97_get_float(modbus, UMAX, &fvalue)) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
+               *data = g_variant_new_boolean(TRUE);
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
+               if ((ret = maynuo_m97_get_bit(modbus, IOVER, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue);
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+               if ((ret = maynuo_m97_get_float(modbus, IMAX, &fvalue)) == SR_OK)
+                       *data = g_variant_new_double(fvalue);
+               break;
+       case SR_CONF_OVER_TEMPERATURE_PROTECTION:
+               *data = g_variant_new_boolean(TRUE);
+               break;
+       case SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE:
+               if ((ret = maynuo_m97_get_bit(modbus, HEAT, &ivalue)) == SR_OK)
+                       *data = g_variant_new_boolean(ivalue);
+               break;
        default:
                return SR_ERR_NA;
        }
@@ -106,6 +346,8 @@ static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *s
 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_modbus_dev_inst *modbus;
        int ret;
 
        (void)data;
@@ -114,9 +356,32 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
 
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
        ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_LIMIT_SAMPLES:
+               devc->limit_samples = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_LIMIT_MSEC:
+               devc->limit_msec = g_variant_get_uint64(data);
+               break;
+       case SR_CONF_ENABLED:
+               ret = maynuo_m97_set_input(modbus, g_variant_get_boolean(data));
+               break;
+       case SR_CONF_VOLTAGE_TARGET:
+               ret = maynuo_m97_set_float(modbus, UFIX, g_variant_get_double(data));
+               break;
+       case SR_CONF_CURRENT_LIMIT:
+               ret = maynuo_m97_set_float(modbus, IFIX, g_variant_get_double(data));
+               break;
+       case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
+               ret = maynuo_m97_set_float(modbus, UMAX, g_variant_get_double(data));
+               break;
+       case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
+               ret = maynuo_m97_set_float(modbus, IMAX, g_variant_get_double(data));
+               break;
        default:
                ret = SR_ERR_NA;
        }
@@ -127,17 +392,61 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd
 static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi,
                const struct sr_channel_group *cg)
 {
+       struct dev_context *devc;
+       GVariantBuilder gvb;
        int ret;
 
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       /* Always available, even without sdi. */
+       if (key == SR_CONF_SCAN_OPTIONS) {
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                               scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
+               return SR_OK;
+       } else if (key == SR_CONF_DEVICE_OPTIONS && !sdi) {
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                               drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
+               return SR_OK;
+       }
+
+       if (!sdi)
+               return SR_ERR_ARG;
+       devc = sdi->priv;
 
        ret = SR_OK;
-       switch (key) {
-       /* TODO */
-       default:
-               return SR_ERR_NA;
+       if (!cg) {
+               /* No channel group: global options. */
+               switch (key) {
+               case SR_CONF_DEVICE_OPTIONS:
+                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       } else {
+               switch (key) {
+               case SR_CONF_DEVICE_OPTIONS:
+                       *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
+                                       devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
+                       break;
+               case SR_CONF_VOLTAGE_TARGET:
+                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+                       /* Min, max, write resolution. */
+                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
+                       g_variant_builder_add_value(&gvb, g_variant_new_double(devc->model->max_voltage));
+                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.001));
+                       *data = g_variant_builder_end(&gvb);
+                       break;
+               case SR_CONF_CURRENT_LIMIT:
+                       g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
+                       /* Min, max, step. */
+                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
+                       g_variant_builder_add_value(&gvb, g_variant_new_double(devc->model->max_current));
+                       g_variant_builder_add_value(&gvb, g_variant_new_double(0.0001));
+                       *data = g_variant_builder_end(&gvb);
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
        }
 
        return ret;
@@ -146,33 +455,54 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *
 static int dev_acquisition_start(const struct sr_dev_inst *sdi,
                void *cb_data)
 {
-       (void)sdi;
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+       int ret;
+
        (void)cb_data;
 
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
 
-       /* TODO: configure hardware, reset acquisition state, set up
-        * callbacks and send header packet. */
+       modbus = sdi->conn;
+       devc = sdi->priv;
 
-       return SR_OK;
+       if ((ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10,
+                       maynuo_m97_receive_data, (void *)sdi)) != SR_OK)
+               return ret;
+
+       /* Send header packet to the session bus. */
+       std_session_send_df_header(sdi, LOG_PREFIX);
+
+       devc->num_samples = 0;
+       devc->starttime = g_get_monotonic_time();
+
+       return maynuo_m97_capture_start(sdi);
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
 {
+       struct sr_modbus_dev_inst *modbus;
+       struct sr_datafeed_packet packet;
+
        (void)cb_data;
 
        if (sdi->status != SR_ST_ACTIVE)
                return SR_ERR_DEV_CLOSED;
 
-       /* TODO: stop acquisition. */
+       /* End of last frame. */
+       packet.type = SR_DF_END;
+       sr_session_send(sdi, &packet);
+
+       modbus = sdi->conn;
+       sr_modbus_source_remove(sdi->session, modbus);
 
        return SR_OK;
 }
 
 SR_PRIV struct sr_dev_driver maynuo_m97_driver_info = {
        .name = "maynuo-m97",
-       .longname = "maynuo-m97",
+       .longname = "maynuo M97/M98 series",
        .api_version = 1,
        .init = init,
        .cleanup = cleanup,
index e6bc0294f393a5b927d28feb2a2a61b66a98d9f5..5ef16ac730284da67562252df6ff85fa0930cc24 100644 (file)
 
 #include "protocol.h"
 
+SR_PRIV int maynuo_m97_get_bit(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_coil address, int *value)
+{
+       uint8_t coil;
+       int ret = sr_modbus_read_coils(modbus, address, 1, &coil);
+       *value = coil & 1;
+       return ret;
+}
+
+SR_PRIV int maynuo_m97_set_bit(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_coil address, int value)
+{
+       return sr_modbus_write_coil(modbus, address, value);
+}
+
+SR_PRIV int maynuo_m97_get_float(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_register address, float *value)
+{
+       uint16_t registers[2];
+       int ret = sr_modbus_read_holding_registers(modbus, address, 2, registers);
+       if (ret == SR_OK)
+               *value = RBFL(registers);
+       return ret;
+}
+
+SR_PRIV int maynuo_m97_set_float(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_register address, float value)
+{
+       uint16_t registers[2];
+       WBFL(registers, value);
+       return sr_modbus_write_multiple_registers(modbus, address, 2, registers);
+}
+
+
+static int maynuo_m97_cmd(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_mode cmd)
+{
+       uint16_t registers[1];
+       WB16(registers, cmd);
+       return sr_modbus_write_multiple_registers(modbus, CMD, 1, registers);
+}
+
+SR_PRIV int maynuo_m97_get_mode(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_mode *mode)
+{
+       uint16_t registers[1];
+       int ret;
+       ret = sr_modbus_read_holding_registers(modbus, SETMODE, 1, registers);
+       *mode = RB16(registers) & 0xFF;
+       return ret;
+}
+
+SR_PRIV int maynuo_m97_set_mode(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_mode mode)
+{
+       return maynuo_m97_cmd(modbus, mode);
+}
+
+SR_PRIV int maynuo_m97_set_input(struct sr_modbus_dev_inst *modbus, int enable)
+{
+       enum maynuo_m97_mode mode;
+       int ret;
+       if ((ret = maynuo_m97_get_mode(modbus, &mode)) != SR_OK)
+               return ret;
+       if ((ret = maynuo_m97_cmd(modbus, enable ? INPUT_ON : INPUT_OFF)) != SR_OK)
+               return ret;
+       return maynuo_m97_set_mode(modbus, mode);
+}
+
+SR_PRIV int maynuo_m97_get_model_version(struct sr_modbus_dev_inst *modbus,
+               uint16_t *model, uint16_t *version)
+{
+       uint16_t registers[2];
+       int ret;
+       ret = sr_modbus_read_holding_registers(modbus, MODEL, 2, registers);
+       *model   = RB16(registers+0);
+       *version = RB16(registers+1);
+       return ret;
+}
+
+
+SR_PRIV const char *maynuo_m97_mode_to_str(enum maynuo_m97_mode mode)
+{
+       switch(mode) {
+       case CC:             return "CC";
+       case CV:             return "CV";
+       case CW:             return "CP";
+       case CR:             return "CR";
+       case CC_SOFT_START:  return "CC Soft Start";
+       case DYNAMIC:        return "Dynamic";
+       case SHORT_CIRCUIT:  return "Short Circuit";
+       case LIST:           return "List Mode";
+       case CC_L_AND_UL:    return "CC Loading and Unloading";
+       case CV_L_AND_UL:    return "CV Loading and Unloading";
+       case CW_L_AND_UL:    return "CP Loading and Unloading";
+       case CR_L_AND_UL:    return "CR Loading and Unloading";
+       case CC_TO_CV:       return "CC + CV";
+       case CR_TO_CV:       return "CR + CV";
+       case BATTERY_TEST:   return "Battery Test";
+       case CV_SOFT_START:  return "CV Soft Start";
+       default:             return "UNKNOWN";
+       }
+}
+
+
+static void maynuo_m97_session_send_value(const struct sr_dev_inst *sdi, struct sr_channel *ch, float value, enum sr_mq mq, enum sr_unit unit)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_analog analog;
+
+       analog.channels = g_slist_append(NULL, ch);
+       analog.num_samples = 1;
+       analog.data = &value;
+       analog.mq = mq;
+       analog.unit = unit;
+       analog.mqflags = SR_MQFLAG_DC;
+
+       packet.type = SR_DF_ANALOG;
+       packet.payload = &analog;
+       sr_session_send(sdi, &packet);
+       g_slist_free(analog.channels);
+}
+
+SR_PRIV int maynuo_m97_capture_start(const struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_modbus_dev_inst *modbus;
+       int ret;
+
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       if ((ret = sr_modbus_read_holding_registers(modbus, U, 4, NULL)) == SR_OK)
+               devc->expecting_registers = 4;
+       return ret;
+}
+
 SR_PRIV int maynuo_m97_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_modbus_dev_inst *modbus;
+       struct sr_datafeed_packet packet;
+       uint16_t registers[4];
+       int64_t t;
 
        (void)fd;
+       (void)revents;
 
        if (!(sdi = cb_data))
                return TRUE;
 
-       if (!(devc = sdi->priv))
+       modbus = sdi->conn;
+       devc = sdi->priv;
+
+       devc->expecting_registers = 0;
+       if (sr_modbus_read_holding_registers(modbus, -1, 4, registers) == SR_OK) {
+               packet.type = SR_DF_FRAME_BEGIN;
+               sr_session_send(cb_data, &packet);
+
+               maynuo_m97_session_send_value(sdi, sdi->channels->data,
+                                             RBFL(registers + 0),
+                                             SR_MQ_VOLTAGE, SR_UNIT_VOLT);
+               maynuo_m97_session_send_value(sdi, sdi->channels->next->data,
+                                             RBFL(registers + 2),
+                                             SR_MQ_CURRENT, SR_UNIT_AMPERE);
+
+               packet.type = SR_DF_FRAME_END;
+               sr_session_send(cb_data, &packet);
+               devc->num_samples++;
+       }
+
+       if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
+               sr_info("Requested number of samples reached.");
+               sdi->driver->dev_acquisition_stop(sdi, cb_data);
                return TRUE;
+       }
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       if (devc->limit_msec) {
+               t = (g_get_monotonic_time() - devc->starttime) / 1000;
+               if (t > (int64_t)devc->limit_msec) {
+                       sr_info("Requested time limit reached.");
+                       sdi->driver->dev_acquisition_stop(sdi, cb_data);
+                       return TRUE;
+               }
        }
 
+       maynuo_m97_capture_start(sdi);
        return TRUE;
 }
index 57a6e9546b12caff3d41f3b32fbe14b70dc39b36..7cb1f632a1152d7297626063389c861e409bfd49 100644 (file)
 #define LIBSIGROK_HARDWARE_MAYNUO_M97_PROTOCOL_H
 
 #include <stdint.h>
-#include <glib.h>
 #include "libsigrok.h"
 #include "libsigrok-internal.h"
 
 #define LOG_PREFIX "maynuo-m97"
 
+struct maynuo_m97_model {
+       unsigned int id;
+       const char *name;
+       unsigned int max_current;
+       unsigned int max_voltage;
+       unsigned int max_power;
+};
+
 /** Private, per-device-instance driver context. */
 struct dev_context {
        /* Model-specific information */
+       const struct maynuo_m97_model *model;
 
        /* Acquisition settings */
+       uint64_t limit_samples;
+       uint64_t limit_msec;
 
        /* Operational state */
+       uint64_t num_samples;
+       int64_t starttime;
+       int expecting_registers;
+};
 
-       /* Temporary state across callbacks */
+enum maynuo_m97_coil {
+       PC1        = 0x0500,
+       PC2        = 0X0501,
+       TRIG       = 0x0502,
+       REMOTE     = 0x0503,
+       ISTATE     = 0x0510,
+       TRACK      = 0x0511,
+       MEMORY     = 0x0512,
+       VOICEEN    = 0x0513,
+       CONNECT    = 0x0514,
+       ATEST      = 0x0515,
+       ATESTUN    = 0x0516,
+       ATESTPASS  = 0x0517,
+       IOVER      = 0x0520,
+       UOVER      = 0x0521,
+       POVER      = 0x0522,
+       HEAT       = 0x0523,
+       REVERSE    = 0x0524,
+       UNREG      = 0x0525,
+       ERREP      = 0x0526,
+       ERRCAL     = 0x0527,
+};
 
+enum maynuo_m97_register {
+       CMD        = 0x0A00,
+       IFIX       = 0x0A01,
+       UFIX       = 0x0A03,
+       PFIX       = 0x0A05,
+       RFIX       = 0x0A07,
+       TMCCS      = 0x0A09,
+       TMCVS      = 0x0A0B,
+       UCCONSET   = 0x0A0D,
+       UCCOFFSET  = 0x0A0F,
+       UCVONSET   = 0x0A11,
+       UCVOFFSET  = 0x0A13,
+       UCPONSET   = 0x0A15,
+       UCPOFFSET  = 0x0A17,
+       UCRONSET   = 0x0A19,
+       UCROFFSET  = 0x0A1B,
+       UCCCV      = 0x0A1D,
+       UCRCV      = 0x0A1F,
+       IA         = 0x0A21,
+       IB         = 0x0A23,
+       TMAWD      = 0x0A25,
+       TMBWD      = 0x0A27,
+       TMTRANSRIS = 0x0A29,
+       TMTRANSFAL = 0x0A2B,
+       MODETRAN   = 0x0A2D,
+       UBATTEND   = 0x0A2E,
+       BATT       = 0x0A30,
+       SERLIST    = 0x0A32,
+       SERATEST   = 0x0A33,
+       IMAX       = 0x0A34,
+       UMAX       = 0x0A36,
+       PMAX       = 0x0A38,
+       ILCAL      = 0x0A3A,
+       IHCAL      = 0x0A3C,
+       ULCAL      = 0x0A3E,
+       UHCAL      = 0x0A40,
+       TAGSCAL    = 0x0A42,
+       U          = 0x0B00,
+       I          = 0x0B02,
+       SETMODE    = 0x0B04,
+       INPUTMODE  = 0x0B05,
+       MODEL      = 0x0B06,
+       EDITION    = 0x0B07,
 };
 
+enum maynuo_m97_mode {
+       CC            =  1,
+       CV            =  2,
+       CW            =  3,
+       CR            =  4,
+       CC_SOFT_START = 20,
+       DYNAMIC       = 25,
+       SHORT_CIRCUIT = 26,
+       LIST          = 27,
+       CC_L_AND_UL   = 30,
+       CV_L_AND_UL   = 31,
+       CW_L_AND_UL   = 32,
+       CR_L_AND_UL   = 33,
+       CC_TO_CV      = 34,
+       CR_TO_CV      = 36,
+       BATTERY_TEST  = 38,
+       CV_SOFT_START = 39,
+       SYSTEM_PARAM  = 41,
+       INPUT_ON      = 42,
+       INPUT_OFF     = 43,
+};
+
+SR_PRIV int maynuo_m97_get_bit(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_coil address, int *value);
+SR_PRIV int maynuo_m97_set_bit(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_coil address, int value);
+SR_PRIV int maynuo_m97_get_float(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_register address, float *value);
+SR_PRIV int maynuo_m97_set_float(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_register address, float value);
+
+SR_PRIV int maynuo_m97_get_mode(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_mode *mode);
+SR_PRIV int maynuo_m97_set_mode(struct sr_modbus_dev_inst *modbus,
+               enum maynuo_m97_mode mode);
+SR_PRIV int maynuo_m97_set_input(struct sr_modbus_dev_inst *modbus, int enable);
+SR_PRIV int maynuo_m97_get_model_version(struct sr_modbus_dev_inst *modbus,
+               uint16_t *model, uint16_t *version);
+
+SR_PRIV const char *maynuo_m97_mode_to_str(enum maynuo_m97_mode mode);
+
+SR_PRIV int maynuo_m97_capture_start(const struct sr_dev_inst *sdi);
 SR_PRIV int maynuo_m97_receive_data(int fd, int revents, void *cb_data);
 
 #endif