X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Frdtech-dps%2Fapi.c;h=9a08c792a35cd4fc6ab8f838bd8a0a519b78b275;hb=2142a79b53e7fb2d9d050c382b624820601c1bd6;hp=1829975455f6967c1935e573b00dd2a347431c9f;hpb=0549416e36730bb455068fd6a68b856d817fb087;p=libsigrok.git diff --git a/src/hardware/rdtech-dps/api.c b/src/hardware/rdtech-dps/api.c index 18299754..9a08c792 100644 --- a/src/hardware/rdtech-dps/api.c +++ b/src/hardware/rdtech-dps/api.c @@ -2,6 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2018 James Churchill + * Copyright (C) 2019 Frank Stettner * * 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 @@ -18,55 +19,214 @@ */ #include +#include #include "protocol.h" -static GSList *scan(struct sr_dev_driver *di, GSList *options) +static const uint32_t scanopts[] = { + SR_CONF_CONN, + SR_CONF_SERIALCOMM, + SR_CONF_MODBUSADDR, +}; + +static const uint32_t drvopts[] = { + SR_CONF_POWER_SUPPLY, +}; + +static const uint32_t devopts[] = { + SR_CONF_CONTINUOUS, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, + 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_ENABLED | SR_CONF_GET | SR_CONF_SET, + SR_CONF_REGULATION | 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_ACTIVE | SR_CONF_GET, + SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET, +}; + +/* Model ID, model name, max current, max voltage, max power */ +static const struct rdtech_dps_model supported_models[] = { + { 3005, "DPS3005", 5, 30, 160, 3, 2 }, + { 5005, "DPS5005", 5, 50, 250, 3, 2 }, + { 5205, "DPH5005", 5, 50, 250, 3, 2 }, + { 5015, "DPS5015", 15, 50, 750, 2, 2 }, + { 5020, "DPS5020", 20, 50, 1000, 2, 2 }, + { 8005, "DPS8005", 5, 80, 408, 3, 2 }, +}; + +static struct sr_dev_driver rdtech_dps_driver_info; + +static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus) { - struct drv_context *drvc; - GSList *devices; + const struct rdtech_dps_model *model = NULL; + struct dev_context *devc; + struct sr_dev_inst *sdi; + uint16_t id, version; + unsigned int i; + + int ret = rdtech_dps_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("Unknown model: %d.", id); + return NULL; + } - (void)options; + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("RDTech"); + sdi->model = g_strdup(model->name); + sdi->version = g_strdup_printf("v%d", version); + sdi->conn = modbus; + sdi->driver = &rdtech_dps_driver_info; + sdi->inst_type = SR_INST_MODBUS; - devices = NULL; - drvc = di->context; - drvc->instances = NULL; + sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V"); + sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I"); + sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "P"); - /* TODO: scan for devices, either based on a SR_CONF_CONN option - * or on a USB scan. */ + devc = g_malloc0(sizeof(struct dev_context)); + sr_sw_limits_init(&devc->limits); + devc->model = model; + devc->current_multiplier = pow(10.0, model->current_digits); + devc->voltage_multiplier = pow(10.0, model->voltage_digits); + + 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->context, 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; } static int dev_open(struct sr_dev_inst *sdi) { - (void)sdi; + struct sr_modbus_dev_inst *modbus = sdi->conn; + + if (sr_modbus_open(modbus) < 0) + return SR_ERR; - /* TODO: get handle from sdi->conn and open it. */ + rdtech_dps_set_reg(sdi, REG_LOCK, 1); return SR_OK; } static int dev_close(struct sr_dev_inst *sdi) { - (void)sdi; + struct sr_modbus_dev_inst *modbus; - /* TODO: get handle from sdi->conn and close it. */ + modbus = sdi->conn; + if (!modbus) + return SR_ERR_BUG; - return SR_OK; + rdtech_dps_set_reg(sdi, REG_LOCK, 0); + + return sr_modbus_close(modbus); } static int config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { + struct dev_context *devc; int ret; + uint16_t ivalue; - (void)sdi; - (void)data; (void)cg; + devc = sdi->priv; + ret = SR_OK; switch (key) { - /* TODO */ + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + ret = sr_sw_limits_config_get(&devc->limits, key, data); + break; + case SR_CONF_ENABLED: + if ((ret = rdtech_dps_get_reg(sdi, REG_ENABLE, &ivalue)) == SR_OK) + *data = g_variant_new_boolean(ivalue); + break; + case SR_CONF_REGULATION: + if ((ret = rdtech_dps_get_reg(sdi, REG_CV_CC, &ivalue)) != SR_OK) + break; + *data = g_variant_new_string((ivalue == MODE_CC) ? "CC" : "CV"); + break; + case SR_CONF_VOLTAGE: + if ((ret = rdtech_dps_get_reg(sdi, REG_UOUT, &ivalue)) == SR_OK) + *data = g_variant_new_double((float)ivalue / devc->voltage_multiplier); + break; + case SR_CONF_VOLTAGE_TARGET: + if ((ret = rdtech_dps_get_reg(sdi, REG_USET, &ivalue)) == SR_OK) + *data = g_variant_new_double((float)ivalue / devc->voltage_multiplier); + break; + case SR_CONF_CURRENT: + if ((ret = rdtech_dps_get_reg(sdi, REG_IOUT, &ivalue)) == SR_OK) + *data = g_variant_new_double((float)ivalue / devc->current_multiplier); + break; + case SR_CONF_CURRENT_LIMIT: + if ((ret = rdtech_dps_get_reg(sdi, REG_ISET, &ivalue)) == SR_OK) + *data = g_variant_new_double((float)ivalue / devc->current_multiplier); + break; + case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: + *data = g_variant_new_boolean(TRUE); + break; + case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE: + if ((ret = rdtech_dps_get_reg(sdi, REG_PROTECT, &ivalue)) == SR_OK) + *data = g_variant_new_boolean(ivalue == STATE_OVP); + break; + case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD: + if ((ret = rdtech_dps_get_reg(sdi, PRE_OVPSET, &ivalue)) == SR_OK) + *data = g_variant_new_double((float)ivalue / devc->voltage_multiplier); + break; + case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: + *data = g_variant_new_boolean(TRUE); + break; + case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE: + if ((ret = rdtech_dps_get_reg(sdi, REG_PROTECT, &ivalue)) == SR_OK) + *data = g_variant_new_boolean(ivalue == STATE_OCP); + break; + case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD: + if ((ret = rdtech_dps_get_reg(sdi, PRE_OCPSET, &ivalue)) == SR_OK) + *data = g_variant_new_double((float)ivalue / devc->current_multiplier); + break; default: return SR_ERR_NA; } @@ -77,63 +237,107 @@ static int config_get(uint32_t key, GVariant **data, static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - int ret; + struct dev_context *devc; - (void)sdi; - (void)data; (void)cg; - ret = SR_OK; + devc = sdi->priv; + switch (key) { - /* TODO */ + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + return sr_sw_limits_config_set(&devc->limits, key, data); + case SR_CONF_ENABLED: + return rdtech_dps_set_reg(sdi, REG_ENABLE, g_variant_get_boolean(data)); + case SR_CONF_VOLTAGE_TARGET: + return rdtech_dps_set_reg(sdi, REG_USET, + g_variant_get_double(data) * devc->voltage_multiplier); + case SR_CONF_CURRENT_LIMIT: + return rdtech_dps_set_reg(sdi, REG_ISET, + g_variant_get_double(data) * devc->current_multiplier); + case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD: + return rdtech_dps_set_reg(sdi, PRE_OVPSET, + g_variant_get_double(data) * devc->voltage_multiplier); + case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD: + return rdtech_dps_set_reg(sdi, PRE_OCPSET, + g_variant_get_double(data) * devc->current_multiplier); 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) { - int ret; + struct dev_context *devc; - (void)sdi; - (void)data; - (void)cg; + devc = (sdi) ? sdi->priv : NULL; - ret = SR_OK; 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_VOLTAGE_TARGET: + *data = std_gvar_min_max_step(0.0, devc->model->max_voltage, + 1 / devc->voltage_multiplier); + break; + case SR_CONF_CURRENT_LIMIT: + *data = std_gvar_min_max_step(0.0, devc->model->max_current, + 1 / devc->current_multiplier); + break; default: return SR_ERR_NA; } - return ret; + return SR_OK; } static int dev_acquisition_start(const struct sr_dev_inst *sdi) { - /* TODO: configure hardware, reset acquisition state, set up - * callbacks and send header packet. */ + struct dev_context *devc; + struct sr_modbus_dev_inst *modbus; + uint16_t registers[3]; + int ret; - (void)sdi; + modbus = sdi->conn; + devc = sdi->priv; + + /* Prefill actual states */ + ret = rdtech_dps_read_holding_registers(modbus, REG_PROTECT, 3, registers); + if (ret != SR_OK) + return ret; + devc->actual_ovp_state = RB16(registers + 0) == STATE_OVP; + devc->actual_ocp_state = RB16(registers + 0) == STATE_OCP; + devc->actual_regulation_state = RB16(registers + 1); + devc->actual_output_state = RB16(registers + 2); + + if ((ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10, + rdtech_dps_receive_data, (void *)sdi)) != SR_OK) + return ret; + + sr_sw_limits_acquisition_start(&devc->limits); + std_session_send_df_header(sdi); return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi) { - /* TODO: stop acquisition. */ + struct sr_modbus_dev_inst *modbus; - (void)sdi; + std_session_send_df_end(sdi); + + modbus = sdi->conn; + sr_modbus_source_remove(sdi->session, modbus); return SR_OK; } -SR_PRIV struct sr_dev_driver rdtech_dps_driver_info = { +static struct sr_dev_driver rdtech_dps_driver_info = { .name = "rdtech-dps", - .longname = "RDTech DPS", + .longname = "RDTech DPS/DPH series power supply", .api_version = 1, .init = std_init, .cleanup = std_cleanup, @@ -149,5 +353,4 @@ SR_PRIV struct sr_dev_driver rdtech_dps_driver_info = { .dev_acquisition_stop = dev_acquisition_stop, .context = NULL, }; - SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info);