{ Контроллер напряжений 5.105.198 (АК-5) } {--------------------------------------------------------------------------- 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 ----------------------------------------------------------------------------} {$X+} Unit c_Volts; INTERFACE USES MITypes, c_Ctrl, c_Ctrl1, c_Bus; const cDefaultTimeOut=1000; { [мс]. } cDefaultRetryCount=10; { [раз]. Число повторов ожидания стабильности данных измерения} cDefaultRetryDelay=200; { [мс]. Период ожидания стабильности данных измерения} type { Коды ошибок } tErrorCodes=({$I C_ErrCds.Inc}, ecFailAssignChannel, ecFailStartMeasurement, ecFailFlags, ecFailGetData, ecFailExInit, ecBadControllerState); tDigit=1..4; tDigits=set of tDigit; const cDefaultRetryMask=[1..4]; { маска десятичных цифр результата на которых проверяется стабильность, разряды нумеруются: 1234 } type tPort=( owStrobe, owChannel, roFlags, roVoltageDataHi, roVoltageDataLo ); tPorts=record case byte of 0:(owStrobe, owChannel, roFlags, roVoltageDataHi, roVoltageDataLo:word; ); 1:(Ports:array[tPort] of word); end; const cUnreadablePorts=[]; type tControlFlag=(cfFastExInit); tControlFlags=set of tControlFlag; tFlag=(fChannelChanged); tFlags=set of tFlag; tChlData=record RetryDelay:word; RetryCount:word; Mask:tDigits; LastRetryCount:word; LastData:tMkVolts; end; tChlDataArray=array[tVoltageChannel] of tChlData; tMeasurementData=record Data:word; Flags:byte; end; tData=record Ports:tPorts; Flags:tFlags; ControlFlags:tControlFlags; Channel:tVoltageChannel; Data:tChlDataArray; end; tTmpData=record BusPtr:c_Bus.tCtrlPtr; 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; procedure exInit; virtual; function ControlState(f:tControlFlag):boolean; procedure ControlSet(f:tControlFlag); procedure ControlClear(f:tControlFlag); procedure ControlsSet(f:tControlFlags); procedure ControlsGet(var f:tControlFlags); { Сохранение/восстановление состояния контроллера } 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 Channel(x:tVoltageChannel); procedure ChannelChanged; procedure RetryCount(x:word); procedure RetryDelay(x:word); procedure RetryMask(x:tDigits); function CurChannel:tVoltageChannel; function CurRetryCount:word; function CurRetryDelay:word; procedure CurRetryMask(x:tDigits); procedure SetNoError; virtual; procedure SetErrorCode(ec:tErrorCode); function ErrorMessage(en:tErrorCode):string; virtual; function exCurVoltage:tMkVolts; { 10е-6 Вольт или микроВольты } function exCurVoltageFast:tMkVolts; { 10е-6 Вольт или микроВольты } function exVoltage(x:tVoltageChannel):tMkVolts; { 10е-6 Вольт или микроВольты } function exVoltageOnce(x:tVoltageChannel):tMkVolts; { 10е-6 Вольт или микроВольты } procedure exSelfTest; procedure exDetectCtrl(var x:tCtrlAlive); virtual; private prData:tData; prTmpData:tTmpData; function CurMask:word; function Data2Voltage(X:tMeasurementData):tMkVolts; procedure exWaitReady; procedure exSelectChannel; procedure exStart; procedure exGetDataX(var X:tMeasurementData); function exFlags:Byte; function exGetData:word; { просто считывает магистраль данных цифры разрядов (двоично-десятичные значения) помещаются в тетрады слова (порции по 4-е бита) в порядке старшинства: первая цифра -> младшая тетрада Word ---------------------------------------------------- |бит 15| бит 14| | | | | | | | | | | | |бит 1|бит 0| ---------------------------------------------------- | старший разряд | | |младший разряд | | индикатора | | |индикатора | | Значение | | | Значение | | от 0 до 9 | | | от 0 до 9 | ----------------------------------------------------} function exGetVoltage:tMkVolts; { считывает магистраль данных, преобразует в напряжение со знаком и учетом диапазона } function exReadData:tMkVolts; { считывает магистраль данных с ожиданием стабилизации, преобразует даные в напряжение со знаком и учетом диапазона } { Вспомогательные функции определния наличия питания, см. exIsPowerON} function PortsNumber:word; virtual; function exReadSafePorts(var PortsValues:tPortValuesArray; Count:word):boolean; virtual; end; IMPLEMENTATION USES DataSave, MiscFunc; type tRange=(r100mV, r1V, r10V, r100V); tRanges=array[tRange] of tMkVolts; {в микроВольтах} const cCtrlAlive=3; { верно для контр. развертки } cReadyMask =(1 shl 3); cReadyPatt =(1 shl 3); cBusyPatt = 0; cNegativePatt=(1 shl 2); cLimitMask = 1+(1 shl 1); cCtrlDefaultPorts:tPorts=( owStrobe: $C7; owChannel: $C8; roFlags: $CF; roVoltageDataHi:$CD; roVoltageDataLo:$CE ); cRanges:tRanges=(10, 100, 1000, 10000); { * Функции --------------------------------- } function Polarity(b:Byte):ShortInt; {$IfDef Delphi} register; {$EndIf} assembler; asm mov dl,b; xor ax,ax; inc al and dl,cNegativePatt; jz @Pos neg al @Pos: end; { * Методы ----------------------------------- } constructor tCtrl.Init(var Bus:c_Bus.tCtrl; Ports:tPorts); var c:tVoltageChannel; begin Inherited Init; prTmpData.BusPtr:=@Bus; prData.Ports:=Ports; prData.Flags:=[]; prData.ControlFlags:=[]; TimeOut(cDefaultTimeOut); for c:=Lens downto tVoltageChannel(0) do begin Channel(c); RetryCount(cDefaultRetryCount); RetryDelay(cDefaultRetryDelay); RetryMask(cDefaultRetryMask); end; If Bus.Error and NoError then begin SetErrorCode(tErrorCode(ecBadBus)); end; end; constructor tCtrl.InitDefault(var Bus:c_Bus.tCtrl); begin Init(Bus, cCtrlDefaultPorts); end; destructor tCtrl.Done; begin if NotInitialized then Exit; Inherited Done; prData.Flags:=[]; end; function tCtrl.Name; begin Name:='Volts'; end; function tCtrl.ControlState; begin ControlState:=f in prData.ControlFlags; end; procedure tCtrl.ControlsSet; begin prData.ControlFlags:=f; end; procedure tCtrl.ControlsGet; begin f:=prData.ControlFlags; end; procedure tCtrl.ControlSet; begin Include(prData.ControlFlags,f); end; procedure tCtrl.ControlClear; begin Exclude(prData.ControlFlags,f); end; procedure tCtrl.exSelfTest; var cl:tVoltageChannel; x,x1:tMkVolts; const CCheckChannels=[IMCh, Acceleration, Magnet, Mutiplicator, AntiDinatron, BaseUPT]; begin x1:=exVoltage(Low(cl)); for cl:=Succ(Low(cl)) to High(cl) do begin if (cl in CCheckChannels) then begin x:=exVoltage(cl); if Error then x1:=x; if NoError and (x=x1) then SetErrorCode(Ord(ecFailExInit)); x1:=x; end; end; end; procedure tCtrl.exInit; begin Inherited exInit; if not (cfFastExInit in prData.ControlFlags) then begin Channel(Low(tVoltageChannel)); exCurVoltageFast; end; SetExInitDone(NoError); end; function tCtrl.Restore(var DataPtr:pointer):boolean; begin if Inherited Restore(DataPtr) then Restore:=RestoreDataEx(DataPtr, prData, SizeOf(prData)) else Restore:=FALSE; Include(prData.Flags, fChannelChanged); 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.Channel; begin // if prData.Channel<>x then begin prData.Channel:=x; ChannelChanged; // end; end; procedure tCtrl.RetryCount(x:word); begin prData.Data[prData.Channel].RetryCount:=x; end; procedure tCtrl.RetryDelay(x:word); begin prData.Data[prData.Channel].RetryDelay:=x; end; procedure tCtrl.RetryMask(x:tDigits); begin prData.Data[prData.Channel].Mask:=x; end; {------------------------------------------------------------------------} { Чтение текущих значений параметров (без ввода/вывода в порты)} function tCtrl.CurChannel; begin CurChannel:=prData.Channel; end; function tCtrl.CurRetryCount:word; begin CurRetryCount:=prData.Data[prData.Channel].RetryCount; end; function tCtrl.CurRetryDelay:word; begin CurRetryDelay:=prData.Data[prData.Channel].RetryDelay; end; procedure tCtrl.CurRetryMask(x:tDigits); begin x:=prData.Data[prData.Channel].Mask; end; {------------------------------------------------------------------------} { Вспомогательные функции (без ввода/вывода в порты)} procedure tCtrl.SetNoError; begin prTmpData.BusPtr^.SetNoError; Inherited SetNoError; end; procedure tCtrl.SetErrorCode(ec:tErrorCode); begin Inherited SetErrorCode(ec); if ec<>0 then Include(prData.Flags, fChannelChanged); end; function tCtrl.ErrorMessage(en:tErrorCode):string; begin case tErrorCodes(en) of ecFailAssignChannel: ErrorMessage:='Ошибка назначения канала измерения'; ecFailStartMeasurement: ErrorMessage:='Не удалось запустить измерение'; ecFailFlags: ErrorMessage:='Ошибка чтения флагов состояния'; ecFailGetData: ErrorMessage:='Ошибка чтения данных'; ecFailExInit: ErrorMessage:='Не удалось провести инициализацию'; ecBadControllerState: ErrorMessage:='Ошибочное состояние контроллера'; else ErrorMessage:=Inherited ErrorMessage(en)+' (измерение напряжений)'; end; end; procedure tCtrl.exStart; begin CheckExInitDone; if NoError then begin prTmpData.BusPtr^.exOutByte(prData.Ports.owStrobe, 0); end; end; function tCtrl.exFlags:Byte; var x:TMeasurementData; begin exGetDataX(x); exFlags:=x.Flags; end; function tCtrl.exCurVoltage:tMkVolts; begin CheckExInitDone; // Exclude(prFlags,fChannelChanged); exSelectChannel; exStart; exCurVoltage:=exReadData; end; function tCtrl.exVoltage; begin CheckExInitDone; Channel(x); exVoltage:=exCurVoltage; end; function tCtrl.exVoltageOnce; var y:tVoltageChannel; begin CheckExInitDone; y:=CurChannel; Channel(x); exVoltageOnce:=exCurVoltage; Channel(y); exSelectChannel; end; function tCtrl.exCurVoltageFast:tMkVolts; begin CheckExInitDone; exCurVoltageFast:=exGetVoltage; end; { конвертирует маску tDigits в двоичную маску } function Digits2Word(x:tDigits):word; {$IfDef Seg16} assembler; asm mov dl,x; shr dl,1 { DL:=маска !!! не с нуля [1..4]} mov bx,$0F; mov cx,4; xor ax,ax @loop: shr dl,1; jnc @NoBit or ax,bx @NoBit: shl bx,4 loop @loop end; {$Else IfDef Seg16} assembler; asm push bx mov dl,x; shr dl,1 { DL:=маска !!! не с нуля [1..4]} xor ecx,ecx; mov cx,4; mov bx,$0F; xor ax,ax @loop: shr dl,1; jnc @NoBit or ax,bx @NoBit: shl bx,4 loop @loop pop bx end; {$EndIf Def Seg16} function tCtrl.CurMask:word; begin CurMask:=Digits2Word(prData.Data[CurChannel].Mask); 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; { * PRIVATE секция } procedure tCtrl.exWaitReady; begin if NoError then begin If prTmpData.BusPtr^.exWait(prData.Ports.roFlags, cReadyMask, cReadyPatt, CurTimeOut)<>0 then begin SetErrorCode(tErrorCode(ecTimeOut)); end; end; end; function tCtrl.exReadData:tMkVolts; var w0,w:word; x:TMeasurementData; Mask:word; var Count:word; d:tMkVolts; begin exReadData:=0; if NoError then begin Count:=CurRetryCount; exGetDataX(x); if Count>1 then begin Mask:=CurMask; w:=x.Data AND Mask; repeat w0:=w; exStart; prTmpData.BusPtr^.Delay(CurRetryDelay); exGetDataX(x); w:=x.Data AND Mask; Dec(count); until (Count=0) or (w=w0); end; d:=Data2Voltage(x); exReadData:=d; with prData.Data[CurChannel] do begin LastRetryCount:=Count; LastData:=d; end; end; end; procedure tCtrl.exGetDataX; begin X.Data:=0; X.Flags:=0; exSelectChannel; exWaitReady; if NoError then begin X.Flags:=prTmpData.BusPtr^.exInByte(prData.Ports.roFlags); if prTmpData.BusPtr^.Error then begin SetErrorCode(tErrorCode(ecFailFlags)); Exit; end; X.Data:=exGetData; If prTmpData.BusPtr^.Error then begin SetErrorCode(tErrorCode(ecFailGetData)); end; end; end; function tCtrl.exGetData:word; { просто считывает магистраль данных } type tDB=record bl,bh:byte end; var w:word; begin tDB(w).bl:=prTmpData.BusPtr^.exInByte(prData.Ports.roVoltageDataLo); tDB(w).bh:=prTmpData.BusPtr^.exInByte(prData.Ports.roVoltageDataHi); If prTmpData.BusPtr^.Error then begin SetErrorCode(tErrorCode(ecFailGetData)); exGetData:=0; end else begin exGetData:=w; end; end; function tCtrl.Data2Voltage; begin Data2Voltage:=Polarity(X.Flags)*cRanges[tRange(X.Flags and cLimitMask)]*WordBdc2Bin(X.Data); end; function tCtrl.exGetVoltage:tMkVolts; var x:TMeasurementData; begin exGetVoltage:=0; if NoError then begin exGetDataX(x); exGetVoltage:=Data2Voltage(x); end; end; procedure tCtrl.exSelectChannel; begin if not (fChannelChanged in prData.Flags) then Exit; if NoError then begin prTmpData.BusPtr^.exOutByte(prData.Ports.owChannel, Byte(prData.Channel)); prTmpData.BusPtr^.exOutByte(prData.Ports.owChannel, Byte(prData.Channel)); // prTmpData.BusPtr^.exOutByte(prData.Ports.owStrobe, $FF); if prTmpData.BusPtr^.NoError then begin Exclude(prData.Flags,fChannelChanged); end else begin SetErrorCode(tErrorCode(ecFailAssignChannel)); end; end; end; procedure tCtrl.ChannelChanged; begin Include(prData.Flags,fChannelChanged); 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.Ports[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; END.