rdtech-dps: introduce support for RD6006 and other Riden RD models

The RD devices differ from DPS devices in their default communication
bitrate (115200), register content endianess (BE), and register set
layout (addresses for registers). In either case 16bit registers get
accessed by means of Modbus communication over a serial channel. The
interpretation of the registers' values mostly is the same as for DPS
devices once a register with the same meaning got identified. Device
identification includes a 32bit serial number which DPS devices appear
to not provide.

All other product features are the same, and map to identical sigrok
mechanisms. That's why re-using the rdtech-dps driver to add rdtech-rd
support is considered desirable. This implementation shares all code
"above" the raw register addressing and raw value interpretation. All
logical processing, configuration interface, state tracking and data
submission to the session feed, are shared among the different device
types.

Declare support for the RD6006, RD6012, and RD6018 models. Their specs
were taken from the 2020.12.2 instruction manual. The driver was tested
with an RD6006 device (firmware version 1.28). Unfortunately automatic
device detection is not possible or will not be reliable, which is why
users need to specify the respective model by picking one of the two
drivers. Within a driver variant the device identification and use of
the device are automatically dealt with.
This commit is contained in:
Gerhard Sittig 2021-04-25 09:03:22 +02:00
parent d7a4dad881
commit 884ae8c021
3 changed files with 440 additions and 156 deletions

View File

@ -54,47 +54,82 @@ static const uint32_t devopts[] = {
/* Model ID, model name, max current/voltage/power, current/voltage digits. */
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 },
{ MODEL_DPS, 3005, "DPS3005", 5, 30, 160, 3, 2 },
{ MODEL_DPS, 5005, "DPS5005", 5, 50, 250, 3, 2 },
{ MODEL_DPS, 5205, "DPH5005", 5, 50, 250, 3, 2 },
{ MODEL_DPS, 5015, "DPS5015", 15, 50, 750, 2, 2 },
{ MODEL_DPS, 5020, "DPS5020", 20, 50, 1000, 2, 2 },
{ MODEL_DPS, 8005, "DPS8005", 5, 80, 408, 3, 2 },
/* All RD specs taken from the 2020.12.2 instruction manual. */
{ MODEL_RD , 6006, "RD6006" , 6, 60, 360, 3, 2 },
{ MODEL_RD , 6012, "RD6012" , 12, 60, 720, 2, 2 },
{ MODEL_RD , 6018, "RD6018" , 18, 60, 1080, 2, 2 },
};
static struct sr_dev_driver rdtech_dps_driver_info;
static struct sr_dev_driver rdtech_rd_driver_info;
static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus,
enum rdtech_dps_model_type model_type)
{
static const char *type_prefix[] = {
[MODEL_DPS] = "DPS",
[MODEL_RD] = "RD",
};
uint16_t id, version;
uint32_t serno;
int ret;
const struct rdtech_dps_model *model;
const struct rdtech_dps_model *model, *supported;
size_t i;
struct sr_dev_inst *sdi;
struct dev_context *devc;
ret = rdtech_dps_get_model_version(modbus, &id, &version);
ret = rdtech_dps_get_model_version(modbus,
model_type, &id, &version, &serno);
sr_dbg("probe: ret %d, type %s, model %u, vers %u, snr %u.",
ret, type_prefix[model_type], id, version, serno);
if (ret != SR_OK)
return NULL;
model = NULL;
for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
if (id == supported_models[i].id) {
model = &supported_models[i];
break;
}
supported = &supported_models[i];
if (model_type != supported->model_type)
continue;
if (id != supported->id)
continue;
model = supported;
break;
}
if (!model) {
sr_err("Unknown model: %u.", id);
sr_err("Unknown model: %s%u.", type_prefix[model_type], id);
return NULL;
}
sdi = g_malloc0(sizeof(*sdi));
sdi->status = SR_ST_INACTIVE;
sdi->vendor = g_strdup("RDTech");
sdi->model = g_strdup(model->name);
sdi->version = g_strdup_printf("v%u", version);
switch (model_type) {
case MODEL_DPS:
sdi->model = g_strdup(model->name);
sdi->version = g_strdup_printf("v%u", version);
sdi->driver = &rdtech_dps_driver_info;
break;
case MODEL_RD:
sdi->model = g_strdup(model->name);
sdi->version = g_strdup_printf("v%u.%u",
version / 100, version % 100);
if (serno)
sdi->serial_num = g_strdup_printf("%u", serno);
sdi->driver = &rdtech_rd_driver_info;
break;
default:
sr_err("Programming error, unhandled DPS/DPH/RD device type.");
g_free(sdi->vendor);
g_free(sdi);
return NULL;
}
sdi->conn = modbus;
sdi->driver = &rdtech_dps_driver_info;
sdi->inst_type = SR_INST_MODBUS;
sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
@ -112,23 +147,55 @@ static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus)
return sdi;
}
static struct sr_dev_inst *probe_device_dps(struct sr_modbus_dev_inst *modbus)
{
return probe_device(modbus, MODEL_DPS);
}
static struct sr_dev_inst *probe_device_rd(struct sr_modbus_dev_inst *modbus)
{
return probe_device(modbus, MODEL_RD);
}
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)
static GSList *scan(struct sr_dev_driver *di, GSList *options,
enum rdtech_dps_model_type model_type)
{
static const char *default_serialcomm_dps = "9600/8n1";
static const char *default_serialcomm_rd = "115200/8n1";
struct sr_config default_serialcomm = {
.key = SR_CONF_SERIALCOMM,
.data = g_variant_new_string("9600/8n1"),
.data = NULL,
};
struct sr_config default_modbusaddr = {
.key = SR_CONF_MODBUSADDR,
.data = g_variant_new_uint64(1),
};
GSList *opts, *devices;
const char *serialcomm;
struct sr_dev_inst *(*probe_func)(struct sr_modbus_dev_inst *modbus);
/* TODO See why di->context isn't available yet at this time. */
serialcomm = NULL;
probe_func = NULL;
if (di->context == &rdtech_dps_driver_info || model_type == MODEL_DPS) {
serialcomm = default_serialcomm_dps;
probe_func = probe_device_dps;
}
if (di->context == &rdtech_rd_driver_info || model_type == MODEL_RD) {
serialcomm = default_serialcomm_rd;
probe_func = probe_device_rd;
}
if (!probe_func)
return NULL;
if (serialcomm && *serialcomm)
default_serialcomm.data = g_variant_new_string(serialcomm);
opts = options;
if (!g_slist_find_custom(options, &default_serialcomm, config_compare))
@ -136,7 +203,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
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);
devices = sr_modbus_scan(di->context, opts, probe_func);
while (opts != options)
opts = g_slist_delete_link(opts, opts);
@ -146,6 +213,16 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
return devices;
}
static GSList *scan_dps(struct sr_dev_driver *di, GSList *options)
{
return scan(di, options, MODEL_DPS);
}
static GSList *scan_rd(struct sr_dev_driver *di, GSList *options)
{
return scan(di, options, MODEL_RD);
}
static int dev_open(struct sr_dev_inst *sdi)
{
struct sr_modbus_dev_inst *modbus;
@ -417,7 +494,7 @@ static struct sr_dev_driver rdtech_dps_driver_info = {
.api_version = 1,
.init = std_init,
.cleanup = std_cleanup,
.scan = scan,
.scan = scan_dps,
.dev_list = std_dev_list,
.dev_clear = std_dev_clear,
.config_get = config_get,
@ -430,3 +507,23 @@ static struct sr_dev_driver rdtech_dps_driver_info = {
.context = NULL,
};
SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info);
static struct sr_dev_driver rdtech_rd_driver_info = {
.name = "rdtech-rd",
.longname = "RDTech RD series power supply",
.api_version = 1,
.init = std_init,
.cleanup = std_cleanup,
.scan = scan_rd,
.dev_list = std_dev_list,
.dev_clear = std_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,
.context = NULL,
};
SR_REGISTER_DEV_DRIVER(rdtech_rd_driver_info);

View File

@ -26,36 +26,36 @@
#include "protocol.h"
enum rdtech_dps_register {
REG_USET = 0x00, /* Mirror of 0x50 */
REG_ISET = 0x01, /* Mirror of 0x51 */
REG_UOUT = 0x02,
REG_IOUT = 0x03,
REG_POWER = 0x04,
REG_UIN = 0x05,
REG_LOCK = 0x06,
REG_PROTECT = 0x07,
REG_CV_CC = 0x08,
REG_ENABLE = 0x09,
REG_BACKLIGHT = 0x0A, /* Mirror of 0x55 */
REG_MODEL = 0x0B,
REG_VERSION = 0x0C,
REG_DPS_USET = 0x00, /* Mirror of 0x50 */
REG_DPS_ISET = 0x01, /* Mirror of 0x51 */
REG_DPS_UOUT = 0x02,
REG_DPS_IOUT = 0x03,
REG_DPS_POWER = 0x04,
REG_DPS_UIN = 0x05,
REG_DPS_LOCK = 0x06,
REG_DPS_PROTECT = 0x07,
REG_DPS_CV_CC = 0x08,
REG_DPS_ENABLE = 0x09,
REG_DPS_BACKLIGHT = 0x0A, /* Mirror of 0x55 */
REG_DPS_MODEL = 0x0B,
REG_DPS_VERSION = 0x0C,
REG_PRESET = 0x23, /* Loads a preset into preset 0. */
REG_DPS_PRESET = 0x23, /* Loads a preset into preset 0. */
/*
* Add (preset * 0x10) to each of the following, for preset 1-9.
* Preset 0 regs below are the active output settings.
*/
PRE_USET = 0x50,
PRE_ISET = 0x51,
PRE_OVPSET = 0x52,
PRE_OCPSET = 0x53,
PRE_OPPSET = 0x54,
PRE_BACKLIGHT = 0x55,
PRE_DISABLE = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
PRE_BOOT = 0x57, /* Enable output at boot if 1. */
PRE_DPS_USET = 0x50,
PRE_DPS_ISET = 0x51,
PRE_DPS_OVPSET = 0x52,
PRE_DPS_OCPSET = 0x53,
PRE_DPS_OPPSET = 0x54,
PRE_DPS_BACKLIGHT = 0x55,
PRE_DPS_DISABLE = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
PRE_DPS_BOOT = 0x57, /* Enable output at boot if 1. */
};
#define REG_PRESET_STRIDE 0x10
#define PRE_DPS_STRIDE 0x10
enum rdtech_dps_protect_state {
STATE_NORMAL = 0,
@ -69,6 +69,37 @@ enum rdtech_dps_regulation_mode {
MODE_CC = 1,
};
enum rdtech_rd_register {
REG_RD_MODEL = 0, /* u16 */
REG_RD_SERIAL = 1, /* u32 */
REG_RD_FIRMWARE = 3, /* u16 */
REG_RD_TEMP_INT = 4, /* 2x u16 */
REG_RD_TEMP_INT_F = 6, /* 2x u16 */
REG_RD_VOLT_TGT = 8, /* u16 */
REG_RD_CURR_LIM = 9, /* u16 */
REG_RD_VOLTAGE = 10, /* u16 */
REG_RD_CURRENT = 11, /* u16 */
REG_RD_ENERGY = 12, /* u16 */
REG_RD_POWER = 13, /* u16 */
REG_RD_VOLT_IN = 14, /* u16 */
REG_RD_PROTECT = 16, /* u16 */
REG_RD_REGULATION = 17, /* u16 */
REG_RD_ENABLE = 18, /* u16 */
/*
* Battery at 32 == 0x20 pp:
* Mode, voltage, temperature, capacity, energy.
*/
/*
* Date/time at 48 == 0x30 pp:
* Year, month, day, hour, minute, second.
*/
/* Backlight at 72 == 0x48. */
REG_RD_OVP_THR = 82, /* 0x52 */
REG_RD_OCP_THR = 83, /* 0x53 */
/* One "live" slot and 9 "memory" positions. */
REG_RD_START_MEM = 84, /* 0x54 */
};
/* Retries failed modbus read attempts for improved reliability. */
static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
int address, int nb_registers, uint16_t *registers)
@ -87,31 +118,7 @@ static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
return ret;
}
/* Get one 16bit register. */
static int rdtech_dps_get_reg(const struct sr_dev_inst *sdi,
uint16_t address, uint16_t *value)
{
struct dev_context *devc;
struct sr_modbus_dev_inst *modbus;
uint16_t registers[1];
int ret;
const uint8_t *rdptr;
devc = sdi->priv;
modbus = sdi->conn;
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
address, ARRAY_SIZE(registers), registers);
g_mutex_unlock(&devc->rw_mutex);
rdptr = (void *)registers;
*value = read_u16le(rdptr);
return ret;
}
/* Set one 16bit register. */
/* Set one 16bit register. LE format for DPS devices. */
static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
uint16_t address, uint16_t value)
{
@ -135,32 +142,75 @@ static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
return ret;
}
/* Set one 16bit register. BE format for RD devices. */
static int rdtech_rd_set_reg(const struct sr_dev_inst *sdi,
uint16_t address, uint16_t value)
{
struct dev_context *devc;
struct sr_modbus_dev_inst *modbus;
uint16_t registers[1];
int ret;
uint8_t *wrptr;
devc = sdi->priv;
modbus = sdi->conn;
wrptr = (void *)registers;
write_u16be(wrptr, value);
g_mutex_lock(&devc->rw_mutex);
ret = sr_modbus_write_multiple_registers(modbus, address,
ARRAY_SIZE(registers), registers);
g_mutex_unlock(&devc->rw_mutex);
return ret;
}
/* Get DPS model number and firmware version from a connected device. */
SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
uint16_t *model, uint16_t *version)
enum rdtech_dps_model_type model_type,
uint16_t *model, uint16_t *version, uint32_t *serno)
{
uint16_t registers[REG_VERSION + 1 - REG_MODEL];
uint16_t registers[4];
int ret;
const uint8_t *rdptr;
/* Silence a compiler warning about an unused routine. */
(void)rdtech_dps_get_reg;
/*
* Get the MODEL and VERSION registers. No mutex here, because
* there is no sr_dev_inst when this function is called.
* No mutex here because when the routine executes then the
* device instance was not created yet (probe phase).
*/
ret = rdtech_dps_read_holding_registers(modbus,
REG_MODEL, ARRAY_SIZE(registers), registers);
if (ret != SR_OK)
return ret;
rdptr = (void *)registers;
*model = read_u16le_inc(&rdptr);
*version = read_u16le_inc(&rdptr);
sr_info("RDTech DPS PSU model: %u version: %u", *model, *version);
return ret;
switch (model_type) {
case MODEL_DPS:
/* Get the MODEL and VERSION registers. */
ret = rdtech_dps_read_holding_registers(modbus,
REG_DPS_MODEL, 2, registers);
if (ret != SR_OK)
return ret;
rdptr = (void *)registers;
*model = read_u16le_inc(&rdptr);
*version = read_u16le_inc(&rdptr);
*serno = 0;
sr_info("RDTech DPS/DPH model: %u version: %u",
*model, *version);
return SR_OK;
case MODEL_RD:
/* Get the MODEL, SERIAL, and FIRMWARE registers. */
ret = rdtech_dps_read_holding_registers(modbus,
REG_RD_MODEL, 4, registers);
if (ret != SR_OK)
return ret;
rdptr = (void *)registers;
*model = read_u16be_inc(&rdptr) / 10;
*serno = read_u32be_inc(&rdptr);
*version = read_u16be_inc(&rdptr);
sr_info("RDTech RD model: %u version: %u, serno %u",
*model, *version, *serno);
return SR_OK;
default:
sr_err("Unexpected RDTech PSU device type. Programming error?");
return SR_ERR_ARG;
}
/* UNREACH */
}
/* Send a measured value to the session feed. */
@ -208,7 +258,7 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
{
struct dev_context *devc;
struct sr_modbus_dev_inst *modbus;
uint16_t registers[REG_ENABLE + 1 - REG_USET];
uint16_t registers[12];
int ret;
const uint8_t *rdptr;
uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
@ -226,57 +276,122 @@ SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
if (!state)
return SR_ERR_ARG;
/* Transfer a chunk of registers in a single call. */
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
REG_USET, ARRAY_SIZE(registers), registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
/*
* Transfer a chunk of registers in a single call. It's
* unfortunate that the model dependency and the sparse
* register map force us to open code addresses, sizes,
* and the sequence of the registers and how to interpret
* their bit fields. But then this is not too unusual for
* a hardware specific device driver ...
*
* TODO Optionally reduce the transfer volume depending
* on the caller specified state query context.
*/
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
REG_DPS_USET, 10, registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
/* Interpret the registers' values. */
rdptr = (const void *)registers;
uset_raw = read_u16le_inc(&rdptr);
volt_target = uset_raw / devc->voltage_multiplier;
iset_raw = read_u16le_inc(&rdptr);
curr_limit = iset_raw / devc->current_multiplier;
uout_raw = read_u16le_inc(&rdptr);
curr_voltage = uout_raw / devc->voltage_multiplier;
iout_raw = read_u16le_inc(&rdptr);
curr_current = iout_raw / devc->current_multiplier;
power_raw = read_u16le_inc(&rdptr);
curr_power = power_raw / 100.0f;
(void)read_u16le_inc(&rdptr); /* UIN */
reg_val = read_u16le_inc(&rdptr); /* LOCK */
is_lock = reg_val != 0;
reg_val = read_u16le_inc(&rdptr); /* PROTECT */
uses_ovp = reg_val == STATE_OVP;
uses_ocp = reg_val == STATE_OCP;
reg_state = read_u16le_inc(&rdptr); /* CV_CC */
is_reg_cc = reg_state == MODE_CC;
out_state = read_u16le_inc(&rdptr); /* ENABLE */
is_out_enabled = out_state != 0;
/* Interpret the registers' values. */
rdptr = (const void *)registers;
uset_raw = read_u16le_inc(&rdptr);
volt_target = uset_raw / devc->voltage_multiplier;
iset_raw = read_u16le_inc(&rdptr);
curr_limit = iset_raw / devc->current_multiplier;
uout_raw = read_u16le_inc(&rdptr);
curr_voltage = uout_raw / devc->voltage_multiplier;
iout_raw = read_u16le_inc(&rdptr);
curr_current = iout_raw / devc->current_multiplier;
power_raw = read_u16le_inc(&rdptr);
curr_power = power_raw / 100.0f;
(void)read_u16le_inc(&rdptr); /* UIN */
reg_val = read_u16le_inc(&rdptr); /* LOCK */
is_lock = reg_val != 0;
reg_val = read_u16le_inc(&rdptr); /* PROTECT */
uses_ovp = reg_val == STATE_OVP;
uses_ocp = reg_val == STATE_OCP;
reg_state = read_u16le_inc(&rdptr); /* CV_CC */
is_reg_cc = reg_state == MODE_CC;
out_state = read_u16le_inc(&rdptr); /* ENABLE */
is_out_enabled = out_state != 0;
/*
* Transfer another chunk of registers in a single call.
* TODO Unfortunately this call site open codes a fixed number
* of registers to read. But there is already some leakage of
* the register layout in this routine, and adding more device
* types in the future will make things "worse". So accept it.
*/
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
PRE_OVPSET, 2, registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
/* Transfer another chunk of registers in a single call. */
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
PRE_DPS_OVPSET, 2, registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
/* Interpret the registers' values. */
rdptr = (const void *)registers;
ovpset_raw = read_u16le_inc(&rdptr); /* PRE OVPSET */
ovp_threshold = ovpset_raw * devc->voltage_multiplier;
ocpset_raw = read_u16le_inc(&rdptr); /* PRE OCPSET */
ocp_threshold = ocpset_raw * devc->current_multiplier;
/* Interpret the second registers chunk's values. */
rdptr = (const void *)registers;
ovpset_raw = read_u16le_inc(&rdptr); /* PRE OVPSET */
ovp_threshold = ovpset_raw * devc->voltage_multiplier;
ocpset_raw = read_u16le_inc(&rdptr); /* PRE OCPSET */
ocp_threshold = ocpset_raw * devc->current_multiplier;
break;
case MODEL_RD:
/* Retrieve a set of adjacent registers. */
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
REG_RD_VOLT_TGT, 11, registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
/* Interpret the registers' raw content. */
rdptr = (const void *)registers;
uset_raw = read_u16be_inc(&rdptr); /* USET */
volt_target = uset_raw / devc->voltage_multiplier;
iset_raw = read_u16be_inc(&rdptr); /* ISET */
curr_limit = iset_raw / devc->current_multiplier;
uout_raw = read_u16be_inc(&rdptr); /* UOUT */
curr_voltage = uout_raw / devc->voltage_multiplier;
iout_raw = read_u16be_inc(&rdptr); /* IOUT */
curr_current = iout_raw / devc->current_multiplier;
(void)read_u16be_inc(&rdptr); /* ENERGY */
power_raw = read_u16be_inc(&rdptr); /* POWER */
curr_power = power_raw / 100.0f;
(void)read_u16be_inc(&rdptr); /* VOLT_IN */
(void)read_u16be_inc(&rdptr);
reg_val = read_u16be_inc(&rdptr); /* PROTECT */
uses_ovp = reg_val == STATE_OVP;
uses_ocp = reg_val == STATE_OCP;
reg_state = read_u16be_inc(&rdptr); /* REGULATION */
is_reg_cc = reg_state == MODE_CC;
out_state = read_u16be_inc(&rdptr); /* ENABLE */
is_out_enabled = out_state != 0;
/* Retrieve a set of adjacent registers. */
g_mutex_lock(&devc->rw_mutex);
ret = rdtech_dps_read_holding_registers(modbus,
REG_RD_OVP_THR, 2, registers);
g_mutex_unlock(&devc->rw_mutex);
if (ret != SR_OK)
return ret;
/* Interpret the registers' raw content. */
rdptr = (const void *)registers;
ovpset_raw = read_u16be_inc(&rdptr); /* OVP THR */
ovp_threshold = ovpset_raw / devc->voltage_multiplier;
ocpset_raw = read_u16be_inc(&rdptr); /* OCP THR */
ocp_threshold = ocpset_raw / devc->current_multiplier;
/* Details which we cannot query from the device. */
is_lock = FALSE;
break;
default:
/* ShouldNotHappen(TM). Probe should have failed. */
return SR_ERR_ARG;
}
/* Store gathered details in the high level container. */
memset(state, 0, sizeof(*state));
@ -327,39 +442,103 @@ SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
/* Only a subset of known values is settable. */
if (state->mask & STATE_OUTPUT_ENABLED) {
reg_value = state->output_enabled ? 1 : 0;
ret = rdtech_dps_set_reg(sdi, REG_ENABLE, reg_value);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
ret = rdtech_dps_set_reg(sdi, REG_DPS_ENABLE, reg_value);
if (ret != SR_OK)
return ret;
break;
case MODEL_RD:
ret = rdtech_rd_set_reg(sdi, REG_RD_ENABLE, reg_value);
if (ret != SR_OK)
return ret;
break;
default:
return SR_ERR_ARG;
}
}
if (state->mask & STATE_VOLTAGE_TARGET) {
reg_value = state->voltage_target * devc->voltage_multiplier;
ret = rdtech_dps_set_reg(sdi, REG_USET, reg_value);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
ret = rdtech_dps_set_reg(sdi, REG_DPS_USET, reg_value);
if (ret != SR_OK)
return ret;
break;
case MODEL_RD:
ret = rdtech_rd_set_reg(sdi, REG_RD_VOLT_TGT, reg_value);
if (ret != SR_OK)
return ret;
break;
default:
return SR_ERR_ARG;
}
}
if (state->mask & STATE_CURRENT_LIMIT) {
reg_value = state->current_limit * devc->current_multiplier;
ret = rdtech_dps_set_reg(sdi, REG_ISET, reg_value);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
ret = rdtech_dps_set_reg(sdi, REG_DPS_ISET, reg_value);
if (ret != SR_OK)
return ret;
break;
case MODEL_RD:
ret = rdtech_rd_set_reg(sdi, REG_RD_CURR_LIM, reg_value);
if (ret != SR_OK)
return ret;
break;
default:
return SR_ERR_ARG;
}
}
if (state->mask & STATE_OVP_THRESHOLD) {
reg_value = state->ovp_threshold * devc->voltage_multiplier;
ret = rdtech_dps_set_reg(sdi, PRE_OVPSET, reg_value);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
ret = rdtech_dps_set_reg(sdi, PRE_DPS_OVPSET, reg_value);
if (ret != SR_OK)
return ret;
break;
case MODEL_RD:
ret = rdtech_rd_set_reg(sdi, REG_RD_OVP_THR, reg_value);
if (ret != SR_OK)
return ret;
break;
default:
return SR_ERR_ARG;
}
}
if (state->mask & STATE_OCP_THRESHOLD) {
reg_value = state->ocp_threshold * devc->current_multiplier;
ret = rdtech_dps_set_reg(sdi, PRE_OCPSET, reg_value);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
ret = rdtech_dps_set_reg(sdi, PRE_DPS_OCPSET, reg_value);
if (ret != SR_OK)
return ret;
break;
case MODEL_RD:
ret = rdtech_rd_set_reg(sdi, REG_RD_OCP_THR, reg_value);
if (ret != SR_OK)
return ret;
break;
default:
return SR_ERR_ARG;
}
}
if (state->mask & STATE_LOCK) {
reg_value = state->lock ? 1 : 0;
ret = rdtech_dps_set_reg(sdi, REG_LOCK, reg_value);
if (ret != SR_OK)
return ret;
switch (devc->model->model_type) {
case MODEL_DPS:
reg_value = state->lock ? 1 : 0;
ret = rdtech_dps_set_reg(sdi, REG_DPS_LOCK, reg_value);
if (ret != SR_OK)
return ret;
break;
case MODEL_RD:
/* Do nothing, _and_ silently succeed. */
break;
default:
return SR_ERR_ARG;
}
}
return SR_OK;

View File

@ -32,7 +32,14 @@
#define LOG_PREFIX "rdtech-dps"
enum rdtech_dps_model_type {
MODEL_NONE,
MODEL_DPS,
MODEL_RD,
};
struct rdtech_dps_model {
enum rdtech_dps_model_type model_type;
unsigned int id;
const char *name;
unsigned int max_current;
@ -85,7 +92,8 @@ SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
struct rdtech_dps_state *state);
SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
uint16_t *model, uint16_t *version);
enum rdtech_dps_model_type model_type,
uint16_t *model, uint16_t *version, uint32_t *serno);
SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi);
SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data);