X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=blobdiff_plain;f=src%2Fhardware%2Frdtech-dps%2Fprotocol.c;fp=src%2Fhardware%2Frdtech-dps%2Fprotocol.c;h=9f45fc4f6d8fa974f37b78b807859faabd5f3e9b;hp=d8bd5e82175fe979312df61909966d534a012938;hb=884ae8c02167e9575c84f09c7eee427e0c7353b7;hpb=d7a4dad881bf78ce17519d3d7728f74d0ec11415 diff --git a/src/hardware/rdtech-dps/protocol.c b/src/hardware/rdtech-dps/protocol.c index d8bd5e82..9f45fc4f 100644 --- a/src/hardware/rdtech-dps/protocol.c +++ b/src/hardware/rdtech-dps/protocol.c @@ -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_PRESET = 0x23, /* Loads a preset into preset 0. */ + 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_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,32 +118,32 @@ 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) +/* 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) { struct dev_context *devc; struct sr_modbus_dev_inst *modbus; uint16_t registers[1]; int ret; - const uint8_t *rdptr; + uint8_t *wrptr; devc = sdi->priv; modbus = sdi->conn; + wrptr = (void *)registers; + write_u16le(wrptr, value); + g_mutex_lock(&devc->rw_mutex); - ret = rdtech_dps_read_holding_registers(modbus, - address, ARRAY_SIZE(registers), registers); + ret = sr_modbus_write_multiple_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. */ -static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi, +/* 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; @@ -125,7 +156,7 @@ static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi, modbus = sdi->conn; wrptr = (void *)registers; - write_u16le(wrptr, value); + write_u16be(wrptr, value); g_mutex_lock(&devc->rw_mutex); ret = sr_modbus_write_multiple_registers(modbus, address, @@ -137,30 +168,49 @@ static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi, /* 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. */ + 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; - /* - * 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; + /* 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; - /* 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; + /* 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;