From 8f7ed08086fd2030e887618af8d2cfe4330ae275 Mon Sep 17 00:00:00 2001 From: "Henrique F. Simoes" Date: Wed, 1 Mar 2023 11:09:28 -0300 Subject: [PATCH] heater: Add heater controller support Heater support include the switching between automatic and manual heating control, besides the parameters for both methods. For the manual heating setting, the voltage is used to indirectly control the temperature. On the other hand, automatic control (through PID method) is allows to specify a set-point temperature to be reached and Kp, Ti, and Td constants from both boards. Periodic scan is used in read-back records so that PV has the updated value when some external access is done to the device to update its configuration. An arbitrary value of 1 second has been chosen to keep the value reasonably updated while not overloading the device. Moreover, when a value is updated, the corresponding read-back value must be updated so that scripts can directly read back the correct value. Therefore, it has also been used a forward link to read-back from set-point records. Note that it is used the `PROC` field in the forwarding. This is because a non-passive scan is used in the read-back records. The PV names have been kept from the previous IOC. This implies that the issue reported about the misleading PID coefficient names has not been solved here [1]. This is because we should do that coordinated with the IOC clients. Therefore, Td should be read as Kd and Ti should be read as Ki. For this reason, they have been limited to be non-negative as defined in the PID controller theory. The same goes for Kp, which is referred as Kc by the firmware and thus in the protocol. The upper limit (DRVH) must be specified explicitly, so that the clipping condition `DRVL < DRVH` holds (as both values defaults to 0). In this case, it is unlimited (i.e. infinity). [1]: https://github.com/lnls-dig/bpm-epics-ioc/issues/35 --- BPMRFFEApp/Db/bpmrffe.db | 242 ++++++++++++++++++++++++++++++++++++ BPMRFFEApp/Db/bpmrffe.proto | 44 +++++++ 2 files changed, 286 insertions(+) diff --git a/BPMRFFEApp/Db/bpmrffe.db b/BPMRFFEApp/Db/bpmrffe.db index 0e8cce8..6adee52 100644 --- a/BPMRFFEApp/Db/bpmrffe.db +++ b/BPMRFFEApp/Db/bpmrffe.db @@ -41,6 +41,248 @@ record(ai, "$(P)$(R)RFFETempBD-Mon") { field(EGU, "oC") } +# Heating controls + +record(bo, "$(P)$(R)RFFETempCtl-SP") { + field(DESC, "Set auto temperature control") + field(DTYP, "stream") + field(PINI, "YES") + field(SCAN, "Passive") + field(VAL, "0") + field(ZNAM, "off") + field(ONAM, "on") + field(OUT, "@bpmrffe.proto outTempCtl $(PORT)") + field(FLNK, "$(P)$(R)RFFETempCtl-RB.PROC CA") +} + +record(bi, "$(P)$(R)RFFETempCtl-RB") { + field(DESC, "Get auto temperature control") + field(DTYP, "stream") + field(SCAN, "1 second") + field(ZNAM, "off") + field(ONAM, "on") + field(INP, "@bpmrffe.proto inTempCtl $(PORT)") +} + +# Manual heater control settings + +record(dfanout, "$(P)$(R)RFFEHeater-SP") { + field(DESC, "Set both boards' heater voltage") + field(OMSL, "supervisory") + field(OUTA, "$(P)$(R)RFFEHeaterAC-SP PP") + field(OUTB, "$(P)$(R)RFFEHeaterBD-SP PP") +} + +record(ao, "$(P)$(R)RFFEHeaterAC-SP") { + field(DESC, "Set AC board heater's voltage") + field(DTYP, "stream") + field(PINI, "YES") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outHeaterVolt(AC) $(PORT)") + field(EGU, "V") + field(FLNK, "$(P)$(R)RFFEHeaterAC-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEHeaterAC-RB") { + field(DESC, "Get AC board heater's voltage") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inHeaterVolt(AC) $(PORT)") + field(EGU, "V") +} + +record(ao, "$(P)$(R)RFFEHeaterBD-SP") { + field(DESC, "Set BD board heater's voltage") + field(DTYP, "stream") + field(PINI, "YES") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outHeaterVolt(BD) $(PORT)") + field(EGU, "V") + field(FLNK, "$(P)$(R)RFFEHeaterBD-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEHeaterBD-RB") { + field(DESC, "Get BD board heater's voltage") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inHeaterVolt(BD) $(PORT)") + field(EGU, "V") +} + +# PID (automatic) controller parameters + +record(dfanout, "$(P)$(R)RFFEPidSp-SP") { + field(DESC, "Set both boards' heater temp. setpoint") + field(OMSL, "supervisory") + field(OUTA, "$(P)$(R)RFFEPidSpAC-SP PP") + field(OUTB, "$(P)$(R)RFFEPidSpBD-SP PP") +} + +record(ao, "$(P)$(R)RFFEPidSpAC-SP") { + field(DESC, "Set AC board heater temp. setpoint") + field(DTYP, "stream") + field(PINI, "YES") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDTempSp(AC) $(PORT)") + field(EGU, "oC") + field(FLNK, "$(P)$(R)RFFEPidSpAC-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidSpAC-RB") { + field(DESC, "Get AC board heater temp. setpoint") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDTempSp(AC) $(PORT)") + field(EGU, "oC") +} + +record(ao, "$(P)$(R)RFFEPidSpBD-SP") { + field(DESC, "Set BD board heater temp. setpoint") + field(DTYP, "stream") + field(PINI, "YES") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDTempSp(BD) $(PORT)") + field(EGU, "oC") + field(FLNK, "$(P)$(R)RFFEPidSpBD-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidSpBD-RB") { + field(DESC, "Get BD board heater temp. setpoint") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDTempSp(BD) $(PORT)") + field(EGU, "oC") +} + +record(dfanout, "$(P)$(R)RFFEPidKp-SP") { + field(DESC, "Set both boards' PID Kp constant") + field(OMSL, "supervisory") + field(OUTA, "$(P)$(R)RFFEPidACKp-SP PP") + field(OUTB, "$(P)$(R)RFFEPidBDKp-SP PP") +} + +record(ao, "$(P)$(R)RFFEPidACKp-SP") { + field(DESC, "Set AC board's PID Kp constant") + field(DTYP, "stream") + field(PINI, "YES") + field(DRVL, "0") + field(DRVH, "inf") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDParam(Kc,AC) $(PORT)") + field(FLNK, "$(P)$(R)RFFEPidACKp-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidACKp-RB") { + field(DESC, "Get AC board's PID Kp constant") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDParam(Kc,AC) $(PORT)") +} + +record(ao, "$(P)$(R)RFFEPidBDKp-SP") { + field(DESC, "Set BD board's PID Kp constant") + field(DTYP, "stream") + field(PINI, "YES") + field(DRVL, "0") + field(DRVH, "inf") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDParam(Kc,BD) $(PORT)") + field(FLNK, "$(P)$(R)RFFEPidBDKp-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidBDKp-RB") { + field(DESC, "Get BD board's PID Kp constant") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDParam(Kc,BD) $(PORT)") +} + +record(dfanout, "$(P)$(R)RFFEPidTi-SP") { + field(DESC, "Set both boards' PID Ti constant") + field(OMSL, "supervisory") + field(OUTA, "$(P)$(R)RFFEPidACTi-SP PP") + field(OUTB, "$(P)$(R)RFFEPidBDTi-SP PP") +} + +record(ao, "$(P)$(R)RFFEPidACTi-SP") { + field(DESC, "Set AC board's PID Ti constant") + field(DTYP, "stream") + field(PINI, "YES") + field(DRVL, "0") + field(DRVH, "inf") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDParam(Ti,AC) $(PORT)") + field(FLNK, "$(P)$(R)RFFEPidACTi-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidACTi-RB") { + field(DESC, "Get AC board's PID Ti constant") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDParam(Ti,AC) $(PORT)") +} + +record(ao, "$(P)$(R)RFFEPidBDTi-SP") { + field(DESC, "Set BD board's PID Ti constant") + field(DTYP, "stream") + field(PINI, "YES") + field(DRVL, "0") + field(DRVH, "inf") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDParam(Ti,BD) $(PORT)") + field(FLNK, "$(P)$(R)RFFEPidBDTi-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidBDTi-RB") { + field(DESC, "Get BD board's PID Ti constant") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDParam(Ti,BD) $(PORT)") +} + +record(dfanout, "$(P)$(R)RFFEPidTd-SP") { + field(DESC, "Set both boards' PID Td constant") + field(OMSL, "supervisory") + field(OUTA, "$(P)$(R)RFFEPidACTd-SP PP") + field(OUTB, "$(P)$(R)RFFEPidBDTd-SP PP") +} + +record(ao, "$(P)$(R)RFFEPidACTd-SP") { + field(DESC, "Set AC board's PID Td constant") + field(DTYP, "stream") + field(PINI, "YES") + field(DRVL, "0") + field(DRVH, "inf") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDParam(Td,AC) $(PORT)") + field(FLNK, "$(P)$(R)RFFEPidACTd-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidACTd-RB") { + field(DESC, "Get AC board's PID Td constant") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDParam(Td,AC) $(PORT)") +} + +record(ao, "$(P)$(R)RFFEPidBDTd-SP") { + field(DESC, "Set BD board's PID Td constant") + field(DTYP, "stream") + field(DRVL, "0") + field(DRVH, "inf") + field(PINI, "YES") + field(SCAN, "Passive") + field(OUT, "@bpmrffe.proto outPIDParam(Td,BD) $(PORT)") + field(FLNK, "$(P)$(R)RFFEPidBDTd-RB.PROC CA") +} + +record(ai, "$(P)$(R)RFFEPidBDTd-RB") { + field(DESC, "Get BD board's PID Td constant") + field(DTYP, "stream") + field(SCAN, "1 second") + field(INP, "@bpmrffe.proto inPIDParam(Td,BD) $(PORT)") +} + # Operations record(bo, "$(P)$(R)RFFERst-SP") { diff --git a/BPMRFFEApp/Db/bpmrffe.proto b/BPMRFFEApp/Db/bpmrffe.proto index 128b1ef..362c6cd 100644 --- a/BPMRFFEApp/Db/bpmrffe.proto +++ b/BPMRFFEApp/Db/bpmrffe.proto @@ -20,6 +20,50 @@ inMeasTemp { in "%f"; } +# Heating control operations +outTempCtl { + out "SET:TEMPControl:AUTOmatic %{0|1}"; +} + +inTempCtl { + out "GET:TEMPControl:AUTOmatic?"; + in "%{0|1}"; +} + +# Manual heating setting operations +# $1: Board: AC | BD +outHeaterVolt { + out "SET:DAC:OUTput:\$1 %f"; +} + +inHeaterVolt { + out "GET:DAC:OUTput:\$1?"; + in "%f"; +} + +# Temperature setpoint setting operations +# $1: Board: AC | BD +outPIDTempSp { + out "SET:TEMPerature:SETPoint:\$1 %f"; +} + +inPIDTempSp { + out "GET:TEMPerature:SETPoint:\$1?"; + in "%f"; +} + +# PID controller parameters setting operations +# $1: Parameter: Kc | Ti | Td +# $2: Board: AC | BD +outPIDParam { + out "SET:PID:\$1:\$2 %f"; +} + +inPIDParam { + out "GET:PID:\$1:\$2?"; + in "%f"; +} + # Operation Rst { out "SYSTem:RESet";