/*--------------------------------------------------------------------------- lsc370driv.c Driver for the LakeShore Model 370 AC Resistance Bridge Markus Zolliker, July 2006 OBSOLETE, scriptcontext driver in use May 2016 ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lscsupport.h" #include "fsm.h" #include "initializer.h" #define PID_FLAG 1 #define RDGRNG_FLAG 2 #define HTRRNG_FLAG 3 #define MAX_CHAN 4 typedef struct { EaseDriv d; float t; float htr; float set; float prop; float integ; float deriv; float resist; /* Ohm */ float temp[MAX_CHAN]; float res[MAX_CHAN]; int channel[MAX_CHAN]; int ighHeater; /* IGH heater range (-1 if output is direct) */ int htrRange; int currentEx; int voltageEx; int range; int autoRange; int index; int controlChan; } Lsc370; static ParClass lsc370Class = { "LSC370", sizeof(Lsc370) }; /*----------------------------------------------------------------------------*/ static float Lsc370Power(Lsc370 * drv, float percent) { float power; /* mW */ float current; /* A */ float voltage; /* V */ if (drv->htrRange == 0 || drv->ighHeater == 0) { power = 0; } else if (drv->ighHeater > 0) { current = pow(10, drv->htrRange * 0.5 - 5) * percent / 100; voltage = current * 500; if (voltage > 10) voltage = 10; power = pow(10, drv->ighHeater) * 0.0002 * voltage / 10; } else { current = pow(10, drv->htrRange * 0.5 - 5) * percent / 100; voltage = current * drv->resist; if (voltage > 10) { power = 10 * 10 / drv->resist * 1e3; } else { power = current * voltage * 1e3; } } return power; } /*----------------------------------------------------------------------------*/ static void Lsc370ParDef(void *object) { Lsc370 *drv = ParCast(&lsc370Class, object); float power; int i; static char *heaterList[] = { "off", "30uA", "100uA", "300uA", "1mA", "3mA", "10mA", "30mA", "100mA", NULL }; static char *currentList[] = { "off", "1pA", "3pA", "10pA", "30pA", "100pA", "300pA", "1nA", "3nA", "10nA", "30nA", "100nA", "300nA", "1uA", "3uA", "10uA", "30uA", "100uA", "300uA", "1mA", "3mA", "10mA", "30mA", NULL }; static char *voltageList[] = { "off", "2uV", "6uV", "20uV", "60uV", "200uV", "600uV", "2mV", "6mV", "20mV", "60mV", "200mV", "600mV", NULL }; static char *rangeList[] = { "auto", "2mOhm", "6mOhm", "20mOhm", "60mOhm", "200mOhm", "600mOhm", "2Ohm", "6Ohm", "20Ohm", "60Ohm", "200Ohm", " 600Ohm", "2kOhm", "6kOhm", "20kOhm", "60kOhm", "200kOhm", "600kOhm", "2MegaOhm", "6MegaOhm", "20MegaOhm", "60MegaOhm", NULL }; static char *offOn[] = { "off", "on", NULL }; static char *tNames[] = { "tsample", "tstill", "tmix", "tplate" }; static char *rNames[] = { "rsample", "rstill", "rmix", "rplate" }; static char *cNames[] = { "csample", "cstill", "cmix", "cplate" }; ParName(""); ParTail("K"); ParFloat(&drv->temp[0], PAR_NAN); ParName("res"); ParTail("Ohm"); ParFloat(&drv->res[0], PAR_NAN); ParName("set"); ParTail("K"); if (EaseUpdate(EASE_RUN)) { ParFloat(&drv->d.targetValue, PAR_NAN); } else { ParFloat(&drv->set, PAR_NAN); } ParName("htr"); ParTail("%"); ParFloat(&drv->htr, PAR_NAN); for (i = 0; i < MAX_CHAN; i++) { if (drv->channel[i] > 0 && i > 0) { ParName(tNames[i]); ParTail("K"); ParFloat(&drv->temp[i], PAR_NAN); ParName(rNames[i]); ParTail("Ohm"); ParFloat(&drv->res[i], PAR_NAN); } ParName(cNames[i]); ParAccess(usUser); ParInt(&drv->channel[i], 0); } ParName("prop"); ParTail("(gain)"); EaseUpdate(PID_FLAG); ParFmt("%.3f"); ParFloat(&drv->prop, PAR_NAN); ParName("integ"); ParTail("sec"); EaseUpdate(PID_FLAG); ParFmt("%.0f"); ParFloat(&drv->integ, PAR_NAN); ParName("deriv"); ParTail("sec"); EaseUpdate(PID_FLAG); ParFmt("%.0f"); ParFloat(&drv->deriv, PAR_NAN); ParName("resist"); ParTail("Ohm"); ParAccess(usUser); ParFloat(&drv->resist, 500.0); ParName("ighHeater"); ParAccess(usUser); ParInt(&drv->ighHeater, -1); ParName("controlChan"); ParList(NULL); ParAccess(usUser); ParInt(&drv->controlChan, 1); ParName("currentEx"); EaseUpdate(RDGRNG_FLAG); ParEnum(currentList); if (drv->currentEx > 0) ParList(NULL); ParInt(&drv->currentEx, PAR_NAN); if (ParActionIs(PAR_SET) > 0) { if (drv->currentEx > 22) { drv->currentEx = 22; } else if (drv->currentEx <= 0) { drv->currentEx = 0; } else { drv->voltageEx = 0; } } ParName("voltageEx"); EaseUpdate(RDGRNG_FLAG); ParEnum(voltageList); if (drv->voltageEx > 0) ParList(NULL); ParInt(&drv->voltageEx, PAR_NAN); if (ParActionIs(PAR_SET) > 0) { if (drv->voltageEx > 12) { drv->voltageEx = 12; } else if (drv->voltageEx <= 0) { drv->voltageEx = 0; } else { drv->currentEx = 0; } } ParName("range"); EaseUpdate(RDGRNG_FLAG); ParEnum(rangeList); if (drv->autoRange == 0) ParList(NULL); ParInt(&drv->range, PAR_NAN); if (ParActionIs(PAR_SET) > 0) { if (drv->range > 22) { drv->range = 22; drv->autoRange = 0; } else if (drv->range <= 0) { drv->autoRange = 1; drv->range = 0; } } ParName("autoRange"); EaseUpdate(RDGRNG_FLAG); ParEnum(offOn); ParList(NULL); ParInt(&drv->autoRange, PAR_NAN); if (ParActionIs(PAR_SET) > 0) { if (drv->autoRange) { drv->autoRange = 1; } } ParName("htrRange"); EaseUpdate(HTRRNG_FLAG); ParEnum(heaterList); ParList(NULL); ParInt(&drv->htrRange, PAR_NAN); if (ParActionIs(PAR_SET) > 0) { if (drv->htrRange > 8) { drv->htrRange = 8; } else if (drv->htrRange <= 0) { drv->htrRange = 0; } } ParName("maxPower"); ParTail("mW"); power = Lsc370Power(drv, 100.0); ParFmt("%.3g"); ParFloat(&power, 0.0); ParName("power"); ParTail("mW"); power = Lsc370Power(drv, drv->htr); ParFloat(&power, 0.0); EaseBasePar(drv); EaseSendPar(drv); EaseDrivPar(drv, "%.5g", "K"); ParStdDef(); EaseMsgPar(drv); ParName("period"); ParAccess(usUser); ParInt(&drv->d.b.p.period, 5); } /*----------------------------------------------------------------------------*/ static long Lsc370Read(long pc, void *object) { Lsc370 *drv = ParCast(&lsc370Class, object); EaseBase *eab = object; int mode, exi, rng, autoR, eoff; float x, y, z; char buf[16]; switch (pc) { default: /* FSM BEGIN ****************************** */ if (pc != 0) { return -2; } drv->index = 0; chanLoop: if (drv->channel[drv->index] == 0) { drv->temp[drv->index] = PAR_NAN; goto noRead; } snprintf(buf, sizeof buf, "RDGK?%d", drv->channel[drv->index]); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ if (1 == sscanf(eab->ans, "%f", &x)) { drv->temp[drv->index] = x; } snprintf(buf, sizeof buf, "RDGR?%d", drv->channel[drv->index]); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ if (1 == sscanf(eab->ans, "%f", &x)) { drv->res[drv->index] = x; } noRead: drv->index++; if (drv->index < MAX_CHAN) goto chanLoop; EaseWrite(eab, "HTR?"); return __LINE__; case __LINE__: /**********************************/ if (1 == sscanf(eab->ans, "%f", &x)) { drv->htr = x; } EaseWrite(eab, "SETP?"); return __LINE__; case __LINE__: /**********************************/ if (1 == sscanf(eab->ans, "%f", &x)) { if (drv->set != x && !EaseGetUpdate(drv, EASE_RUN)) { drv->d.targetValue = x; } drv->set = x; } if (EaseGetUpdate(drv, PID_FLAG)) goto skipPid; EaseWrite(eab, "PID?"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, PID_FLAG)) goto skipPid; if (3 == sscanf(eab->ans, "%f,%f,%f", &x, &y, &z)) { drv->prop = x; drv->integ = y; drv->deriv = z; } skipPid: if (EaseGetUpdate(drv, HTRRNG_FLAG)) goto skipHtrRng; EaseWrite(eab, "HTRRNG?"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, HTRRNG_FLAG)) goto skipHtrRng; if (1 == sscanf(eab->ans, "%d", &rng)) { drv->htrRange = rng; /* if (rng == 0) { drv->set = 0; } */ } skipHtrRng: if (EaseGetUpdate(drv, RDGRNG_FLAG)) goto skipRdgRng; EaseWrite(eab, "RDGRNG?1"); return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, RDGRNG_FLAG)) goto skipRdgRng; if (5 == sscanf(eab->ans, "%d,%d,%d,%d,%d", &mode, &exi, &rng, &autoR, &eoff)) { if (eoff) { drv->currentEx = 0; drv->voltageEx = 0; } else if (mode) { drv->currentEx = exi; drv->voltageEx = 0; } else { drv->voltageEx = exi; drv->currentEx = 0; } drv->range = rng; drv->autoRange = autoR; } skipRdgRng: ParLog(drv); return 0; } /* FSM END ******************************** */ } /*----------------------------------------------------------------------------*/ static long Lsc370Start(long pc, void *object) { Lsc370 *drv = ParCast(&lsc370Class, object); EaseBase *eab = object; char msg[256]; switch (pc) { default: /* FSM BEGIN ****************************** */ EasePchk(drv); EaseWrite(eab, "*IDN?"); return __LINE__; case __LINE__: /**********************************/ if (0 != strncmp(eab->version, "LSCI,MODEL370", 13)) { snprintf(msg, sizeof msg, "unknown temperature controller version: %s", eab->version); EaseStop(eab, msg); goto quit; } ParPrintf(drv, eLog, "connected to %s", eab->version); FsmCall(Lsc370Read); return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ******************************************* */ } /*----------------------------------------------------------------------------*/ static long Lsc370Set(long pc, void *object) { Lsc370 *drv = ParCast(&lsc370Class, object); EaseBase *eab = object; char cmd[128]; int upd, mode, exi, eoff; switch (pc) { default: /* FSM BEGIN ****************************** */ EasePchk(drv); EaseWrite(eab, "MODE 1;MODE?"); /* remote mode */ return __LINE__; case __LINE__: /**********************************/ loop: upd = EaseNextUpdate(drv); if (upd == EASE_RUN) goto run; if (upd == HTRRNG_FLAG) goto htrrng; if (upd == PID_FLAG) goto pid; if (upd == RDGRNG_FLAG) goto rdgrng; goto finish; run: snprintf(cmd, sizeof cmd, "SETP %.5g;SETP?", drv->d.targetValue); EaseWrite(eab, cmd); return __LINE__; case __LINE__: /**********************************/ /* fall through */ htrrng: snprintf(cmd, sizeof cmd, "CSET %d,1,1,1,1,8,%g;CSET?", drv->controlChan, drv->resist); EaseWrite(eab, cmd); return __LINE__; case __LINE__: /**********************************/ snprintf(cmd, sizeof cmd, "HTRRNG %d;HTRRNG?", drv->htrRange); EaseWrite(eab, cmd); return __LINE__; case __LINE__: /**********************************/ goto loop; pid: snprintf(cmd, sizeof cmd, "PID %.5g,%.5g,%.5g;PID?", drv->prop, drv->integ, drv->deriv); EaseWrite(eab, cmd); return __LINE__; case __LINE__: /**********************************/ goto loop; rdgrng: if (drv->voltageEx > 0) { mode = 0; drv->currentEx = 0; exi = drv->voltageEx; eoff = 0; } else if (drv->currentEx > 0) { mode = 1; drv->voltageEx = 0; exi = drv->currentEx; eoff = 0; } else { mode = 0; exi = 5; eoff = 1; } if (drv->range <= 0) { if (drv->autoRange) { drv->range = 13; } else { drv->range = 1; } } if (drv->autoRange) drv->autoRange = 1; snprintf(cmd, sizeof cmd, "RDGRNG 1,%d,%d,%d,%d,%d;RDGRNG?1", mode, exi, drv->range, drv->autoRange, eoff); EaseWrite(eab, cmd); return __LINE__; case __LINE__: /**********************************/ goto loop; finish: EaseWrite(eab, "MODE 0;MODE?"); /* local mode */ return __LINE__; case __LINE__: /**********************************/ return 0; } /* FSM END ******************************************* */ } /*----------------------------------------------------------------------------*/ static int Lsc370Init(SConnection * con, int argc, char *argv[], int dynamic) { /* args: MakeObject objectname lsc370 MakeObject objectname lsc370 */ Lsc370 *drv; drv = EaseMakeDriv(con, &lsc370Class, argc, argv, dynamic, 7, Lsc370ParDef, LscHandler, Lsc370Start, NULL, Lsc370Read, Lsc370Set); if (drv == NULL) return 0; drv->htrRange = 0; setRS232ReplyTerminator(drv->d.b.ser, "\n"); setRS232SendTerminator(drv->d.b.ser, "\n"); return 1; } /*----------------------------------------------------------------------------*/ void Lsc370Startup(void) { ParMakeClass(&lsc370Class, EaseDrivClass()); MakeDriver("LSC370", Lsc370Init, 0, "LakeShore 370 AC Resistance Bridge"); }