/*--------------------------------------------------------------------------- itcdriv.c Driver for the Oxford Instruments ITC503/ITC4 temperature controller Version 2 (based on ease). Markus Zolliker, May 2005 ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "oxinst.h" #include "fsm.h" #include "initializer.h" #define TESLATRON -1 #define ITC_SETHTR 1 #define ITC_SETGAS 2 typedef struct { EaseDriv d; float t[4]; /* temperatures (0 is set point) */ int dig[4]; /* format for these */ float htr; float gas; float autoGasLimit; float setGas; int sampleChan; int controlChan; int gasMode; int htrMode; int remote; int h; /* actual heater channel */ int a; /* actual auto mode */ } Itc; static ParClass itcClass = { "ITC", sizeof(Itc) }; /*----------------------------------------------------------------------------*/ static void ItcParDef(void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; char fmt[8]=""; int i; static char *ti[4] = {"setp","t1","t2","t3"}; static char *modeList[]={"off", "manual", "auto", NULL }; ParName("sampleChan"); if (eab->syntax != TESLATRON) ParAccess(usUser); ParInt(&drv->sampleChan, 1); if (ParActionIs(PAR_SET) > 0) { if (drv->sampleChan < 1 || drv->sampleChan > 3) { drv->sampleChan = 1; } } if (eab->syntax != TESLATRON) { ParName("controlChan"); ParAccess(usUser); ParInt(&drv->controlChan, 0); if (ParActionIs(PAR_SET) > 0) { if (drv->controlChan < 0 || drv->controlChan > 3) { drv->controlChan = 1; } } } ParName(""); if (eab->syntax == TESLATRON) { ParTail("mbar"); } else { ParTail("K"); } ParFloat(&drv->t[drv->sampleChan], PAR_NAN); ParName("dig1"); ParAccess(usUser); ParLogAs(NULL); ParInt(&drv->dig[1], -1); ParName("dig2"); ParAccess(usUser); ParLogAs(NULL); ParInt(&drv->dig[2], -1); ParName("dig3"); ParAccess(usUser); ParLogAs(NULL); ParInt(&drv->dig[3], -1); if (eab->syntax != TESLATRON) { ParName(ti[0]); if (drv->controlChan != 0) { snprintf(fmt, sizeof fmt, "%%.%df", drv->dig[drv->controlChan]); ParFmt(fmt); if (fabsf(drv->d.targetValue - drv->t[0]) < 9e-4) { ParList(""); } } else { ParList(""); } ParTail("K"); ParFloat(&drv->t[0], PAR_NAN); } for (i=1; i<=3; i++) { ParName(ti[i]); if (drv->dig[i] >= 0 && drv->sampleChan != i) { snprintf(fmt, sizeof fmt, "%%.%df", drv->dig[i]); ParFmt(fmt); ParTail("K"); } else { ParList(""); } ParFloat(&drv->t[i], PAR_NAN); } if (eab->syntax != TESLATRON) { ParName("htrMode"); if (drv->controlChan == 0 && drv->htr == 0) { ParList(""); } ParEnum(modeList); ParSave(1); ParInt(&drv->htrMode, 2); ParName("htr"); ParFmt("%.1f"); ParTail("%"); ParSave(1); EaseUpdate(ITC_SETHTR); ParFloat(&drv->htr, PAR_NAN); } ParName("gasMode"); ParAccess(usUser); ParEnum(modeList); if (eab->syntax == TESLATRON) { ParInt(&drv->gasMode, 1); } else { ParList(""); ParInt(&drv->gasMode, 1); } ParName("gas"); ParFmt("%.1f"); ParTail("%"); if (drv->gasMode < 1) { ParList(""); } ParFloat(&drv->gas, PAR_NAN); ParName("setGas"); ParFmt("%.1f"); ParTail("%"); if (drv->gasMode < 1) { ParList(""); } EaseUpdate(ITC_SETGAS); ParSave(1); ParFloat(&drv->setGas, 20.0); if (eab->syntax != TESLATRON) { ParName("autoGasLimit"); ParAccess(usUser); ParFmt("%.1f"); ParTail("K"); ParSave(1); if (drv->gasMode != 2) { ParList(""); } ParFloat(&drv->autoGasLimit, 3.0); } if (drv->controlChan == 0) { i = drv->sampleChan; } else { i = drv->controlChan; } if (drv->dig[i] < 0) { snprintf(fmt, sizeof fmt, "%s", "%.3f"); } else { snprintf(fmt, sizeof fmt, "%s.%df", "%", drv->dig[i]); } EaseBasePar(drv); EaseSendPar(drv); if (eab->syntax != TESLATRON) { EaseDrivPar(drv, fmt, "K"); } ParStdDef(); EaseMsgPar(drv); } /*----------------------------------------------------------------------------*/ void ItcStatus(Itc *drv) { char *ans; int *code; if (drv->d.b.state != EASE_read) return; ans=drv->d.b.ans; code=&drv->d.b.errCode; if (ans[0] != 'X' || ans[2] != 'A' || ans[4] != 'C' || ans[6] != 'S') { ParPrintf(drv, eError, "illegal status response"); *code = EASE_FAULT; return; } drv->a = ans[3] - '0'; if (ans[9] == 'H') { drv->h = ans[10] - '0'; } else { drv->h = drv->controlChan; } if (ans[6] != '3' && drv->remote == 2) { ParPrintf(drv, eError, "ITC switched to local"); *code = EASE_FAULT; drv->remote = 1; return; } } /*----------------------------------------------------------------------------*/ static long ItcRead(long pc, void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; char *p; int l; char buf[4]; switch (pc) { default: /* FSM BEGIN *******************************/ EaseWrite(eab, "X"); return __LINE__; case __LINE__: /**********************************/ ItcStatus(drv); /* check for errors */ if (drv->dig[1] < 0) goto skip1; EaseWrite(eab, "R1"); /* read sensor 1 */ return __LINE__; case __LINE__: /**********************************/ drv->t[1] = OxiGet(eab, drv->dig[1], &drv->dig[1]); skip1: if (drv->dig[2] < 0) goto skip2; EaseWrite(eab, "R2"); /* read sensor 2 */ return __LINE__; case __LINE__: /**********************************/ drv->t[2] = OxiGet(eab, drv->dig[2], &drv->dig[2]); skip2: if (drv->dig[3] < 0) goto skip3; EaseWrite(eab, "R3"); /* read sensor 3 */ return __LINE__; case __LINE__: /**********************************/ drv->t[3] = OxiGet(eab, drv->dig[3], &drv->dig[3]); skip3: if (drv->controlChan == 0 || drv->a == 0) { drv->t[0] = drv->d.targetValue; goto skip0; } EaseWrite(eab, "R0"); /* read control T */ return __LINE__; case __LINE__: /**********************************/ drv->t[0] = OxiGet(eab, drv->dig[drv->controlChan], NULL); if (drv->gasMode == 2) { if (drv->t[drv->controlChan] > drv->autoGasLimit + 1.0) { if (drv->a < 2) { drv->a |= 2; /* switch gas to auto */ ParPrintf(drv, eWarning, "switch to auto needle valve"); } else { goto skip0; } } else { if (drv->a >= 2) { if (drv->t[drv->controlChan] < drv->autoGasLimit) { drv->a &= 1; /* switch gas to manual */ ParPrintf(drv, eWarning, "switch to manual needle valve (%f %%)", drv->setGas); } else { goto skip0; } } if (drv->setGas != drv->gas) { EaseSetUpdate(drv, ITC_SETGAS, 1); } } } else if (drv->a < 2) { goto skip0; } else { drv->a &= 1; /* switch gas to manual */ } EaseWrite(eab, "C3"); return __LINE__; case __LINE__: /**********************************/ drv->remote = 2; snprintf(buf, sizeof buf, "A%d", drv->a); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ skip0: if (!drv->remote) goto skiprmt; EaseWrite(eab, "C0"); drv->remote = 0; return __LINE__; case __LINE__: /**********************************/ skiprmt: if (EaseGetUpdate(drv, ITC_SETHTR)) goto skiphtr; EaseWrite(eab, "R5"); /* read heater */ return __LINE__; case __LINE__: /**********************************/ if (EaseGetUpdate(drv, ITC_SETHTR)) goto skiphtr; drv->htr = OxiGet(eab, 1, NULL); skiphtr: EaseWrite(eab, "R7"); /* read gas flow */ return __LINE__; case __LINE__: /**********************************/ drv->gas = OxiGet(eab, 1, NULL); skipgas: ParLog(drv); fsm_quit: return 0; } /* FSM END *********************************/ } /*----------------------------------------------------------------------------*/ static long ItcStart(long pc, void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; switch (pc) { default: /* FSM BEGIN *******************************/ EaseWrite(eab, "V"); return __LINE__; case __LINE__: /**********************************/ if (eab->syntax == TESLATRON) { if (0 != strncmp(eab->version, "TESLATRON", 9)) { snprintf(eab->msg, sizeof eab->msg, "unknown teslatron version: %s", eab->version); ParPrintf(drv, eError, "ERROR: %s", eab->msg); EaseStop(eab); goto quit; } } else { if (0 == strncmp(eab->version, "ITC503", 6)) { eab->syntax = 3; } else if (0 == strncmp(eab->version, "ITC4", 4)) { eab->syntax = 0; } else { snprintf(eab->msg, sizeof eab->msg, "unknown temperature controller version: %s", eab->version); ParPrintf(drv, eError, "ERROR: %s", eab->msg); EaseStop(eab); goto quit; } } ParPrintf(drv, eStatus, "connected to %s", eab->version); if (drv->controlChan == 0 && drv->h >= 1 && drv->h <= 3) { drv->controlChan = drv->h; } FsmCall(ItcRead); return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static long ItcSetTemp(long pc, void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; char buf[4]; int a; switch (pc) { default: /* FSM BEGIN *******************************/ if (drv->controlChan == 0) { ParPrintf(drv, eError, "no control channel selected"); goto quit; } if (drv->h == drv->controlChan) goto skiph; EaseWrite(eab, "A0"); /* heater off */ return __LINE__; case __LINE__: /**********************************/ drv->remote = 2; snprintf(buf, sizeof buf, "H%d", drv->controlChan); EaseWrite(eab, buf); /* set heater to channel */ return __LINE__; case __LINE__: /**********************************/ skiph: OxiSet(eab, "T", drv->d.targetValue, drv->dig[drv->controlChan]); /* set point */ return __LINE__; case __LINE__: /**********************************/ EaseWrite(eab, "L1"); /* 'auto' pid on */ return __LINE__; case __LINE__: /**********************************/ a = 1; /* auto heater */ if (drv->a >= 2) a = 3; /* auto gas & heater */ if (drv->d.targetValue == 0.0) { drv->htrMode = 0; a = 0; if (drv->setGas != drv->gas) { EaseSetUpdate(drv, ITC_SETGAS, 1); } } else { drv->htrMode = 2; /* heater auto */ } if (drv->h == drv->controlChan && drv->a == a) goto skipa; snprintf(buf, sizeof buf, "A%d", a); drv->a = a; EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ skipa: if (drv->a != 0) goto quit; EaseWrite(eab, "O0"); /* switch off heater */ return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static long ItcSetGas(long pc, void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; char buf[4]; switch (pc) { default: /* FSM BEGIN *******************************/ if (drv->gasMode != 1 && drv->gasMode != 2) { ParPrintf(drv, eError, "gasMode must be set to manual or auto"); goto quit; } if (drv->a == 2) { EaseWrite(eab, "A0"); } else if (drv->a == 3) { EaseWrite(eab, "A1"); } else { goto skipmode; } return __LINE__; case __LINE__: /**********************************/ skipmode: OxiSet(eab, "G", drv->setGas, 1); /* cold valve setting */ return __LINE__; case __LINE__: /**********************************/ EaseWrite(eab, "R7"); /* read gas flow */ return __LINE__; case __LINE__: /**********************************/ drv->gas = OxiGet(eab, 1, NULL); if (drv->a < 2) goto quit; snprintf(buf, sizeof buf, "A%d", drv->a); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static long ItcSetHtr(long pc, void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; char buf[4]; switch (pc) { default: /* FSM BEGIN *******************************/ if (drv->a == 0) goto skipmode; EaseWrite(eab, "A0"); return __LINE__; case __LINE__: /**********************************/ skipmode: OxiSet(eab, "O", drv->htr, 1); /* manual heater setting */ return __LINE__; case __LINE__: /**********************************/ if (drv->a == 0) goto quit; snprintf(buf, sizeof buf, "A%d", drv->a); EaseWrite(eab, buf); return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static long ItcSet(long pc, void *object) { Itc *drv = ParCast(&itcClass, object); EaseBase *eab = object; int upd; switch (pc) { default: /* FSM BEGIN *******************************/ EaseWrite(eab, "C3"); loop: return __LINE__; case __LINE__: /**********************************/ upd = EaseNextUpdate(drv); switch (upd) { case EASE_RUN: FsmCall(ItcSetTemp); goto loop; case ITC_SETHTR: FsmCall(ItcSetHtr); goto loop; case ITC_SETGAS: FsmCall(ItcSetGas); goto loop; default: break; } EaseWrite(eab, "C0"); return __LINE__; case __LINE__: /**********************************/ drv->remote = 0; quit: return 0; } /* FSM END ********************************************/ } /*----------------------------------------------------------------------------*/ static int ItcInit(SConnection *con, int argc, char *argv[], int dynamic) { /* args: MakeObject objectname itc MakeObject objectname itc */ Itc *drv; drv = EaseMakeDriv(con, &itcClass, argc, argv, dynamic, 7, ItcParDef, OxiHandler, ItcStart, NULL, ItcRead, ItcSet); if (drv == NULL) return 0; drv->d.b.syntax = 0; ParPrintf(drv, eValue, "OI Temperature Controller"); return 1; } /*----------------------------------------------------------------------------*/ static int ItcInitLc(SConnection *con, int argc, char *argv[], int dynamic) { /* args: MakeObject objectname lc MakeObject objectname lc */ Itc *drv; drv = EaseMakeDriv(con, &itcClass, argc, argv, dynamic, 7, ItcParDef, OxiHandler, ItcStart, NULL, ItcRead, ItcSet); if (drv == NULL) return 0; drv->d.b.syntax = TESLATRON; ParPrintf(drv, eValue, "OI Lambda Controller"); return 1; } /*----------------------------------------------------------------------------*/ void ItcStartup(void) { ParMakeClass(&itcClass, EaseDrivClass()); MakeDriver("ITC", ItcInit, 0); MakeDriver("LC", ItcInitLc, 0); }