{ Контроллер блока питания источника ионов 5.105.177 (АК-2) } {--------------------------------------------------------------------------- The control units for mass-spectrometer MI1201-AGM (c) Copyright Aleksandrov O.E., 1998 Модуль управления масс-спектрометром МИ1201-АГМ (c) Собственность Александрова О.Е., 1998 Molecular Physics department 620002, Екатеринбург, К-2 USTU, Ekaterinsburg, K-2, 620002 УГТУ, RUSSIA Кафедра молекулярной физики phone 75-48-39 тел. 75-48-39 E-mail: aleks@dpt.ustu.ru ----------------------------------------------------------------------------} Unit c_ISSB; INTERFACE USES MITypes, c_Ctrl, c_Ctrl1, c_Bus; const cDefaultTimeOut=500; { мс } cDefaultStepDelay=3; { задержка после шага двигателей управления напряжениями } type { Коды ошибок } tErrorCodes=({$I C_ErrCds.Inc}, ecValueOutOfUpRangeBound, ecValueOutOfLoRangeBound, ecFailSwitchBeam, ecFailReadFlags, ecFailStepDown, ecFailStepUp, ecFailexAllToLowLimit, ecFailexResetAllToCurValue, ecFailInitMotors, ecBPGIisOFF, ecHardwareError, ecBadControllerState); tFlag=(fBeamON, fSkipExDoneBeam, fSkipExDone, fIgnoreStepLimits); tFlags=set of tFlag; tTmpFlag=(fDone, fMotorsInitDone); tTmpFlags=set of tTmpFlag; tUnit=(IonizationVoltage, EmissionCurrent, ExtractingVoltage, FocusingVoltage, CorrectionX, CorrectionZ); tUnits=set of tUnit; tCount=longint; tValue=longint; tValueType=(vt_mStep, vt_mVolt, vt_mAmper); type tValueRangeData=record Max:longint; Min:longint; MaxCount:word; VType:tValueType; end; tValueRangeArray=array[tUnit] of tValueRangeData; tCounterArray=array[tUnit] of integer; tCurCounterArray=array[tUnit] of word; tPort=(roFlags, owStabilizatorBeam, owIonizationVoltage,owEmissionCurrent, owExtractingVoltage, owFocusingVoltage, owCorrectionX, owCorrectionZ); tPortsArray=array[tPort] of Byte; const cUnreadablePorts=[]; type tPorts0=record roFlags:word; owStabilizatorBeam:word; case byte of 0:(owIonizationVoltage, owEmissionCurrent, owExtractingVoltage, owFocusingVoltage, owCorrectionX, owCorrectionZ:word); 1:(owPort:array[tUnit] of word); end; tPorts=record case byte of 0:(P:tPorts0); 1:(PA:array[tPort] of word); end; tData=record Ports:tPorts; Flags:tFlags; StepDelay:word; PortsData:tPortsArray; CurCounters:tCurCounterArray; { сохраняемые значения счетчиков } end; tTmpData=record BusPtr:c_Bus.tCtrlPtr; Flags:tTmpFlags; RangesData:tValueRangeArray; Counters:tCounterArray; { значения счетчиков - 0 соответствует старту программы } CheckedUnits:tUnits; end; tCtrl=object(c_Ctrl1.tCtrl) function Name:tName; virtual; { НЕисполняемые функции (без ввода/вывода в порты)} constructor InitDefault(var Bus:c_Bus.tCtrl); constructor Init(var Bus:c_Bus.tCtrl; Ports:tPorts); destructor Done; function MaxCount(u:tUnit):tCount; function CurCount(u:tUnit):tCount; function MaxValue(u:tUnit):tValue; function MinValue(u:tUnit):tValue; function ValueType(u:tUnit):tValueType; function Step(u:tUnit):tValue; function IgnoreStepLimitGet:boolean; procedure IgnoreStepLimitSet(State:boolean); procedure CheckedUnitsGet(var AUnits:tUnits); function IsCheckedUnit(AUnit:tUnit):boolean; function Indicator(u:tUnit):integer; { округляет значение V до ближайшего допустимого для устройства U } function RoundValue(u:tUnit; v:tValue):tValue; { преобразует значение V до ближайшего допустимого значения счетчика для устройства U } function Value2Count(u:tUnit; v:tValue):tCount; { преобразует значение счетчика С до ближайшего допустимого значения для устройства U } function Count2Value(u:tUnit; c:tCount):tValue; function CurValue(u:tUnit):tValue; function CurStepDelay:word; procedure SetStepDelay(x:word); function ErrorMessage(en:tErrorCode):string; virtual; procedure SetNoError; virtual; { Сохранение/восстановление состояния контроллера } function DataSize:word; function Restore(var DataPtr:pointer):boolean; function Save(var DataPtr:pointer):boolean; { Исполняемые функции (ввод/вывод в порты)} procedure exInit; virtual; procedure exSetValue(u:tUnit; v:tValue); procedure exSetCount(u:tUnit; s:tCount); function exStepUp(u:tUnit):boolean; function exStepDown(u:tUnit):boolean; procedure exResetValue(u:tUnit); procedure exResetAllValues; procedure exBeamON(ABeamON:boolean); function exCurBeamON:boolean; procedure exCurFlags(var x:tCtrlFlags); function exBPGIisON:boolean; procedure exDetectCtrl(var x:tCtrlAlive); virtual; private prData:tData; prTmpData:tTmpData; procedure exAllToLowLimit; procedure exResetAllToCurValue; procedure SetAllCountersToLowLimit; procedure exCheckBPGIisON; function exDelayForBPGITurnON:boolean; function exWaitForBPGIIsON:boolean; function exCheckHardwareErrors:boolean; function exBadState:boolean; procedure CheckBadState; procedure exDone; virtual; function UpLimit(u:tUnit):boolean; function LoLimit(u:tUnit):boolean; {Процедуры сохранения значений в портах} procedure exReadPortsData(var PD:tPortsArray); procedure exSavePortsData; function exIsNotChangedPortsData:boolean; procedure ClearPortsData; procedure IncCurCount(u:tUnit); procedure DecCurCount(u:tUnit); function AbsoluteMaxCount:word; function AbsoluteMaxCurCount:word; function FindMax_MaxCount(AUnits:tUnits):word; function FindMax_CurCount(AUnits:tUnits):word; procedure exInitMotors; function PortsNumber:word; virtual; function exReadSafePorts(var PortsValues:tPortValuesArray; Count:word):boolean; virtual; end; IMPLEMENTATION USES xStrings, DataSave, MiscFunc; const cCtrlAlive=2; { число портов <>$FF для "живого" контроллера, используется для детектирования наличия } { описание рабочих диапазонов шаговых двигателей } cDefaultRanges:tValueRangeArray=( {IonizationVoltage} (Max:100000; Min:30000; {mkV} MaxCount:479; VType:vt_mVolt), {EmissionCurrent} (Max:1000; Min:0; {mkA} MaxCount:479; VType:vt_mAmper), {без привязки к напряжениям } {ExtractingVoltage} (Max:99; Min:0; { шаги } MaxCount:99; VType:vt_mStep), {FocusingVoltage} (Max:99; Min:0; { шаги } MaxCount:99; VType:vt_mStep), {CorrectionX} (Max:99; Min:0; { шаги } MaxCount:99; VType:vt_mStep), {CorrectionZ} (Max:99; Min:0; { шаги } MaxCount:99; VType:vt_mStep) ); cDefaultPorts:tPorts=(P:( roFlags :$90; owStabilizatorBeam :$91; owIonizationVoltage:$97; owEmissionCurrent :$96; owExtractingVoltage:$95; owFocusingVoltage :$94; owCorrectionX :$93; owCorrectionZ :$92 )); {------------------------------------------------------------------------} { Инициализация (без ввода/вывода в порты)} constructor tCtrl.Init(var Bus:c_Bus.tCtrl; Ports:tPorts); var u:tUnit; begin Inherited Init; prTmpData.BusPtr:=@Bus; prTmpData.Flags:=[]; prTmpData.CheckedUnits:=[]; for u:=Low(u) to High(u) do begin prTmpData.Counters[u]:=0; prData.CurCounters[u]:=0; end; // prTmpData.Indicators:=prTmpData.Counters; prData.Ports:=Ports; prData.Flags:=[]; prTmpData.RangesData:=cDefaultRanges; TimeOut(cDefaultTimeOut); SetStepDelay(cDefaultStepDelay); IgnoreStepLimitSet(TRUE); If Bus.Error {and NoError} then begin SetErrorCode(tErrorCode(ecBadBus)); end; end; constructor tCtrl.InitDefault(var Bus:c_Bus.tCtrl); begin Init(Bus, cDefaultPorts); end; destructor tCtrl.Done; begin if NotInitialized then Exit; Inherited Done; prTmpData.BusPtr:=nil; Include(prTmpData.Flags,fDone); end; function tCtrl.Name; begin Name:='ISSB'; end; (*procedure tCtrl.exRestore; begin // CheckExInitDone; Inherited exRestore(DataPtr); RestoreData(DataPtr, prData, SizeOf(prData)); if NoError then begin exBeamON(fBeamON in prData.Flags); end; end; *) (*procedure tCtrl.Save; begin Inherited Save(DataPtr); StoreData(DataPtr, prData, SizeOf(prData)); end;*) function tCtrl.Restore(var DataPtr:pointer):boolean; begin if Inherited Restore(DataPtr) then begin Restore:=RestoreDataEx(DataPtr, prData, SizeOf(prData)); end else begin Restore:=FALSE; end; end; function tCtrl.Save(var DataPtr:pointer):boolean; begin if Inherited Save(DataPtr) then Save:=StoreDataEx(DataPtr, prData, SizeOf(prData)) else Save:=FALSE; end; function tCtrl.DataSize:word; begin DataSize:=Inherited DataSize + DataSave.SizeOfData(SizeOf(prData)); end; {------------------------------------------------------------------------} { Установка параметров (без ввода/вывода в порты)} {------------------------------------------------------------------------} { Чтение текущих значений параметров (без ввода/вывода в порты)} function tCtrl.CurCount(u:tUnit):tCount; begin CurCount:=prData.CurCounters[u]; end; procedure tCtrl.IncCurCount(u:tUnit); begin if prData.CurCounters[u]0 then Dec(prData.CurCounters[u]); end; function tCtrl.MaxCount(u:tUnit):tCount; begin MaxCount:=prTmpData.RangesData[u].MaxCount; end; function tCtrl.CurValue(u:tUnit):tValue; begin with prTmpData.RangesData[u] do CurValue:=Min+(CurCount(u)*(Max-Min)+(MaxCount div 2)) div MaxCount; end; function tCtrl.MaxValue(u:tUnit):tValue; begin MaxValue:=prTmpData.RangesData[u].Max; end; function tCtrl.MinValue(u:tUnit):tValue; begin MinValue:=prTmpData.RangesData[u].Min; end; function tCtrl.ValueType(u:tUnit):tValueType; begin ValueType:=prTmpData.RangesData[u].VType; end; function tCtrl.Step(u:tUnit):tValue; begin with prTmpData.RangesData[u] do Step:=(Max-Min + (MaxCount div 2)) div MaxCount; end; function tCtrl.RoundValue(u:tUnit; v:tValue):tValue; begin RoundValue:=Count2Value(u, Value2Count(u,v)); end; function tCtrl.Value2Count(u:tUnit; v:tValue):tCount; var c:longint; vm,d,dh:tValue; begin with prTmpData.RangesData[u] do begin {$IfOpt R+ } // if vMax then RunError(201); {$Else If} // if vMax then v:=Max; {$EndIf} d:=(Max-Min); dh:=(d div 2); vm:=(v-Min); if Abs(vm)<((High(c)-dh) div MaxCount) then begin c:=(vm*MaxCount+dh) div d; end else if vm<0 then begin c:=0 end else begin c:=MaxCount end; // {$IfOpt R+ } if c>MaxCount then RunError(201); {$EndIf} end; Value2Count:=c; end; function tCtrl.Count2Value(u:tUnit; c:tCount):tValue; var v, dm,hmc:tValue; begin with prTmpData.RangesData[u] do begin dm:=(Max-Min); hmc:=(MaxCount div 2); if Abs(c)<((High(v)-hmc) div dm) then begin v:=Min+((c*dm+hmc) div MaxCount); end else if c<0 then begin v:=Min; end else begin v:=Max; end; (* {$IfOpt R+ } if (v>Max) or (vMaxCount(u)) then RunError(201);} {$EndIf} i:=s-CurCount(u); if i>MaxCount(u) then i:=MaxCount(u); if i<-MaxCount(u) then i:=-MaxCount(u); dt:=Abs(i)*CurStepDelay; OperationTimeInc(dt); while (i>0) and NoError and not UpLimit(u) do begin exStepUp(u); Dec(i); end; while (i<0) and NoError and not LoLimit(u) do begin exStepDown(u); Inc(i); end; OperationTimeDec(dt); end; procedure tCtrl.exResetAllValues; begin CheckExInitDone; exAllToLowLimit; SetAllCountersToLowLimit; end; procedure tCtrl.exResetValue(u:tUnit); var c:tCount; begin c:=CurCount(u); exSetCount(u,-MaxCount(u)); exSetCount(u,c); if NoError then Include(prTmpData.CheckedUnits,u) end; procedure tCtrl.exBeamON(ABeamON:boolean); var b:byte; patt:tCtrlFlags; const cBeamMask:tCtrlFlags=[fBeam_Off]; begin CheckExInitDone; If NoError then begin OperationTimeInc(2*CurTimeOut); { Ожидание сделано потому, что при быстром включении блоков луч включиться не успевал } if ABeamON then begin exWaitForBPGIIsON; end; if exBPGIIsON then begin OperationTimeDec(CurTimeOut); if ABeamON then begin b:=0; patt:=[]; end else begin b:=1; patt:=[fBeam_Off]; end; prTmpData.BusPtr^.exOutByte(prData.Ports.P.owStabilizatorBeam, b); if prTmpData.BusPtr^.exWait(prData.Ports.P.roFlags, Byte(cBeamMask), Byte(patt), CurTimeOut)<>0 then begin prTmpData.BusPtr^.exOutByte(prData.Ports.P.owStabilizatorBeam, b); if prTmpData.BusPtr^.exWait(prData.Ports.P.roFlags, Byte(cBeamMask), Byte(patt), CurTimeOut)<>0 then begin SetErrorCode(tErrorCode(ecFailSwitchBeam)); end else begin if ABeamON then Include(prData.Flags, fBeamON) else Exclude(prData.Flags, fBeamON); end; end else begin if ABeamON then Include(prData.Flags, fBeamON) else Exclude(prData.Flags, fBeamON); end; end; OperationTimeDec(CurTimeOut); end; (* CheckExInitDone; If NoError then begin OperationTimeInc(2*CurTimeOut); if exDelayForBPGITurnON then begin OperationTimeDec(CurTimeOut); if ABeamON then begin b:=0; patt:=[]; end else begin b:=1; patt:=[fBeam_Off]; end; prTmpData.BusPtr^.exOutByte(prData.Ports.P.owStabilizatorBeam, b); if prTmpData.BusPtr^.exWait(prData.Ports.P.roFlags, Byte(cBeamMask), Byte(patt), CurTimeOut)<>0 then begin SetErrorCode(tErrorCode(ecFailSwitchBeam)); end else begin if ABeamON then Include(prData.Flags, fBeamON) else Exclude(prData.Flags, fBeamON); end; end; OperationTimeDec(CurTimeOut); end;*) end; function tCtrl.exCurBeamON:boolean; var x:tCtrlFlags; begin CheckExInitDone; exCurFlags(x); If NoError then begin exCurBeamON:=not (fBeam_Off in x); end else begin exCurBeamON:=False; end; end; procedure tCtrl.exCurFlags(var x:tCtrlFlags); begin CheckExInitDone; x:=[]; If NoError then begin Byte(x):=prTmpData.BusPtr^.exInByte(prData.Ports.P.roFlags); if prTmpData.BusPtr^.Error then begin SetErrorCode(tErrorCode(ecFailReadFlags)); end; end; end; {------------------------------------------------------------------------} { * PRIVATE секция } function tCtrl.FindMax_MaxCount(AUnits:tUnits):word; var Max:word; u:tUnit; begin { Поиск максимального значениям счетчика шагов для всех двигателей } Max:=0; for u:=Low(u) to High(u) do if u in AUnits then begin if Max=MaxCount(u)) then begin Include(prTmpData.CheckedUnits,u); end; exStepUp:=not exCheckHardwareErrors; { задержка для срабатывания } prTmpData.BusPtr^.Delay0(ti.TimeLeft); end else begin { НЕуспешно - возбудить состояние ошибки } SetErrorCode(tErrorCode(ecFailStepUp)); Dec(prTmpData.Counters[u]); end; OperationTimeDec(CurStepDelay); end; end; function tCtrl.exStepDown(u:tUnit):boolean; { Шаг двигателя ВНИЗ: последовательный вывод значений 3, 2, 1, 0 в порт двигателя } var ti:tTimeInterval; begin exStepDown:=FALSE; if LoLimit(u) then Exit; exInitMotors; If NoError then begin OperationTimeInc(CurStepDelay); Dec(prTmpData.Counters[u]); ti.Start(CurStepDelay); {$IfNDEf MotorsOff} prTmpData.BusPtr^.exOutByte(prData.Ports.P.owPort[u], Lo(prTmpData.Counters[u])); {$EndIf NDEf MotorsOff} { Обработка ошибки } if prTmpData.BusPtr^.NoError then begin { успешно - изменить счетчик шагов } DecCurCount(u); if (prTmpData.Counters[u]<=-MaxCount(u)) then begin Include(prTmpData.CheckedUnits,u); end; exStepDown:=not exCheckHardwareErrors; { задержка для срабатывания } prTmpData.BusPtr^.Delay0(ti.TimeLeft); end else begin { НЕуспешно - возбудить состояние ошибки } Inc(prTmpData.Counters[u]); SetErrorCode(tErrorCode(ecFailStepDown)); end; OperationTimeDec(CurStepDelay); end; end; function tCtrl.exBPGIisON:boolean; var hdrFlags:tCtrlFlags; begin hdrFlags:=[]; exCurFlags(hdrFlags); exBPGIisON:=NoError and (fBPGI_On in hdrFlags); end; function tCtrl.exDelayForBPGITurnON:boolean; begin If exBPGIisON then begin OperationTimeInc(CurTimeOut); prTmpData.BusPtr^.Delay(CurTimeOut); exDelayForBPGITurnON:=exBPGIisON; OperationTimeDec(CurTimeOut); end else begin exDelayForBPGITurnON:=FALSE; end; end; function tCtrl.exWaitForBPGIIsON:boolean; const cBPGION=1 shl Ord(fBPGI_On); var OK:boolean; begin If NoError then begin OK:=tErrorCodes(prTmpData.BusPtr^.exLazyWait(prData.Ports.P.roFlags, cBPGION,cBPGION, CurTimeOut, 1))=ecOK; exWaitForBPGIIsON:=OK; if not OK and (tErrorCodes(prTmpData.BusPtr^.ErrorCode)=ecTimeOut) then prTmpData.BusPtr^.SetNoError; end else begin exWaitForBPGIIsON:=FALSE; end; end; procedure tCtrl.exCheckBPGIisON; begin if not exBPGIisON then if NoError then SetErrorCode(tErrorCode(ecBPGIisOFF)); end; function tCtrl.exCheckHardwareErrors:boolean; var hdrFlags:tCtrlFlags; begin If NoError then begin exCurFlags(hdrFlags); if NoError and (fBPGI_ON in hdrFlags) and ((not (fCatodOK in hdrFlags)) or (fOverload in hdrFlags)) then SetErrorCode(tErrorCode(ecHardwareError)); end; exCheckHardwareErrors:=Error; end; procedure tCtrl.exDetectCtrl(var x:tCtrlAlive); var c:word; begin c:=prTmpData.BusPtr^.exScanNotFFports(prData.Ports, (SizeOf(prData.Ports) shr 1)); case c of cCtrlAlive:x:=aYes; 0: x:=aNo; else if c>=(cCtrlAlive shr 1) then x:=aMayBeYes else x:=aMayBeNo; end; end; procedure tCtrl.CheckBadState; begin if exBadState then SetErrorCode(tErrorCode(ecBadControllerState)); end; function tCtrl.exBadState:boolean; var e:word; begin e:=prTmpData.BusPtr^.ErrorCode; if e<>0 then prTmpData.BusPtr^.SetNoError; exBadState:=(prTmpData.BusPtr^.exInByte(prData.Ports.P.roFlags)=$FF); if prTmpData.BusPtr^.Error then prTmpData.BusPtr^.SetNoError; prTmpData.BusPtr^.SetErrorCode(e); end; procedure tCtrl.exReadPortsData; var i:tPort; begin ClearPortsDataX(PD); if NoError then begin for i:=Low(tPort) to High(tPort) do begin if not (i in cUnreadablePorts) then begin PD[i]:=prTmpData.BusPtr^.exInByte(prData.Ports.PA[i]); if prTmpData.BusPtr^.NoError then begin SetErrorCode(Ord(ecFailReadPortsData)); break; end; end; end; end; end; function tCtrl.PortsNumber:word; begin PortsNumber:=Succ(Ord(High(tPort))); end; function tCtrl.exReadSafePorts(var PortsValues:tPortValuesArray; Count:word):boolean; var i:tPort; begin exReadSafePorts:=FALSE; if NoError and (Count=PortsNumber) then begin for i:=Low(tPort) to High(tPort) do begin if prTmpData.BusPtr^.NoError then begin if not (i in cUnreadablePorts) then PortsValues[Ord(i)]:=prTmpData.BusPtr^.exInByte(prData.Ports.PA[i]) else PortsValues[Ord(i)]:=$FF; end else begin SetErrorCode(Ord(ecFailReadPortsData)); break; end; end; if prTmpData.BusPtr^.Error then SetErrorCode(Ord(ecFailReadPortsData)) else exReadSafePorts:=TRUE; end; end; procedure tCtrl.exSavePortsData; begin exReadPortsData(prData.PortsData); end; procedure tCtrl.ClearPortsData; begin ClearPortsDataX(prData.PortsData); end; function tCtrl.exIsNotChangedPortsData:boolean; var PD:tPortsArray; begin exReadPortsData(pd); exIsNotChangedPortsData:=Equal(PD,prData.PortsData,SizeOf(PD)); end; function tCtrl.IgnoreStepLimitGet:boolean; begin IgnoreStepLimitGet:=fIgnoreStepLimits in prData.Flags; end; procedure tCtrl.IgnoreStepLimitSet(State:boolean); begin if State then Include(prData.Flags,fIgnoreStepLimits) else Exclude(prData.Flags,fIgnoreStepLimits); end; procedure tCtrl.CheckedUnitsGet(var AUnits:tUnits); begin AUnits:=prTmpData.CheckedUnits; end; function tCtrl.IsCheckedUnit(AUnit:tUnit):boolean; begin IsCheckedUnit:=AUnit in prTmpData.CheckedUnits; end; function tCtrl.Indicator(u:tUnit):integer; begin Indicator:=prTmpData.Counters[u]; end; END.