{ Контроллер счетчика ионов (AК8) 5.105.182 } unit MI1201AGM_IonCounter_Emulator; interface USES {$IfNDef Seg16}SysUtils,{$EndIf} TimeInterval_Emulator, Peak_Emulator, MI1201AGM_Hardware_Emulator, MI1201AGM_Controller_Emulator; const cDataSignature='Ion Counter emulator data. '^M^L; cRegistrySubPath='IonCounter'; cValidPorts=[$20..$27]; cValidReadPorts=cValidPorts-[$20,$24..$26]; cValidWritePorts=cValidPorts; cBasePort=$100; type tDataSignature=array[1..Length(cDataSignature)] of char; tFlag=(fTriggerReset, fGate, fReadyToStart, fTimetCtrlWordOK, fMeasureComplited); tFlags=set of tFlag; tCtrlByteCounter=0..1; tCtrlPortCounter=0..2; tDataByteCounter=0..3; tDataWordCounter=0..1; tCtrlDataByteCounter=0..2; tTimeByteCounter=0..1; tCounterByteArray=array[tDataByteCounter] of Byte; tCounterWordArray=array[tDataWordCounter] of Word; tIonCounterSignal=record case byte of 0:(DWord:{$IfDef Seg16}0..High(longint){$else}cardinal{$EndIf};); 1:(ByteArray:tCounterByteArray); 2:(WordArray:tCounterWordArray); end; tError=( eNotReadyToStart, eInvalidTimetCtrlWord, eInvalidPort, eReadFromInvalidPort, eWriteToInvalidPort ); resourcestring rseNotReadyToStart='не готов к старту'; rseInvalidTimetCtrlWord='неверное слово управления таймера'; rseInvalidPort='неверный порт'; rseReadFromInvalidPort='чтение из неверного порта'; rseWriteToInvalidPort='запись в неверный порт'; const cErrorDescriptions:array[tError] of string=( rseNotReadyToStart, rseInvalidTimetCtrlWord, rseInvalidPort, rseReadFromInvalidPort, rseWriteToInvalidPort ); type tWord=record case byte of 0:(w:word;); 1:(b:array[tCtrlByteCounter] of byte); end; tByteCounter=0..1; tCounterArray=array[tDataWordCounter] of tByteCounter; tCounter=record Read, Write:tCounterArray; end; tTimerData=record Counter:tCtrlByteCounter; case byte of 0:(Time:tWord); 1:(BB:array[tTimeByteCounter] of byte); end; const cCounter0:tCounter=(Read:(0,0); Write:(0,0)); type tCounterData=record Counter:tCounter; case byte of 0:(WW:array[tDataWordCounter] of tWord); 1:(BB:array[tDataByteCounter] of byte); end; tCtrlData=record Counter:tCtrlDataByteCounter; case byte of 0:(BB:array[tCtrlDataByteCounter] of byte); 1:(WW:array[tDataWordCounter] of tWord); 2:(DW:cardinal); end; tSignalBytes=array[tDataByteCounter] of Byte; // Данные, сохраняющиеся между измерениями tData=record Signature:tDataSignature; Flags:tFlags; CtrlWord:tCtrlData; DataWord:tCounterData; countVI53:byte; // Time:tTime_MS; TimerDivideCoefficient:tTimerData; end; // Данные, НЕсохраняющиеся между измерениями tTmpData=record Signal:tIonCounterSignal; end; // Исключительная EIonCounter=class(EController) function ControllerName:string; override; end; tIonCounter=class(tController) private prData:tData; prTmpData:tTmpData; prTimer:tTimerMI1201X; procedure InitChannel; procedure Start; function TriggerReset:byte; procedure RecountVI53; function Gate:byte; function CheckCtrlWord:boolean; procedure OutCtrlWord(b:byte); procedure OutTimerTime(b:byte); procedure OutTimerDivideCoeff(b:byte); function CheckDataWord:boolean; procedure OutDataWord(b:byte); function DataByteGet(n:tDataByteCounter):byte; function DataByte34Get:byte; function ReadyToStart:boolean; procedure OutTimerCtrlWord(b:byte); function InProgress:byte; procedure GetSignal; function Time:tTime_MS; function TimeSpend:tTime_MS; protected // обязательные процедуры function SubPath:string; override; procedure prOutByte(b:Byte; port:word); override; function prInByte(port:word):Byte; override; function CheckData(Data:pointer):boolean; override; function DataSize:integer; override; function SetData(aData:pointer):boolean; override; function GetData(aData:pointer):boolean; override; // прочие процедуры public // обязательные процедуры constructor Create(Hardware:tMi1201HardwareEmulator); destructor Destroy; override; procedure ReInit; override; procedure SetDefaultData; override; function ValidPort(APort:word):boolean; function ValidReadPort(APort:word):boolean; function ValidWritePort(APort:word):boolean; // прочие процедуры end; implementation Uses Emulator_MiscFuncs; function tIonCounter.SetData(aData:pointer):boolean; begin Result:=Inherited SetData(aData); if Result then begin Inc(Cardinal(aData),Inherited DataSize); Result:=CheckData(aData); if Result then begin Move(aData^,prData,DataSize); end; end; end; function tIonCounter.GetData(aData:pointer):boolean; begin Result:=Inherited GetData(aData); if Result then begin Inc(Cardinal(aData),Inherited DataSize); Result:=CheckData(aData); if Result then begin Move(prData,aData^,SizeOf(prData)); end; end; end; function tIonCounter.DataSize:integer; begin Result:=Inherited DataSize + SizeOf(prData); end; function tIonCounter.SubPath:string; begin Result:=cRegistrySubPath; end; function tIonCounter.CheckData(Data:pointer):boolean; begin Result:=Inherited CheckData(Data) and TRUE; end; constructor tIonCounter.Create(Hardware:tMi1201HardwareEmulator); begin prTimer:=tTimerMI1201X.Create; Inherited; end; destructor tIonCounter.Destroy; begin Inherited; prTimer.Free; end; procedure tIonCounter.ReInit; begin end; procedure tIonCounter.SetDefaultData; begin prData.Signature:=cDataSignature; prData.Flags:=[]; prTimer.Clear; prData.TimerDivideCoefficient.Counter:=0; prData.TimerDivideCoefficient.Time.W:=0; prData.countVI53:=0; end; procedure tIonCounter.InitChannel; begin prData.CtrlWord.WW[0].W:=0; prData.CtrlWord.WW[1].W:=0; prData.CtrlWord.Counter:=0; prData.DataWord.WW[0].W:=0; prData.DataWord.WW[1].W:=0; prData.DataWord.Counter:=cCounter0; end; function tIonCounter.CheckCtrlWord:boolean; const cValidCtrlWord:tCtrlData=(BB:($34,$74,$B2)); begin Result:=(prData.CtrlWord.DW=cValidCtrlWord.DW); end; procedure OutToByteArray(b:byte; var counter:byte; var BytaArray:array of byte); begin if counter>High(BytaArray) then Exit; BytaArray[counter]:=b; if counter=High(BytaArray) then counter:=0 else Inc(counter); end; procedure tIonCounter.OutTimerTime(b:byte); begin prTimer.OutTime(b); end; procedure tIonCounter.OutTimerDivideCoeff(b:byte); begin OutToByteArray(b,Byte(prData.TimerDivideCoefficient.Counter),prData.TimerDivideCoefficient.bb); end; function tIonCounter.CheckDataWord:boolean; begin Result:=(prData.DataWord.WW[0].w=$FFFF) and (prData.DataWord.WW[1].w=$FFFF) and (prData.countVI53=2); end; procedure tIonCounter.OutCtrlWord(b:byte); begin OutToByteArray(b,Byte(prData.CtrlWord.Counter),prData.CtrlWord.BB); end; procedure tIonCounter.OutDataWord(b:byte); begin OutToByteArray(b,Byte(prData.DataWord.Counter.Write[1]),prData.DataWord.WW[1].b); end; function tIonCounter.Time:tTime_MS; begin Result:=(tTime_MS(prTimer.Time)*prData.TimerDivideCoefficient.Time.W) div 1000 end; function tIonCounter.TimeSpend:tTime_MS; begin Result:=prTimer.Elapsed; end; procedure tIonCounter.GetSignal; var x:double; begin x:=Hardware.IonCounterSignal; x:=x*TimeSpend; if x<=High(prTmpData.Signal.DWORD) then begin prTmpData.Signal.DWORD:=Round(x); end else begin prTmpData.Signal.DWORD:=High(prTmpData.Signal.DWORD); end; prTmpData.Signal.WordArray[1]:= not prTmpData.Signal.WordArray[1]; end; function tIonCounter.DataByteGet(n:tDataByteCounter):byte; begin if not (fMeasureComplited in prData.Flags) then begin GetSignal; InProgress; end; DataByteGet:=prTmpData.Signal.ByteArray[n]; end; function tIonCounter.DataByte34Get:byte; begin DataByte34Get:=DataByteGet(2+prData.DataWord.Counter.Read[1]); CyclicInc(Byte(prData.DataWord.Counter.Read[1]),High(prData.DataWord.Counter.Read[1])); end; function tIonCounter.TriggerReset:byte; begin Include(prData.Flags,fTriggerReset); Exclude(prData.Flags,fGate); Exclude(prData.Flags,fReadyToStart); prData.countVI53:=0; prData.DataWord.Counter.Read[0]:=0; prData.DataWord.Counter.Read[1]:=0; TriggerReset:=$0; end; function tIonCounter.Gate:byte; begin Include(prData.Flags,fGate); prData.DataWord.WW[0].W:=$FFFF; Gate:=$0; end; procedure tIonCounter.RecountVI53; begin if prData.countVI53=2 then prData.countVI53:=0 else Inc(prData.countVI53); end; function tIonCounter.ReadyToStart:boolean; begin if (fReadyToStart in prData.Flags) then begin ReadyToStart:=TRUE; end else if (fTriggerReset in prData.Flags) and (fGate in prData.Flags) //and (fTimetCtrlWordOK in prData.Flags) and prTimer.ReadyToStart and (CheckCtrlWord) and (CheckDataWord) then begin Include(prData.Flags,fReadyToStart); ReadyToStart:=TRUE; end else begin ReadyToStart:=FALSE; end; end; procedure tIonCounter.OutTimerCtrlWord(b:byte); begin if b=$32 then begin Include(prData.Flags,fTimetCtrlWordOK); end else begin Exclude(prData.Flags,fTimetCtrlWordOK); ErrorCode:=Ord(eInvalidTimetCtrlWord); // raise EIonCounter.CreateFmt('неверный вывод в порт таймера b=%u',[b]); end; end; procedure tIonCounter.Start; begin if ReadyToStart then begin prTimer.Start; Exclude(prData.Flags,fMeasureComplited); end else begin ErrorCode:=Ord(eNotReadyToStart); // raise EIonCounter.Create('не готов к старту.'); end; end; function tIonCounter.InProgress:byte; begin if prTimer.Expired then begin InProgress:=0; GetSignal; Include(prData.Flags,fMeasureComplited); end else InProgress:=1; end; procedure tIonCounter.prOutByte(b:Byte; port:word); begin case port of $120: begin { Сброс триггера } TriggerReset; end; $121: begin { досчет ВИ53 } RecountVI53; end; $122: begin { Gate + сброс 1 и 2 байтов счетчика } Gate; end; $123: begin { пуск счета } Start; end; $124: begin { Запись управляющего слова } OutCtrlWord(b); end; $125: begin { Запись времени интегрирования } OutTimerTime(b); end; $126: begin { Запись коэффициента деления } OutTimerDivideCoeff(b); end; $127: begin { Запись 3 и 4 байтов счетчика } OutDataWord(b); end; else if port in cValidReadPorts then begin WriteToInvalidPort:=TRUE; ErrorCode:=Ord(eWriteToInvalidPort); end else begin InvalidPort:=TRUE; ErrorCode:=Ord(eInvalidPort); end; end; end; function tIonCounter.prInByte(port:word):Byte; begin Result:=$FF; case port of $121: begin { Чтение готовности. 0-й бит: "0"- готов "1"- не готов} Result:=InProgress; end; $122: begin { Чтение 2-го байта счетчика } Result:=DataByteGet(1); end; $123: begin { Чтение 1-го байта счетчика } Result:=DataByteGet(0); end; $127: begin { Чтение 3 и 4 байтов счетчика } Result:=DataByte34Get; end; else if port in cValidWritePorts then begin ReadFromInvalidPort:=TRUE; ErrorCode:=Ord(eReadFromInvalidPort); end else begin InvalidPort:=TRUE; ErrorCode:=Ord(eInvalidPort); end; end; if not (fTriggerReset in prData.Flags) then Result:=$FF; end; function tIonCounter.ValidPort(APort:word):boolean; begin Result:= ((APort and $FF00)=cBasePort) and ((APort and $FF) in cValidPorts); end; function tIonCounter.ValidReadPort(APort:word):boolean; begin Result:= ((APort and $FF00)=cBasePort) and ((APort and $FF) in cValidReadPorts); end; function tIonCounter.ValidWritePort(APort:word):boolean; begin Result:= ((APort and $FF00)=cBasePort) and((APort and $FF) in cValidWritePorts); end; function EIonCounter.ControllerName:string; resourcestring cIonCounterName='Счетчик ионов (AК8)'; begin Result:=cIonCounterName; end; end. { Контроллер счетчика ионов (AК8) 5.105.182 г========T================T=====================================¬ ¦ Адрес ¦ ПОРТ ¦ ¦ ¦ порта +-------T--------+ Назначение ¦ ¦ ¦чтение ¦запись ¦ ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $121 ¦ + ¦ + ¦ Чтение готовности, ¦ ¦ ¦ ¦ ¦ 0-й бит: "0"- готов "1"- не готов ¦ ¦ ¦ ¦ ¦ Запись - досчет ВИ53 ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $122 ¦ + ¦ + ¦ Чтение 2-го байта счетчика ¦ ¦ ¦ ¦ ¦ Запись - Gate + сброс 1 и 2 байтов ¦ ¦ ¦ ¦ ¦ счетчика ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $123 ¦ + ¦ + ¦ Чтение 1-го байта счетчика ¦ ¦ ¦ ¦ ¦ Запись - пуск счета ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $120 ¦ - ¦ + ¦ Сброс триггеров ¦ ¦ ¦ ¦ ¦ ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ Порты таймера ¦ ¦--------T-------T--------T-------------------------------------¦ ¦ $124 ¦ - ¦ + ¦ Запись управляющего слова: ¦ ¦ ¦ ¦ ¦ $34 - для 3 и 4 байтов счетчика ¦ ¦ ¦ ¦ ¦ $74 - для делителя ¦ ¦ ¦ ¦ ¦ $B2 - для таймера ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $125 ¦ - ¦ + ¦ Запись времени интегрирования ¦ ¦ ¦ ¦ ¦ в mS (если К= 1000) ¦ ¦ ¦ ¦ ¦ ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $126 ¦ - ¦ + ¦ Запись коэффициента деления ¦ ¦ ¦ ¦ ¦ К=1000 ($3E8) ¦ ¦--------+-------+--------+-------------------------------------¦ ¦ $127 ¦ + ¦ + ¦ Запись/Чтение 3 и 4 байтов ¦ ¦ ¦ ¦ ¦ счетчика ¦ L========¦=======¦========¦=====================================- Порядок работы: 1. Инициализация. - записать управляющие слова в $124 = $34,$74,$B2; - записать коэффициент деления в $126 = 1000 mod 256 и 1000 div 256 - записать время интегрирования в mS в $125 = mS mod 256 и ms div 256 - записать $FF два раза в $127 - сбросить триггера $120 2. Пуск счета и снятие информации. - установить Gate в "1" - запись в $122 = 0; - досчитать два раза $121 = 0 ; - пуск счета $123 = 0; - ожидать окончания счета читать $121 если 0-й бит = "1" - счет продолжается, = "0" - счет закончен; - считать информацию с регистров: 1-й байт - B1=$123 2-й - B2=$122 3-й - B3=not $127 4-й - B4=not $127 результат N = B1+(B2+(B3+B4*256)*256)*256 }