unit ADCDevice;

interface

uses
  Windows, Classes, SysUtils, Math, Registry, dialogs;

const
  CHANNEL_COUNT  = 8;
//  GAIN_COUNT     = 1;

type

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------

  TADCDevice = class
  private
    FCriticalSection : TRTLCriticalSection; // Секция для безопасного выполнения команд

    // Список доступных портов
    procedure GetListPorts(var APortBox : TStringList);

    // заблокировать секцию
    procedure Lock;

    // разблокировать секцию
    procedure UnLock;

    procedure PerformanceDelay(s: Cardinal);
  public
    constructor Create;
    destructor Destroy; override;

    // Список подключенных устройств
    procedure DiveceList(AList: TStrings);

    // Поиск устройства
    function DeviceSearch(const AInf : string): boolean;

    function Disconnect: boolean;

    // Инициализация параметров порта
    function Init_ADC: boolean;

    //Проверка соединения DTR-RI.
    function DtrRi: boolean;


    //Чтение ADCS-12-1
    function ReadADC0(out Value : double): boolean;

    //Чтение ADCS-12-8
    function ReadADC1(AChannel: integer; out Value : double): boolean;

  end;


//---------------------------------------------------------------------------
// Ошибки при работе с устройством
//---------------------------------------------------------------------------
//resourcestring
//  SNo = '.';

var
  Device: TADCDevice;

implementation

uses
  Config, Calib;

const
  comapi32_dll = 'comapi32.dll';

//Imported from comapi32.dll
function AccessCOM(P: PChar): boolean; stdcall; external comapi32_dll;
function OpenCOM(P: PChar): boolean; stdcall; external comapi32_dll;
function CloseCOM: boolean; stdcall; external comapi32_dll;
function SetCOM(Baud: DWORD; bSize, Parity, sBits: byte): boolean; stdcall; external comapi32_dll;
function SetModCOM(Baud: DWORD; bSize, Parity, sBits:byte; DTR, RTS: boolean): boolean; stdcall; external comapi32_dll;
function SetCOMTo{SetCOMTimeouts}(RdIvl, RdTotM, RdTotC, WrTotM, WrTotC: DWORD): boolean; stdcall; external comapi32_dll;
function SetCOMBuff{SetCOMBufSize}(RdBuff, WrBuff: DWORD): boolean; stdcall; external comapi32_dll;
function EscFuncCOM{SetModemLns}(F: DWORD): boolean; stdcall; external comapi32_dll;
function GetModem{GetModemLns}(var lpD: DWORD): boolean; stdcall; external comapi32_dll;
function PurgeCOM: boolean; stdcall; external comapi32_dll;
function FlushCOM: boolean; stdcall; external comapi32_dll;
function TxByteCOM(data: byte): boolean; stdcall; external comapi32_dll;
function TxDataCOM(const Buffer; N: DWORD; var lpNDone: DWORD): boolean; stdcall; external comapi32_dll;
function RxDataCOM(var Buffer; N: DWORD; var lpNDone: DWORD): boolean; stdcall; external comapi32_dll;
function ClrErrCOM(var lpE: DWORD): boolean; stdcall; external comapi32_dll;

constructor TADCDevice.Create;
begin
  InitializeCriticalSection(FCriticalSection);

end;

destructor TADCDevice.Destroy;
begin
  Disconnect;
  DeleteCriticalSection(FCriticalSection);
end;

procedure TADCDevice.Lock;
begin
  EnterCriticalSection(FCriticalSection);
end;

procedure TADCDevice.Unlock;
begin
  LeaveCriticalSection(FCriticalSection);
end;

procedure TADCDevice.GetListPorts(var APortBox : TStringList);
var
  i : integer;
begin
  with TRegistry.Create() do
  try
    RootKey := HKEY_LOCAL_MACHINE;
    if OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM', false) then
    begin
      GetValueNames(APortBox);
      for i := 0 to APortBox.Count - 1 do
          APortBox.Strings[i] := ReadString(APortBox.Strings[i]);

      CloseKey;
    end
    else
    begin
      APortBox.Add('COM1');
      APortBox.Add('COM2');
    end;

  finally
    Free;
  end;

end;

// Список устройств
procedure TADCDevice.DiveceList(AList: TStrings);
var
  PortBox : TStringList;
  i : integer;
begin
  AList.Clear;

  PortBox := TStringList.Create;
  try
    GetListPorts(PortBox);

    for i := 0 to PortBox.Count - 1 do
    begin
      try
        if OpenCOM(PChar(PortBox.strings[i])) then
        begin
          Sleep(1);

          if Init_ADC then
              AList.Add('ADCS-12-x');
             //AList.Add(PortBox.strings[i]);

          CloseCOM;
        end;
      except
      end;
    end;

  finally
    PortBox.free;
  end;

end;

// Поиск устройства
function TADCDevice.DeviceSearch(const AInf : string): boolean;
var
  PortBox : TStringList;
  i       : integer;
begin
  Result := false;

//  if FOpened then
//      ClosePort;

  PortBox := TStringList.Create;
  try
    GetListPorts(PortBox);

    for i := 0 to PortBox.Count - 1 do
    begin
      try
        if OpenCom(PChar(PortBox.Strings[i])) then
        begin
          Sleep(1);
          if Init_ADC then
          begin
            result := true;
            exit;
          end;

          CloseCom;
        end;
      except
      end;
    end;

  finally
    PortBox.free;
  end;

end;

function TADCDevice.Disconnect:boolean;
begin
  result := False;
  try
    result := CloseCom;
  except
  end;
end;

// ADCS-12-1
function TADCDevice.ReadADC0(out Value : double): boolean;
var
  Code : integer;
  k, i : integer;
  lpD  : DWORD;
begin
  Lock;
  try
    result := false;

    // 6. Осуществить старт АЦП. Для этого необходимо следующее:
    EscFuncCOM(SETBREAK); // установить линию TXD в состояние "+"
    PerformanceDelay(5); // сформировать временную задержку 1…5 мкс
    EscFuncCOM(CLRBREAK); // установить линию TXD в состояние "-"

    PerformanceDelay(10); //7. Сформировать временную задержку не менее 8 мкс для окончания процесса преобразования АЦП.

    Code := 0;

    // 8. Считать код АЦП.
    for i := 0 to 15 do
    begin
      EscFuncCOM(CLRRTS); // Для считывания разряда выходного кода АЦП сначала необходимо установить линию RTS  в состояние "-"
//      PerformanceDelay(10);

      GetModem(lpD);
      if (lpD and MS_CTS_ON) = 0 then // состояние линии CTS будет отражать значение бита.
          Code := Code or (1 shl (15 - i)); // Заметьте, что этот бит принимает значение, инверсное значению соответствующего бита кода АЦП.

      EscFuncCOM(SETRTS); // установить линию RTS  в состояние "+"
//      PerformanceDelay(10);
    end;

//   code := code and $FFF; //Всего передается 16 разрядов, четыре старших разряда всегда равны нулю.

   if Configuration.General.Diapason[0] = 0 then
       Value := Code * 5 / $fff
   else
       Value := ((Code - $7ff)* 10 / $fff);

   Value := Value * Calibration.Calib.K[0] + Calibration.Calib.S[0];
   Value := RoundTo(Value, (-1) * Configuration.General.Round[0]);

   result := true;
  finally
    UnLock;
  end;

end;


// ADCS-12-8
function TADCDevice.ReadADC1(AChannel: integer; out Value : double): boolean;
var
  Code : integer;
  k, i : integer;
  lpD  : DWORD;
begin
  Lock;
  try
    result := false;

    //6. Считать код АЦП. Для этого необходимо следующее:
    EscFuncCOM(SETDTR); // установить линию DTR в состояние "+"

    Code := 0;

    // Одновременно с чтением кода необходимо записывать в АЦП номер следующего канала.
    if AChannel < 7 then
       k :=((AChannel + 1) shl 3)
    else
       k :=0;

    for i := 0 to 15 do
    begin
      //7. Установить линию TXD в состояние "-" или "+". Записываемое слово представляет номер следующего канала АЦП сдвинутый
      if (k and ($80 shr i)) <> 0 then
          EscFuncCOM(SETBREAK)
      else
          EscFuncCOM(CLRBREAK);

      EscFuncCOM(CLRRTS); // Для считывания разряда выходного кода АЦП сначала необходимо установить линию RTS  в состояние "-"
      //{PerformanceDelay(10);}

      GetModem(lpD);
      if not ((lpD and MS_CTS_ON) = 0) then // состояние линии CTS будет отражать значение бита.
          Code := Code or (1 shl (15 - i));

      EscFuncCOM(SETRTS); // Затем необходимо установить линию RTS  в состояние "+"
     //{PerformanceDelay(10);}  // сформировать временную задержку около 1 мкс.

    end;
    EscFuncCOM(CLRDTR); //8. Установить линию DTR в состояние "-"

//   code := code and $FFF; //Всего передается 16 разрядов, четыре старших разряда всегда равны нулю.

   if Configuration.General.Diapason[AChannel] = 0 then
       Value := Code * 5 / $fff
   else
       Value := ((Code - $7ff)* 20 / $fff);

   Value := Value * Calibration.Calib.K[AChannel] + Calibration.Calib.S[AChannel];
   Value := RoundTo(Value, (-1) * Configuration.General.Round[AChannel]);

   result := true;
  finally
    UnLock;
  end;

end;
(*
unsigned int ADC_Kan(int k)
//Чтение кода АЦП. 4 старших бита д.б. == 0. k - следующий канал
{ int i,cod=0;
asm { cli }
  outportb(Com+4,0x3); //установить DTR в +
  for( i=0,k=(k<<3)&0x38; i<16; i++)
  {
    outportb(Com+3,k & (0x80>>i)? 0x40:0); //линию TXD в + или -
    outportb(Com+4,1); P_delay(T1); //установить SCLK в 1 (RTS в -)
    cod|=((inportb(Com+6)&0x10)!=0? 1:0)<<(15-i); //считать бит по CTS
    outportb(Com+4,3); P_delay(T2); //установить SCLK в 0 (RTS в +)
  }
  outportb(Com+4,0x2); //установить DTR в -
asm { sti }
  return cod;
}
*)

// Инициализация параметров порта
function TADCDevice.Init_ADC: boolean;
var v : double;
begin
  Lock;
  try
    result := False;
    // 19200 - cкорость, 8бит данных, без паритета, 1 стоп-бит
    SetCOM(19200, 8, NOPARITY, ONESTOPBIT);

    if Configuration.General^.ADCS = 0 then
    begin
      EscFuncCOM(CLRBREAK); // 2. Установить линию TXD в состояние "-"
      EscFuncCOM(SETRTS);   // 3. Установить линию RTS  в состояние "+"
      result := DtrRi;      // 4. Продетектировать наличие устройства на порту.
      EscFuncCOM(SETDTR);   // 5. Установить линию DTR  в состояние "+"
    end
    else
    begin
      EscFuncCOM(SETBREAK); // 2. Установить линию TXD в состояние "+"
      EscFuncCOM(SETRTS);   // 3. Установить линию RTS  в состояние "+"
      result := DtrRi;      // 4. Продетектировать наличие устройства на порту.
      EscFuncCOM(CLRDTR);   // 5. Установить линию DTR  в состояние "-"
      if result then
          ReadADC1(7, v);
    end;

  finally
    UnLock;
  end;

end;
(*
int Init_ADC(void) //Инициализация АЦП
{
  T1=3.0*IndexPC; T2=1.0*IndexPC; //Пауза для установления модемных линий (1 мкс)
  outportb(Com+1,0); //запрещаем прерывание
  outportb(Com+3,0x40); //линию TXD в +
  outportb(Com+4,3); //установить RTS в + и DTR в -
  return DtrRi();
}
*)

//Проверка соединения DTR-RI.
function TADCDevice.DtrRi: boolean;
var
  lpD : DWORD;
begin
  Lock;
  try
    result := false;

//    EscFuncCOM(SETBREAK);

    EscFuncCOM(CLRDTR); // установить DTR в -
    //PerformanceDelay(30);
    GetModem(lpD);
    if (lpD and MS_RING_ON) = 0 then
    begin
      EscFuncCOM(SETDTR);  // установить DTR в +
      //PerformanceDelay(30);

      GetModem(lpD);
      if not ((lpD and MS_RING_ON) = 0) then
          result := true
    end

  finally
    UnLock;
  end;

end;
(*
int DtrRi(void) //Проверка соединения DTR-RI. Возвращает: 0 - Ok
{ int otv=1;
  outportb(Com+3,0x40); //линию TXD в +     // TXD(BREAK) - используется для выдачи последовательных данных
  outportb(Com+4,0x2); P_delay(T1); //установить DTR в -
  if((inportb(Com+6) & 0x40) == 0) otv=0; //считать DTR по RI
  outportb(Com+4,0x3); P_delay(T1); //установить DTR в +
  if(!otv) if((inportb(Com+6) & 0x40) != 0) otv=0; else otv=1;
  return otv;
}
*)

procedure TADCDevice.PerformanceDelay(s: Cardinal);
var
  Freq, hrT1, hrT2: Int64;
begin
  QueryPerformanceFrequency(Freq);
  QueryPerformanceCounter(hrT1);
  repeat
    QueryPerformanceCounter(hrT2);
  until ((hrT2 - hrT1) * 1000000 div Freq) > s;

end;

end.

