File Keithley2450Driver.cpp
File List > midas_fe > power > Keithley2450Driver.cpp
Go to the documentation of this file
//
#include "Keithley2450Driver.h"
#include <thread>
Keithley2450Driver::Keithley2450Driver() {}
Keithley2450Driver::~Keithley2450Driver() {}
Keithley2450Driver::Keithley2450Driver(std::string n, EQUIPMENT_INFO* inf) : PowerDriver(n, inf) {
std::cout << " Keithley2450 driver with " << instrumentID.size() << " channels instantiated."
<< std::endl;
}
INT Keithley2450Driver::ConnectODB() {
InitODBArray();
PowerDriver::ConnectODB();
settings["port"](5025);
settings["reply timout"](300);
// minimum reply , 2 chars , not 3 (not fully figured out why)
settings["min reply"](2);
settings["ESR"](0);
settings["Max Voltage"](2);
// based on a diode calibration measurement in zurich
settings["Temp cal y-interception"](0.6643);
// based on a diode calibration measurement in zurich
settings["Temp cal slope"](-0.00188);
if (false) {
return FE_ERR_ODB;
}
return FE_SUCCESS;
}
void Keithley2450Driver::InitODBArray() {
midas::odb settings_array = {{"Channel Names", std::array<std::string, 4>()}};
settings_array.connect("/Equipment/" + name + "/Settings");
}
INT Keithley2450Driver::Init() {
INT err;
ip = settings["IP"];
client->SetDefaultWaitTime(200);
// Global reset if requested
if (settings["Global Reset On FE Start"]) {
if (client->Write("*RST\n")) {
cm_msg(MINFO, "Init KEITH supply ... ", "init global reset of %s", ip.c_str());
} else {
cm_msg(MERROR, "Init KEITH supply ... ", "could not global reset %s", ip.c_str());
}
std::this_thread::sleep_for(std::chrono::milliseconds(client->GetWaitTime()));
}
if (!client->Write(GenerateCommand(COMMAND_TYPE::Beep, 0))) {
cm_msg(MERROR, "Init KEITH supply ... ", "could not beep %s", ip.c_str());
}
std::this_thread::sleep_for(std::chrono::milliseconds(client->GetWaitTime()));
// Channel selection not relevant for HAMEG supply to read ID
// "-1" is a trick not to select a channel before the query
idCode = ReadIDCode(-1, err);
std::cout << "ID code: " << idCode << std::endl;
float interception = settings["Temp cal y-interception"];
float slope = settings["Temp cal slope"];
// KEITH has 1 channel
instrumentID = {1};
int nChannels = instrumentID.size();
settings["NChannels"] = nChannels;
// Voltage
voltage.resize(nChannels);
demandvoltage.resize(nChannels);
//
current.resize(nChannels);
demandcurrent.resize(nChannels);
currentlimit.resize(nChannels);
// Service
state.resize(nChannels);
OVPlevel.resize(nChannels);
SourceMode.resize(nChannels);
temperature.resize(nChannels);
// client->FlushQueu();
// Read channels
for (int i(0); i < nChannels; i++) {
state[i] = ReadState(i, err);
if (err != FE_SUCCESS) {
return err;
}
}
// Push to odb
variables["State"] = state;
variables["Set State"] = state;
for (int i(0); i < nChannels; i++) {
// Voltage
voltage[i] = ReadVoltage(i, err);
demandvoltage[i] = ReadSetVoltage(i, err);
// Current
current[i] = ReadCurrent(i, err);
demandcurrent[i] = ReadSetCurrent(i, err);
// For whatever reason we have this structure...
try {
if (variables["Current Limit"]) {
currentlimit[i] = variables["Current Limit"];
}
} catch (...) {
std::cout << "Current limit not in the ODB yet" << std::endl;
currentlimit[i] = ReadCurrentLimit(i, err);
}
// OVPlevel[i] = ReadOVPLevel(i, err);
// Hardcoded limit of 20V
OVPlevel[i] = 20;
SourceMode[i] = ReadSourceMode(i, err);
temperature[i] = (voltage[i] - interception) / slope;
if (err != FE_SUCCESS) {
return err;
}
}
settings["Identification Code"] = idCode;
settings["ESR"] = ReadESR(-1, err);
settings["Read ESR"] = false;
variables["Voltage"] = voltage;
variables["Demand Voltage"] = demandvoltage;
variables["Current"] = current;
variables["Demand Current"] = demandcurrent;
variables["Current Limit"] = currentlimit;
variables["OVP Level"] = OVPlevel;
variables["Demand OVP Level"] = OVPlevel;
settings["Source Mode"] = SourceMode;
variables["Temperature"] = temperature;
// Watch functions
variables["Set State"].watch(
[&](midas::odb& arg [[maybe_unused]]) { this->SetStateChanged(); });
variables["Demand Voltage"].watch(
[&](midas::odb& arg [[maybe_unused]]) { this->DemandVoltageChanged(); });
variables["Demand Current"].watch(
[&](midas::odb& arg [[maybe_unused]]) { this->DemandCurrentChanged(); });
variables["Current Limit"].watch(
[&](midas::odb& arg [[maybe_unused]]) { this->CurrentLimitChanged(); });
variables["Demand OVP Level"].watch(
[&](midas::odb& arg [[maybe_unused]]) { this->DemandOVPLevelChanged(); });
settings["Source Mode"].watch(
[&](midas::odb& arg [[maybe_unused]]) { this->SourceModeChanged(); });
settings["Read ESR"].watch([&](midas::odb& arg [[maybe_unused]]) { this->ReadESRChanged(); });
return FE_SUCCESS;
}
INT Keithley2450Driver::ReadAll() {
INT err;
INT err_accumulated;
int nChannels = instrumentID.size();
// update local book keeping
for (int i(0); i < nChannels; i++) {
if (readonlythisindex >= 0 && i != readonlythisindex)
continue;
bool bvalue = ReadState(i, err);
err_accumulated = err;
if (state[i] != bvalue) // only update odb if there is a change
{
state[i] = bvalue;
variables["State"][i] = bvalue;
}
float fvalue = ReadVoltage(i, err);
err_accumulated = err_accumulated | err;
if (fabs(voltage[i] - fvalue) > fabs(relevantchange * voltage[i])) {
voltage[i] = fvalue;
variables["Voltage"][i] = fvalue;
float tvalue = (fvalue - 0.6643) / -0.00188;
temperature[i] = tvalue;
variables["Temperature"][i] = tvalue;
}
fvalue = ReadCurrent(i, err);
err_accumulated = err_accumulated | err;
if (fabs(current[i] - fvalue) > fabs(relevantchange * current[i])) {
current[i] = fvalue;
variables["Current"][i] = fvalue;
}
sleep(1);
fvalue = ReadCurrentLimit(i, err);
err_accumulated = err_accumulated | err;
// remove the success bit if there is any
if (err_accumulated != FE_SUCCESS)
return err_accumulated & 0xFFFE;
}
[[maybe_unused]] bool buffer = ClearBuffer();
return FE_SUCCESS;
}
void Keithley2450Driver::ReadESRChanged() {
INT err;
if (settings["Read ESR"]) {
settings["ESR"] = ReadESR(-1, err);
settings["Read ESR"] = false;
}
}
// FIXME: this function will be removed in the next cleanup
// extra check whether it is safe to turn on supply
bool Keithley2450Driver::AskPermissionToTurnOn(int) { return true; }
// TODO: see if we can set HIMPedance mode: p12-38
// :OUTP:<function>:SMODe <state>
// <function> as CURRent / VOLTage
// <state> as NORMal / !!!! HIMPedance / ZERO / GUARd
// Or :SOURce:CURRent/VOLTage:HIGH:CAPacitance ON
// smu.source.highc = smu.ON
std::string Keithley2450Driver::GenerateCommand(COMMAND_TYPE cmdt, float val) {
std::string command_type = settings["Command Type"];
if (cmdt == COMMAND_TYPE::CLearStatus) {
return "*CLS\n";
} else if (cmdt == COMMAND_TYPE::OPC) {
return "*OPC?\n";
} else if (cmdt == COMMAND_TYPE::ReadESR) {
return "*ESR?\n";
} else if (cmdt == COMMAND_TYPE::Reset) {
return "*RST\n";
} else if (command_type == "SCPI") {
// Current
if (cmdt == COMMAND_TYPE::SetCurrent) {
return ":SOUR:CURR " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SetCurrentRange) {
return ":SENS:CURR:RANG " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SetCurrentLimit) {
return ":SOUR:VOLT:ILIM " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::ReadCurrentLimit) {
return ":SOUR:VOLT:ILIM?\n";
} else if (cmdt == COMMAND_TYPE::SetCurrentAsRead) {
return ":SENS:FUNC \"CURR\"\n";
} else if (cmdt == COMMAND_TYPE::ReadSetCurrent) {
return ":SOUR:CURR?\n"; // Have been added recently
} else if (cmdt == COMMAND_TYPE::ReadCurrent) {
// return ":READ?\n";
return ":MEAS:CURR?\n";
}
// Voltage
else if (cmdt == COMMAND_TYPE::SetVoltage) {
return ":SOUR:VOLT " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SetVoltageRange) {
return ":SENS:VOLT:RANG " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SetOVPLevel) {
return ":SOUR:VOLT:PROT PROT" + std::to_string((int)(val)) + "\n";
} else if (cmdt == COMMAND_TYPE::SetVoltageLimit) {
return ":SOUR:CURR:VLIM " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::ReadVoltageLimit) {
return ":SOUR:CURR:VLIM?\n";
} else if (cmdt == COMMAND_TYPE::ReadOVPLevel) {
return ":SOUR:VOLT:PROT?\n";
} else if (cmdt == COMMAND_TYPE::SetVoltageAsRead) {
return ":SENS:FUNC \"VOLT\"\n";
} else if (cmdt == COMMAND_TYPE::ReadSetVoltage) {
return ":SOUR:VOLT?\n";
} else if (cmdt == COMMAND_TYPE::ReadVoltage) {
return ":READ?\n";
// return ":MEAS:VOLT?\n";
}
// Other
else if (cmdt == COMMAND_TYPE::CurrHighCapacitanceOn) {
return ":SOUR:CURR:HIGH:CAP ON\n";
} else if (cmdt == COMMAND_TYPE::VoltHighCapacitanceOn) {
return ":SOUR:VOLT:HIGH:CAP ON\n";
} else if (cmdt == COMMAND_TYPE::ReadSourceMode) {
return ":SOUR:FUNC?\n";
} else if (cmdt == COMMAND_TYPE::SetState) {
int ch = (int)val;
if (ch == 1) {
return ":OUTP:STAT ON\n";
} else if (ch == 0) {
return ":OUTP:STAT OFF\n";
} else {
cm_msg(MERROR, "Init KEITH supply ... ", "SetState can be only 1 or 0, and not %d",
ch);
return "\n";
}
} else if (cmdt == COMMAND_TYPE::ReadState) {
return ":OUTP:STAT?\n";
} else if (cmdt == COMMAND_TYPE::Beep) {
return ":SYST:BEEP 4400,0.5\n";
} else if (cmdt == COMMAND_TYPE::ReadErrorQueue) {
return ":SYST:ERR:NEXT?\n";
} else if (cmdt == COMMAND_TYPE::ClearBuffer) {
return ":TRAC:CLEAR\n";
}
// TOKNOW: It is far from complete!
} else if (command_type == "TSP") {
if (cmdt == COMMAND_TYPE::SetCurrent) {
return "smu.source.ilimit.level=" + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::ReadCurrent) {
return "print(smu.measure.read())";
// return "smu.measure.read(defbuffer1);printbuffer(defbuffer1.n, defbuffer1.n,
// defbuffer1)\n";
} else if (cmdt == COMMAND_TYPE::SetCurrentAsRead) {
// return "smu.source.func = smu.FUNC_CURRENT";
return "smu.source.func = smu.FUNC_DC_CURRENT";
} else if (cmdt == COMMAND_TYPE::SetVoltageAsRead) {
return "smu.source.func = smu.FUNC_DC_VOLTAGE";
} else if (cmdt == COMMAND_TYPE::ReadState) {
return "print(smu.source.output)\n";
} else if (cmdt == COMMAND_TYPE::ReadVoltage) {
return "print(smu.measure.read())";
// return "print(smu.source.level)\n";
} else if (cmdt == COMMAND_TYPE::ReadSetVoltage) {
return "print(smu.source.level)\n";
} else if (cmdt == COMMAND_TYPE::ReadCurrentLimit) {
return "print(smu.source.ilimit.level)\n";
} else if (cmdt == COMMAND_TYPE::SetVoltage) {
return "smu.source.level=" + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::Beep) {
return "beeper.beep(0.5, 4400);\n";
} else if (cmdt == COMMAND_TYPE::SetCurrentLimit) {
return "smu.source.ilimit.level=" + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SetState) {
int ch = (int)val;
if (ch == 1) {
return "smu.source.output=smu.ON\n";
} else if (ch == 0) {
return "smu.source.output=smu.OFF\n";
} else {
std::cout << "Error: set state can be onlz 1 or 0\n"; // TODO: message in midas
return "\n";
}
} else if (cmdt == COMMAND_TYPE::ReadErrorQueue) {
return "print(errorqueue.next())\n";
} else if (cmdt == COMMAND_TYPE::ReadOVPLevel) {
return "print(smu.source.protect.level)\n";
} else if (cmdt == COMMAND_TYPE::SetOVPLevel) {
return "smu.source.protect.level=smu.PROTECT_" + std::to_string((int)(val)) + "V\n";
}
}
return "";
}
std::string Keithley2450Driver::GenerateCommand(COMMAND_TYPE cmdt, int, float val) {
std::string command_type = settings["Command Type"];
if (command_type == "SCPI") {
if (cmdt == COMMAND_TYPE::SelectChannelAndSetVoltage) {
return ":SOUR:VOLT " + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SelectChannelAndSetCurrent) {
return ":SOUR:CURR " + std::to_string(val) + "\n";
}
} else if (command_type == "TSP") {
if (cmdt == COMMAND_TYPE::SelectChannelAndSetVoltage) {
return "smu.source.level=" + std::to_string(val) + "\n";
} else if (cmdt == COMMAND_TYPE::SelectChannelAndSetCurrent) {
return "";
// return "smu.source.level=" + std::to_string(val) + "\n";
}
} else {
std::cout << "This function is usable only with SelectChannelAndSetVoltage(Current)!\n";
return "";
}
return "";
}