{ Контроллер счетчика ионов 5.105.182 (АК-7) } {--------------------------------------------------------------------------- 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_Count; {$X+} INTERFACE USES c_Ctrl, c_Ctrl1, c_Bus, MItypes; const {Значения параметров по-умолчанию } cDefaultDivCoeff=1000; { 1000 соответствует миллисекундам } cDefaultIntegrationTime=100; { [мс] для для коэфф. деления =1000 } cDefaultTimeOut={500} 1000; { [мс] } cDefaultResetCtrl=True; { аппаратный сброс и инициализаци контроллера в начале каждого измерения } type { Коды ошибок } tErrorCodes=({$I C_ErrCds.Inc}, ecFailReset, ecFailResetTrigger, ecFailStart, ecFailRead, ecFailReady, ecInvalid_Time_DivCoeff_Combination, ecBadControllerState); tFlag=(fResetCtrl, fDoInitialResetCtrl); tFlags=set of tFlag; tTmpFlag=(fStarted, fResetCtrlOnce); tTmpFlags=set of tTmpFlag; tTime=1..$FFFF; tDivCoeff=1..$FFFF; tPort=(rwReady_PreCount, rwByte2_Gate, rwByte1_Start, owResetTrigger, owTimerCntrlWord, owTimerIntegrationTime, owDivisionCoefficient, rwBytes3_4); tPortsArray=array[0..Ord(High(tPort))+1] of Byte; const cUnreadablePorts=[]; type tPorts=record case byte of 0:(rwReady_PreCount, rwByte2_Gate, rwByte1_Start, owResetTrigger, owTimerCntrlWord, owTimerIntegrationTime, owDivisionCoefficient, rwBytes3_4:word); 1:(Ports:array[tPort] of word); end; tData=record Ports:tPorts; Flags:tFlags; DivCoeff:tDivCoeff; IntegrationTime:tTime; { время интегрирования с которым БУДЕТ запущено измерение} LastData:tSignalData; PortsData:tPortsArray; end; tTmpData=record BusPtr:c_Bus.tCtrlPtr; Flags:tTmpFlags; StartTime:longint; IntegrationTime:tTime; { время интегрирования с которым было запущено последнее измерение} end; { флаги состояния контроллера } tCtrlFlag=(fBusy, f1, f2, f3, f4, f5, f6, f7); tCtrlFlags= set of tCtrlFlag; 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; procedure DivCoeff(x:tDivCoeff); { коэффициент деления: 1 счетчика соответствует (1 сек)/DivCoeff} procedure IntegrationTime(x:tTime); { время счета в миллисекундах. Всегда и независимо от DivCoeff. Перевод в необходимые единицы при запуске измерения. Если перевод невозможен - возбуждается ошибка.} procedure ResetCtrl(x:boolean); function CurDivCoeff:tDivCoeff; { возвр. текущий коэфф. деления для таймера } function CurIntegrationTime:tTime; { возвр. текущее время интегрирования [мс] } function CurResetCtrl:boolean; { возвр. текущее состояние для аппаратного сброса и инициализации в начале каждого измерения (TRUE - применяется сброс) } function CurCount:tSignalData; { возвр. данные последнего измерения } procedure SetErrorCode(ec:tErrorCode); function ErrorMessage(en:tErrorCode):string; virtual; procedure SetNoError; virtual; { Сохранение/восстановление состояния контроллера } function DataSize:word; // procedure Save(var DataPtr:pointer); // procedure exRestore(var DataPtr:pointer); function Restore(var DataPtr:pointer):boolean; function Save(var DataPtr:pointer):boolean; { Исполняемые функции (ввод/вывод в порты)} procedure exInit; virtual; // procedure exReInit; virtual; function exGetData:tSignalData; { запуск, ожидание окончания счета и возвращает результат измерения } function exStart:boolean; { запуск } function exReady:boolean; { возвращает TRUE если счет закончен } function exWaitReady:boolean; function exRead:tSignalData; { ожидание окончания счета и возвращает результат измерения } function exJustRead:tSignalData; { возвращает текущее значение счетчика} procedure exDetectCtrl(var x:tCtrlAlive); virtual; private prData:tData; prTmpData:tTmpData; procedure exInitialReset; function exWait:boolean; function exLazyWait:boolean; procedure DelayWait; function exReset:boolean; function exResetTrigger:boolean; procedure exDone; virtual; procedure exDoStart; {Процедуры сохранения значений в портах} procedure exReadPortsData(var PD:tPortsArray); procedure exSavePortsData; function exIsNotChangedPortsData:boolean; procedure ClearPortsData; { Вспомогательные функции определния наличия питания, см. exIsPowerON} function PortsNumber:word; virtual; function exReadSafePorts(var PortsValues:tPortValuesArray; Count:word):boolean; virtual; end; function ValidCombination(DC:tDivCoeff; IT:tTime):boolean; IMPLEMENTATION USES {$IFDef LogPortIO} SysUtils, PortsIOLog,{$EndIF Def LogPortIO} xStrings, DataSave, MiscFunc; const cCtrlAlive=2; { число портов <>$FF для "живого" контроллера, используется для детектирования наличия } { ВОТ ГАДЫ!, нет чтобы сказать что для счетчика ионов адреса портов указаны абсолютные, а не относительно базового $ED00. Слагаемое $1300 сделано чтобы общую схему вычисления адреса порта не нарушать: ($PortShift+$1300+$ED00) and $FFFF = $PortShift} cDefaultPorts:tPorts=( rwReady_PreCount: $121; rwByte2_Gate: $122; rwByte1_Start: $123; owResetTrigger: $120; owTimerCntrlWord: $124; owTimerIntegrationTime:$125; owDivisionCoefficient: $126; rwBytes3_4: $127 ); function DCandIT2Counter(DC:tDivCoeff; IT:tTime):word; var x:longint; begin DCandIT2Counter:=0; if DC>0 then begin x:=(Longint(IT)*1000) div DC; if x<=$FFFF then DCandIT2Counter:=x; end; end; function ValidCombination(DC:tDivCoeff; IT:tTime):boolean; begin ValidCombination:=DCandIT2Counter(dc,it)<>0; end; {------------------------------------------------------------------------} { Инициализация (без ввода/вывода в порты)} constructor tCtrl.Init(var Bus:c_Bus.tCtrl; Ports:tPorts); begin Inherited Init; prTmpData.BusPtr:=@Bus; prTmpData.IntegrationTime:=1; prData.Ports:=Ports; prData.Flags:=[fDoInitialResetCtrl]; prData.LastData:=0; DivCoeff(cDefaultDivCoeff); IntegrationTime(cDefaultIntegrationTime); TimeOut(cDefaultTimeOut); ResetCtrl(cDefaultResetCtrl); ClearPortsData; 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 end; function tCtrl.Name; begin Name:='IonCounter'; end; (*procedure tCtrl.exRestore; begin Inherited exRestore(DataPtr); RestoreData(DataPtr, prData, SizeOf(prData)); Include(prTmpData.Flags, fResetCtrlOnce); OperationTimeReset(0); end;*) (*procedure tCtrl.Save; begin exSavePortsData; Inherited Save(DataPtr); StoreData(DataPtr, prData, SizeOf(prData)); end;*) function tCtrl.Restore(var DataPtr:pointer):boolean; begin if Inherited Restore(DataPtr) then Restore:=RestoreDataEx(DataPtr, prData, SizeOf(prData)) else Restore:=FALSE; Include(prTmpData.Flags, fResetCtrlOnce); 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; {------------------------------------------------------------------------} { Установка параметров (без ввода/вывода в порты)} procedure tCtrl.DivCoeff(x:tDivCoeff); begin prData.DivCoeff:=x; Include(prTmpData.Flags, fResetCtrlOnce); end; procedure tCtrl.IntegrationTime(x:tTime); begin prData.IntegrationTime:=x; Include(prTmpData.Flags, fResetCtrlOnce); end; procedure tCtrl.ResetCtrl(x:boolean); begin if x then Include(prData.Flags, fResetCtrl) else Exclude(prData.Flags, fResetCtrl); end; function tCtrl.CurCount:tSignalData; begin CurCount:=prData.LastData; end; {------------------------------------------------------------------------} { Чтение текущих значений параметров (без ввода/вывода в порты)} function tCtrl.CurDivCoeff:tDivCoeff; begin CurDivCoeff:=prData.DivCoeff; end; function tCtrl.CurIntegrationTime:tTime; begin CurIntegrationTime:=prData.IntegrationTime; end; function tCtrl.CurResetCtrl:boolean; begin CurResetCtrl:=(fResetCtrl in prData.Flags); end; procedure tCtrl.SetErrorCode(ec:tErrorCode); begin if ec<>0 then Include(prData.Flags, fDoInitialResetCtrl); Inherited SetErrorCode(ec); end; procedure tCtrl.SetNoError; begin Inherited SetNoError; prTmpData.BusPtr^.SetErrorCode(0); end; function ErrorMessage(en:tErrorCode):string; begin case tErrorCodes(en) of ecFailReset: ErrorMessage:='Ошибка установки праметров контроллера'; ecFailResetTrigger: ErrorMessage:='Ошибка аппаратного сброса контроллера'; ecFailStart: ErrorMessage:='Ошибка запуска счета'; ecTimeOut: ErrorMessage:='Истекло время ожидания готовности'; ecFailRead: ErrorMessage:='Ошибка чтения данных'; ecFailReady: ErrorMessage:='Ошибка чтения состояния счета'; ecInvalid_Time_DivCoeff_Combination: ErrorMessage:='Недопустимое сочетание времени интегрирования и коэффициента деления'; ecBadControllerState: ErrorMessage:='Ошибочное состояние контроллера'; else ErrorMessage:='Неизвестная ошибка'; end; end; function tCtrl.ErrorMessage(en:tErrorCode):string; begin case tErrorCodes(en) of ecOK, ecAbort, ecNotInitialized: ErrorMessage:=Inherited ErrorMessage(en)+' (Ion Counter)'; else ErrorMessage:=Inherited ErrorMessage(en)+' (Ion Counter)'; end; end; {------------------------------------------------------------------------} { Исполняемые функции (ввод/вывод в порты)} procedure tCtrl.exInit; var t:word; begin Inherited exInit; if IsExInitDone then begin t:=CurIntegrationTime; IntegrationTime(1); exDoStart; exStart; exGetData; SetExInitDone(NoError); IntegrationTime(t); end; OperationTimeReset(0); end; function tCtrl.exGetData:tSignalData; begin exStart; exGetData:=exRead; OperationTimeReset(0); end; procedure tCtrl.exDoStart; const h0000:word=0; begin with prData.Ports do begin prTmpData.BusPtr^.exOutByteX(rwByte2_Gate, 0); { установить Gate } prTmpData.BusPtr^.exOutBytesX(rwReady_PreCount, h0000, 2); { досчитать 2 импульса } prTmpData.BusPtr^.exOutByteX(rwByte1_Start, 0); { запустить счет } end; end; function tCtrl.exStart:boolean; const cStart=(1 shl Ord(fBusy)); begin {$IfDef LogPortIO} PortsIOLog.LogStrLn('-----------StartCount'); {$EndIf Def LogPortIO} OperationTimeReSet(2*CurTimeOut); {$IFNdef DBG} временное исправление {$EndIF} if fResetCtrlOnce in prTmpData.Flags then begin MarkTime; end; exReset; If exWait then with prTmpData.BusPtr^ do begin exDoStart; MarkTime; if (prTmpData.IntegrationTime>=500) and (prTmpData.BusPtr^.exWaitX(prData.Ports.rwReady_PreCount, cStart, cStart, CurTimeOut)<>0) then begin SetErrorCode(tErrorCode(ecFailStart)); end else begin OperationTimeReSet(prTmpData.IntegrationTime+CurTimeOut); end; end; exStart:=NoError; end; function tCtrl.exReady:boolean; begin exReady:=FALSE; If NoError then begin if not (fBusy in tCtrlFlags(prTmpData.BusPtr^.exInByteX(prData.Ports.rwReady_PreCount))) then begin if (prTmpData.BusPtr^.NoError) then begin OperationTimeReset(0); exReady:=TRUE; end else begin SetErrorCode(tErrorCode(ecFailReady)); end; end; end; end; function tCtrl.exWaitReady:boolean; begin exWaitReady:=exLazyWait; end; function tCtrl.exRead:tSignalData; begin If exLazyWait then begin prData.LastData:=exJustRead; exRead:=prData.LastData; end else begin exRead:=0; end; OperationTimeReset(0); end; function tCtrl.exJustRead:tSignalData; { возвращает текущее значение счетчика} var dw:tDWord; begin exJustRead:=0; If NoError then begin dw.b1:=prTmpData.BusPtr^.exInByteX(prData.Ports.rwByte1_Start); dw.b2:=prTmpData.BusPtr^.exInByteX(prData.Ports.rwByte2_Gate); prTmpData.BusPtr^.exInBytesX(prData.Ports.rwBytes3_4, dw.w2, 2); dw.w2:=not dw.w2; if prTmpData.BusPtr^.NoError then begin exJustRead:=tSignalData(dw); end else begin Self.SetErrorCode(tErrorCode(ecFailRead)); end; end; end; { * PRIVATE секция } procedure tCtrl.exInitialReset; begin CheckExInitDone; if (fDoInitialResetCtrl in prData.Flags) then exReset; end; procedure tCtrl.DelayWait; var t:longint; const cDt=10; begin If NoError then begin t:=IntelliTimeOut(prTmpData.IntegrationTime,0); Dec(t,cDt); if t>0 then prTmpData.BusPtr^.Delay(t); end; end; function tCtrl.exWait:boolean; var t,t0:tTiks; begin If NoError then begin t:=IntelliTimeOut(tTiks(prTmpData.IntegrationTime)+CurTimeOut,CurTimeOut); t0:=OperationTime; OperationTimeSet(t); exWait:=TRUE; if prTmpData.BusPtr^.exLazyWaitX( prData.Ports.rwReady_PreCount, (1 shl Ord(fBusy)), 0, t,0 )<>0 then begin SetErrorCode(tErrorCode(ecTimeOut)); exWait:=FALSE; end else begin OperationTimeReset(t0); end; end else begin exWait:=FALSE; end; end; function tCtrl.exLazyWait:boolean; var t,t0:tTiks; begin If not exReady then begin DelayWait; t:=prTmpData.IntegrationTime+CurTimeOut; t0:=OperationTime; OperationTimeSet(t); if exReady or (prTmpData.BusPtr^.exLazyWaitX(prData.Ports.rwReady_PreCount, (1 shl Ord(fBusy)), 0, t,1)=0) then begin exLazyWait:=TRUE; OperationTimeReSet(t0); end else begin SetErrorCode(tErrorCode(ecTimeOut)); exLazyWait:=FALSE; end; end else begin exLazyWait:=TRUE; end; end; function tCtrl.exResetTrigger:boolean; begin If NoError then begin exResetTrigger:=TRUE; prTmpData.BusPtr^.exOutByteX(prData.Ports.owResetTrigger, 0); if prTmpData.BusPtr^.Error then begin Self.SetErrorCode(tErrorCode(ecFailResetTrigger)); exResetTrigger:=FALSE; end; end else begin exResetTrigger:=FALSE; end; end; function tCtrl.exReset:boolean; const h3474B2:array[1..3] of Byte=($34, $74, $B2); { управляющие слова таймеров для счетчика ионов} hFFFF:word=$FFFF; { байты для записи в счетчики при инициализации } var x:longint; lIntegrationTime, TimeCount:word; begin exReset:=FALSE; {$IFNdef DBG} временное исправление {$EndIF} if fResetCtrlOnce in prTmpData.Flags then begin MarkTime; end; If exResetTrigger and ((fDoInitialResetCtrl in prData.Flags) or (fResetCtrl in prData.Flags) or (fResetCtrlOnce in prTmpData.Flags)) then begin with prData.Ports do begin { считывание локально текущего времени, для обеспечения реентерабельности и многопотоковости } lIntegrationTime:=prData.IntegrationTime; { перевод времени из миллисекунд в соответствии с коэффициентом деления } x:=DCandIT2Counter(prData.DivCoeff, lIntegrationTime); if (0<>x) then with prTmpData.BusPtr^ do begin TimeCount:=x; { Установка параметров контроллера } exOutBytesX(owTimerCntrlWord, h3474B2, 3); {запись управляющего слова таймера } exOutBytesX(owTimerIntegrationTime, TimeCount, 2); {запись времени интегрирования таймера} if prTmpData.BusPtr^.NoError then begin { запоминание установленного времени интегрирования, для обеспечения реентерабельности и многопотоковости } prTmpData.IntegrationTime:=lIntegrationTime; end; exOutBytesX(owDivisionCoefficient, prData.DivCoeff, 2); {запись коэффициента деления таймера } exOutBytesX(rwBytes3_4, hFFFF, 2); {инициализация счетчика таймера } if exResetTrigger then begin Exclude(prTmpData.Flags, fResetCtrlOnce); Exclude(prData.Flags, fDoInitialResetCtrl); exReset:=TRUE; end else begin Self.SetErrorCode(tErrorCode(ecFailReset)); end; end else begin SetErrorCode(tErrorCode(ecInvalid_Time_DivCoeff_Combination)); end; end; MarkTime; end; end; procedure tCtrl.exDone; begin if IsExDoneDone then Exit; if not CheckExInitDone then Exit; SetNoError; exReset; SetExInitDone(FALSE); SetExDoneDone(NoError); Inherited exDone; end; procedure tCtrl.exDetectCtrl(var x:tCtrlAlive); var c:word; begin c:=prTmpData.BusPtr^.exScanNotFFportsX(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.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[Ord(i)]:=prTmpData.BusPtr^.exInByteX(prData.Ports.Ports[i]); if prTmpData.BusPtr^.Error then begin SetErrorCode(Ord(ecFailReadPortsData)); break; end; end; end; end; if NoError then begin i:=High(i); if not (i in cUnreadablePorts) then begin PD[Ord(i)]:=prTmpData.BusPtr^.exInByte(prData.Ports.Ports[i]); if prTmpData.BusPtr^.Error then begin SetErrorCode(Ord(ecFailReadPortsData)); end; end; 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.PortsNumber:word; begin PortsNumber:=Succ(Ord(High(tPort)))+1; end; function tCtrl.exReadSafePorts(var PortsValues:tPortValuesArray; Count:word):boolean; var i:tPort; begin if NoError and (Count=PortsNumber) then begin for i:=Low(tPort) to High(tPort) do begin if not (i in cUnreadablePorts) then begin PortsValues[Ord(i)]:=prTmpData.BusPtr^.exInByteX(prData.Ports.Ports[i]); if prTmpData.BusPtr^.Error then begin SetErrorCode(Ord(ecFailReadPortsData)); break; end; end; end; end; if NoError then begin i:=High(i); if not (i in cUnreadablePorts) then begin PortsValues[Succ(Ord(i))]:=prTmpData.BusPtr^.exInByte(prData.Ports.Ports[i]); if prTmpData.BusPtr^.Error then begin SetErrorCode(Ord(ecFailReadPortsData)); end; end; end; exReadSafePorts:=NoError; end; END.