unit Labpc;
{ =================================================================
  WinWCP -National Instruments Interface Library V1.1
  (c) John Dempster, University of Strathclyde, All Rights Reserved
  12/2/97 V1.1 Dynamic DLL loading now used.
  5/3/98 V1.2 DMA support can now be disabled
  2/4/98 V1.2a Set_DAQ_Device_Info now only called (by NIADCToMemory)
         when required
  31/5/98 V1.2b Disable DMA code added to MemoryToDAC to avoid -10609
          error when MemoryToDAC called before ADCToMemory
  =================================================================}

interface

uses WinTypes,Dialogs, SysUtils, Global, Shared, WinProcs, maths ;

  procedure NI_LoadNIDAQLibrary  ;
  procedure NI_GetADCVoltageRangeOptions( var RangeOptions : array of TADCRange ;
                                       var NumOptions : Integer) ;
  procedure NI_ADCToMemory( var ADCBuf : array of integer ;
                           nChannels : LongInt ;
                           nSamples : LongInt ;
                           var dt : Single ;
                           ADCVoltageRange : Single ;
                           WaitForExtTrigger : Boolean ;
                           CircularBuffer : Boolean  ) ;
  procedure NI_MemoryToDAC( var DACBuf : array of integer ;
                           nChannels : LongInt ;
                           nPoints : LongInt ;
                           dt : Single ;
                           NumRepeats : LongInt ;
                           var DACActive : Boolean ) ;
  procedure NI_StopDAC ;
  procedure NI_WriteDACs( DACVolts : array of Single ; nChannels : LongInt ) ;
  procedure NI_CheckDACProgress( var NumPointsDone, NumIterationsDone : LongInt ;
                              var Active : Boolean ) ;
  procedure NI_StopADC ;
  procedure NI_GetLabInterfaceInfo( var Supplier, Model : String ) ;
  procedure NI_GetSamplingIntervalRange( var MinInterval,MaxInterval : single ) ;
  function NI_ReadADC( Channel : Integer ; ADCVoltageRange : Single ) : Integer ;

  procedure NI_CheckError( Err : Integer ) ;
  procedure NI_CheckSamplingInterval( var SamplingInterval : double ;
                                      var TimeBase : Integer ;
                                      var ClockTicks : Word )  ;
  procedure NI_MemoryToDigitalPort( var DigBuf : array of Integer ; nBytes : LongInt ) ;
  procedure NI_StopDIG( Pattern : LongInt ) ;
  procedure NI_WriteToDigitalOutPutPort( Pattern : LongInt ) ;
  function NI_GetMaxDACVolts : single ;
  procedure NI_GetChannelOffsets( var Offsets : Array of LongInt ; NumChannels : LongInt ) ;
  procedure NI_ReportFailure( const ProcName : string ) ;
  function NI_IsLabInterfaceAvailable : boolean ;
  procedure NI_ConfigureHardware( DisableDMA : Boolean ) ;
implementation

uses labio ;

const
     Device = 1 ;
     ND_INTERRUPTS                 	 = 19600;
     ND_DATA_XFER_MODE_AI          	 = 14000;
type
    { NIDAQ.DLL procedure NI_ definitions }
    TInit_DA_Brds = function(device : Integer; var brdCode : Integer) : integer;
    TDAQ_Clear =  function(device : Integer) : integer ;
    TDAQ_Config = function(device, startTrig, extConv : Integer) : integer ;
    TDAQ_DB_Config = function(device, dbMode : Integer) : integer;
    TDAQ_Start = function(device, chan, gain : Integer; buffer : Pointer;
                           cnt : Longint; timebase : Integer; sampInt : Word) : integer;
    TDAQ_Rate = function(rate : Double; units : Integer; var timebase : Integer;
                          var sampleInt : Word) : integer;
    TLab_ISCAN_Start = function(device, numChans, gain : Integer; buffer : Pointer;
                                 cnt : Longint; timebase : Integer;
                                 sampleInt, scanInt : Word) : integer;
    TSCAN_Setup  = function(device, num_chans : Integer; chans, gains : PInteger) : integer;
    TSCAN_Start  = function(device : Integer; buffer : Pointer; cnt : Longint;
                             tb1 : Integer; si1 : Word; tb2 : Integer; si2 : Word) : integer;
    TWFM_Rate = function(rate : Double; units : Integer; var timebase : Integer;
                          var updateInterval : Longint) : integer;
    TWFM_ClockRate = function(device, group, whickClock, timebase : Integer;
                               updateInterval : Longint; mode : Integer) : integer;
    TWFM_Load = function(device, numChans : Integer; chanVect : PInteger;
                          buffer : Pointer; count, iterations :
                          Longint; mode : Integer) : integer;
    TWFM_Group_Control = function(device, group, operation : Integer) : integer;
    TWFM_Check = function(device, channel : Integer; var status : Integer;
                           var pointsDone, itersDone : Longint) : integer;
    TAO_Configure = function(device, chan, outputPolarity, intOrExtRef : Integer;
                              refVoltage : Double; updateMode : Integer) : integer;
    TAO_Write  = function(device, chan, value : Integer) : integer;
    TAO_Update  = function(device : Integer) : integer;
    TAO_VScale = function(device, chan : Integer; voltage : Double;
                           var value : Integer) : integer;
    TDIG_Block_Clear = function(device, grp : Integer) : integer;
    TDIG_Block_Out = function(device, grp : Integer; buffer : Pointer;
                                       cnt : Longint) : integer;
    TDIG_SCAN_Setup = function(device, grp, numPorts : Integer; portList : PInteger;
                                direction : Integer) : integer;
    TDIG_Prt_Config  = function(device, port, latch_mode,
                                 direction : Integer) : integer;
    TDIG_Out_Port  = function (device, port, pattern : Integer) : integer;
    TAI_Read = function (device, chan, gain : Integer; var value : Integer) : integer;
    TSet_DAQ_Device_Info = function (device : Integer;infoType,infoVal : LongInt ) : integer ;

var
   DeviceNumber : Integer ;     { Lab. interface board in use }
   ADCVoltageRangeMax : single ;  { Max. positive A/D input voltage range}
   MinSamplingInterval : single ;
   MaxSamplingInterval : single ;
   NIDAQLoaded : boolean ; { True if NIDAQ.DLL procedure NI_ loaded }
   NIDisableDMA : Boolean ; { Disable DMA data transfers }
   NISetDeviceInfo : Boolean ; { Request Device info to be set }

   { NIDAQ.DLL procedure NI_ variables }
   Init_DA_Brds : TInit_DA_Brds ;
   DAQ_Clear : TDAQ_Clear ;
   DAQ_Config : TDAQ_Config ;
   DAQ_DB_Config : TDAQ_DB_Config ;
   DAQ_Start : TDAQ_Start ;
   DAQ_Rate : TDAQ_Rate ;
   Lab_ISCAN_Start : TLab_ISCAN_Start ;
   SCAN_Setup : TSCAN_Setup ;
   SCAN_Start : TSCAN_Start ;
   WFM_Rate : TWFM_Rate ;
   WFM_ClockRate : TWFM_ClockRate ;
   WFM_Load : TWFM_Load ;
   WFM_Group_Control : TWFM_Group_Control ;
   WFM_Check : TWFM_Check ;
   AO_Configure : TAO_Configure ;
   AO_Write : TAO_Write ;
   AO_Update : TAO_Update ;
   AO_VScale : TAO_VScale ;
   DIG_Block_Clear : TDIG_Block_Clear ;
   DIG_Block_Out : TDIG_Block_Out ;
   DIG_SCAN_Setup : TDIG_SCAN_Setup ;
   DIG_Prt_Config : TDIG_Prt_Config ;
   DIG_Out_Port : TDIG_Out_Port ;
   AI_Read : TAI_Read ;
   Set_DAQ_Device_Info : TSet_DAQ_Device_Info ;



procedure NI_LoadNIDAQLibrary  ;
{ ----------------------------------
  Load NIDAQ.DLL library into memory
  ----------------------------------}
var
   Hnd : THandle ;
   DLLName0 : array[0..79] of char ;
begin
     { Load library }
     StrPCopy( DLLName0,'NIDAQ.DLL');
     Hnd := LoadLibrary(DLLName0);

     { Get addresses of procedure NI_s used }
     if Hnd > HINSTANCE_ERROR then begin
        @Init_DA_Brds := GetProcAddress(Hnd,'Init_DA_Brds') ;
        if @Init_DA_Brds = Nil then NI_ReportFailure('Init_DA_Brds') ;
        @DAQ_Clear := GetProcAddress(Hnd,'DAQ_Clear') ;
        if @DAQ_Clear = Nil then NI_ReportFailure('DAQ_Clear') ;
        @DAQ_Config := GetProcAddress(Hnd,'DAQ_Config') ;
        if @DAQ_Config = Nil then NI_ReportFailure('AQ_Config') ;
        @DAQ_DB_Config := GetProcAddress(Hnd,'DAQ_DB_Config') ;
        if @DAQ_DB_Config = Nil then NI_ReportFailure('DAQ_DB_Config') ;
        @DAQ_Start := GetProcAddress(Hnd,'DAQ_Start') ;
        if @DAQ_Start = Nil then NI_ReportFailure('AQ_Start') ;
        @DAQ_Rate := GetProcAddress(Hnd,'DAQ_Rate') ;
        if @DAQ_Rate = Nil then NI_ReportFailure('DAQ_Rate') ;
        @Lab_ISCAN_Start := GetProcAddress(Hnd,'Lab_ISCAN_Start') ;
        if @Lab_ISCAN_Start = Nil then NI_ReportFailure('Lab_ISCAN_Start') ;
        @SCAN_Setup := GetProcAddress(Hnd,'SCAN_Setup') ;
        if @SCAN_Setup = Nil then NI_ReportFailure('SCAN_Setup') ;
        @SCAN_Start := GetProcAddress(Hnd,'SCAN_Start') ;
        if @SCAN_Start = Nil then NI_ReportFailure('SCAN_Start') ;
        @WFM_Rate := GetProcAddress(Hnd,'WFM_Rate') ;
        if @WFM_Rate = Nil then NI_ReportFailure('WFM_Rate') ;
        @WFM_ClockRate := GetProcAddress(Hnd,'WFM_ClockRate') ;
        if @WFM_ClockRate = Nil then NI_ReportFailure('WFM_ClockRate') ;
        @WFM_Load := GetProcAddress(Hnd,'WFM_Load') ;
        if @WFM_Load = Nil then NI_ReportFailure('WFM_Load') ;
        @WFM_Group_Control := GetProcAddress(Hnd,'WFM_Group_Control') ;
        if @WFM_Group_Control = Nil then NI_ReportFailure('WFM_Group_Control') ;
        @WFM_Check := GetProcAddress(Hnd,'WFM_Check') ;
        if @WFM_Check = Nil then NI_ReportFailure('WFM_Check') ;
        @AO_Configure := GetProcAddress(Hnd,'AO_Configure') ;
        if @AO_Configure = Nil then NI_ReportFailure('AO_Configure') ;
        @AO_Write := GetProcAddress(Hnd,'AO_Write') ;
        if @AO_Write = Nil then NI_ReportFailure('AO_Write') ;
        @AO_Update := GetProcAddress(Hnd,'AO_Update') ;
        if @AO_Update = Nil then NI_ReportFailure('AO_Update') ;
        @AO_VScale := GetProcAddress(Hnd,'AO_VScale') ;
        if @AO_VScale = Nil then NI_ReportFailure('AO_VScale') ;
        @DIG_Block_Clear := GetProcAddress(Hnd,'DIG_Block_Clear') ;
        if @DIG_Block_Clear = Nil then NI_ReportFailure('DIG_Block_Clear') ;
        @DIG_Block_Out := GetProcAddress(Hnd,'DIG_Block_Out') ;
        if @DIG_Block_Out = Nil then NI_ReportFailure('DIG_Block_Out') ;
        @DIG_SCAN_Setup := GetProcAddress(Hnd,'DIG_SCAN_Setup') ;
        if @DIG_SCAN_Setup = Nil then NI_ReportFailure('DIG_SCAN_Setup') ;
        @DIG_Prt_Config := GetProcAddress(Hnd,'DIG_Prt_Config') ;
        if @DIG_Prt_Config = Nil then NI_ReportFailure('DIG_Prt_Config') ;
        @DIG_Out_Port := GetProcAddress(Hnd,'DIG_Out_Port') ;
        if @DIG_Out_Port = Nil then NI_ReportFailure('DIG_Out_Port') ;
        @AI_Read := GetProcAddress(Hnd,'AI_Read') ;
        if @AI_Read = Nil then NI_ReportFailure('AI_Read')  ;
        @Set_DAQ_Device_Info := GetProcAddress(Hnd,'Set_DAQ_Device_Info') ;
        if @Set_DAQ_Device_Info = Nil then NI_ReportFailure('Set_DAQ_Device_Info')  ;
        NIDAQLoaded := True ;
        end
     else begin
          MessageDlg( 'NIDAQ.DLL library not found', mtWarning, [mbOK], 0 ) ;
          NIDAQLoaded := False ;
          end ;
     end ;


procedure NI_ReportFailure( const ProcName : string ) ;
begin
     MessageDlg('NIDAQ.DLL- ' + ProcName + ' not found',mtWarning,[mbOK],0) ;
     end ;


function NI_IsLabInterfaceAvailable : boolean ;
{ ------------------------------------------------------------
  Check to see if a lab. interface library is available
  ------------------------------------------------------------}
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;
     NI_IsLabInterfaceAvailable := NIDAQLoaded ;
     end ;


procedure NI_GetADCVoltageRangeOptions( var RangeOptions : array of TADCRange ;
                                     var NumOptions : Integer) ;
{ ------------------------------------------------------------
  Return a string array of A/D converter input voltage ranges
  ------------------------------------------------------------}
var
   Err : Integer ;
begin

     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;
     case DeviceNumber of
          0..2,21..22 : begin
              { ATMIO-16L boards }
              RangeOptions[0] := ' ±5.0 V ' ;
              RangeOptions[1] := ' ±0.5 V ' ;
              RangeOptions[2] := ' ±0.05 V ' ;
              RangeOptions[3] := ' ±0.01 V ' ;
              NumOptions := 4 ;
              end ;
          4..6,23..24 : begin
              { ATMIO-16H boards }
              RangeOptions[0] := ' ±5.0 V ' ;
              RangeOptions[1] := ' ±2.5 V ' ;
              RangeOptions[2] := ' ±1.25 V ' ;
              RangeOptions[3] := ' ±0.625 V ' ;
              NumOptions := 4 ;
              end ;
          19,39,41..43 : begin
              { ATMIO-16X boards (16 bit)}
              RangeOptions[0] := ' ±10.0 V ' ;
              RangeOptions[1] := ' ±5.0 V ' ;
              RangeOptions[2] := ' ±1.0 V ' ;
              RangeOptions[3] := ' ±0.1 V ' ;
              NumOptions := 4 ;
              end ;
          13,56 : begin
              { PC-LPM-16 boards }
              RangeOptions[0] := ' ±5.0 V ' ;
              NumOptions := 1 ;
              end ;
          else begin
              { All other boards }
              RangeOptions[0] := ' ±5.0 V ' ;
              RangeOptions[1] := ' ±2.5 V ' ;
              RangeOptions[2] := ' ±1.0 V ' ;
              RangeOptions[3] := ' ±0.5 V ' ;
              RangeOptions[4] := ' ±0.25 V ' ;
              RangeOptions[5] := ' ±0.1 V ' ;
              RangeOptions[6] := ' ±0.05 V ' ;
              NumOptions := 7
              end ;
          end ;
     ADCVoltageRangeMax := ExtractFloat( RangeOptions[0], 5. ) ;
     end ;


procedure NI_GetSamplingIntervalRange( var MinInterval,MaxInterval : single ) ;
{ ------------------------------------------------------------
  Return the range of valid sampling intervals for this device
  ------------------------------------------------------------}
var
   Err : Integer ;
   Dummy : string ;
begin

     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     if MinSamplingInterval = 0. then  NI_GetLabInterfaceInfo( Dummy,Dummy ) ;
     MinInterval := MinSamplingInterval ;
     MaxInterval := MaxSamplingInterval ;
     end ;


procedure NI_ADCToMemory( var ADCBuf : array of integer ;
                           nChannels : LongInt ;
                           nSamples : LongInt ;
                           var dt : Single ;
                           ADCVoltageRange : Single ;
                           WaitForExtTrigger : Boolean ;
                           CircularBuffer : Boolean  ) ;
{ -----------------------------
  Start A/D converter sampling
  -----------------------------}
const
     EmptyFlag = 32767 ;

var
   TimeBase,err,Channel,Gain,i,NumDACChannels,ch : Integer ;
   ChanVector : array[0..15] of Integer ;
   GainVector : array[0..15] of Integer ;
   ClockTicks : word ;
   SamplingInterval : Double ;
begin

     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     { Disable DMA channel and use interrupts to handle A/D sample transfers }
     if NIDisableDMA and NISetDeviceInfo then begin
        NI_CheckError( Set_DAQ_Device_Info( Device,
                                         ND_DATA_XFER_MODE_AI,
                                         ND_INTERRUPTS ) );
        NISetDeviceInfo := False ;
        end ;

     Err := DAQ_Clear(Device) ; { Reset Data acquisition sub-system }

     { Determine sampling clock time-base and number of ticks }
     SamplingInterval := dt / nChannels ;
     NI_CheckSamplingInterval( SamplingInterval,Timebase,ClockTicks) ;
     dt := SamplingInterval * nChannels ;

     { Set recording trigger mode }
     if WaitForExtTrigger then NI_CheckError(DAQ_Config( Device,1,0))
                          else NI_CheckError(DAQ_Config( Device,0,0)) ;

     {If in 'CircularBuffer' mode set A/D converter to continue
      indefinitely filling ADC buffer }
     if CircularBuffer then NI_CheckError(DAQ_DB_Config(Device, 1))
                       else NI_CheckError(DAQ_DB_Config(Device, 0)) ;

     { Fill A/D data buffer with empty flag }
     for i := 0 to (nSamples*nChannels)-1 do ADCBuf[i] := EmptyFlag ;

     { Set internal gain for A/D converter's programmable amplifier }
     Gain := Trunc( 5.001 / ADCVoltageRange ) ;

     if nChannels < 2 then begin
        { Single A/D channel sampling }
        Channel := 0 ;
        NI_CheckError(DAQ_Start(Device,Channel,Gain,
                             @ADCBuf,nSamples,Timebase,ClockTicks )) ;
        end
     else begin
        { Multiple A/D channel sampling }
        case DeviceNumber of
             9,13,28,31,33,48,49,57 : begin
                { Multi-channel A/D conversion for LAB-PC-like cards }
                NI_CheckError(LAB_ISCAN_Start( Device, nChannels, Gain, @ADCBuf,
                           nSamples*nChannels,TimeBase,ClockTicks,0));
                end ;
        else begin
            { Multi-channel A/D conversion for cards with Channel/Gain lists }
            { Note ... channels are acquired in descending order }
            for ch := 0 to nChannels-1 do begin
                ChanVector[ch] := nChannels-ch-1 ;
                GainVector[ch] := Gain ;
                end ;
            NI_CheckError(SCAN_Setup( Device, nChannels, @ChanVector, @GainVector ) );
            NI_CheckError(SCAN_Start( Device, @ADCBuf, nSamples*nChannels,TimeBase,
                       ClockTicks,TimeBase,0)) ;
            end ;
            end ;
        end ;
     end ;


procedure NI_CheckSamplingInterval( var SamplingInterval : double ;
                                    var TimeBase : Integer ;
                                    var ClockTicks : Word )  ;
{ ---------------------------------------------------------
  Check sampling interval to make sure it lies within valid
  range and adjust it to match sample clock settings
  ---------------------------------------------------------}
var
   Err : Integer ;
   ClockInterval : array[-1..5] of Single ;
begin

     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     { Determine sampling clock time-base and number of ticks }
     Err := DAQ_Rate (SamplingInterval,1,Timebase,ClockTicks ) ;
     ClockInterval[-1] := 2E-6 ;
     ClockInterval[0] := 1E-6 ;
     ClockInterval[1] := 1E-6 ;
     ClockInterval[2] := 1E-5 ;
     ClockInterval[3] := 1E-4 ;
     ClockInterval[4] := 1E-5 ;
     ClockInterval[5] := 1E-2 ;
     SamplingInterval := ClockTicks * ClockInterval[TimeBase] ;
     end ;


procedure NI_CheckError( Err : Integer ) ;
{ --------------------------------------------------------------
  Warn User if the NIDAQ Lab. interface library returns an error
  --------------------------------------------------------------}
begin

     if Err <> 0 then MessageDlg(' Lab. Interface Error = ' +
                                   format('%d',[Err]),
                                   mtWarning, [mbOK], 0 ) ;
     end ;


procedure NI_MemoryToDAC( var DACBuf : array of integer ;
                       nChannels : LongInt ;
                       nPoints : LongInt ;
                       dt : Single ;
                       NumRepeats : LongInt ;
                       var DACActive : Boolean ) ;
{ --------------------------
  Start D/A converter output
  --------------------------}
var
   UpdateInterval : double ;
   TimeBase,Stopped,Err : Integer ;
   ClockTicks,ItersDone,PointsDone,nDACValues : LongInt ;
   Channels : array[0..1] of Integer ;
begin

     if not DACActive then begin
        if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

        { If the board type is not known ... get it now }
        if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

        { Disable DMA channel and use interrupts to handle transfers (added 31/5/98) }
        if NIDisableDMA and NISetDeviceInfo then begin
           NI_CheckError( Set_DAQ_Device_Info( Device,
                                         ND_DATA_XFER_MODE_AI,
                                         ND_INTERRUPTS ) );
           NISetDeviceInfo := False ;
           end ;                                                 {end added 31/5/98}

        { Set D/A update clock }
        UpdateInterval := dt ;
        NI_CheckError(WFM_Rate( UpdateInterval, 1,Timebase,ClockTicks));

        NI_CheckError(WFM_ClockRate(Device,1,0,TimeBase,ClockTicks,0));

        { Set up D/A channel selection array }
        Channels[0] := 0 ;
        Channels[1] := 1;

        { Load D/A data into output buffer }
        nDACValues := nPoints*nChannels ;
        NI_CheckError(WFM_Load( Device,nChannels,@Channels[0],@DACBuf[0],
                      nDACValues,NumRepeats,0)) ;
        { Begin D/A output sequence }
        NI_CheckError(WFM_Group_Control(Device,1,1)) ;
        DACActive := True ;
        end ;

     end ;


procedure NI_CheckDACProgress( var NumPointsDone, NumIterationsDone : LongInt ;
                            var Active : Boolean ) ;
{ -----------------------------------------------
  Return the current state of D/A output waveform
  -----------------------------------------------}
var
   DACStopped,Err : Integer ;
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     NI_CheckError( WFM_Check(Device,0,DACStopped,NumIterationsDone,NumPointsDone ));
     if DACStopped = 0 then Active := True
                       else Active := false ;
     end ;


procedure NI_StopDAC ;
var
   Err,ch : Integer ;
begin
     { Stop D/A output }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;
     Err := WFM_Group_Control(Device,1,0) ;
     end ;


procedure NI_WriteDACs( DACVolts : array of Single ; nChannels : LongInt ) ;
{ --------------------------------------
  Write values to D/A converter outputs
  -------------------------------------}
var
   iDACValue,Err,ch : Integer ;
   MaxDACVolts : single ;
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     MaxDACVolts := NI_GetMaxDACVolts ;

     { Output the final D/A values }
     for ch := 0 to nChannels-1 do begin
         NI_CheckError( AO_Configure( Device, ch, 0, 0, 10., 1 ) ) ;
         iDACValue := Trunc((DACVolts[ch]*MaxDACValue)/MaxDACVolts) ;
         iDACValue := IntLimitTo( iDACValue, MinDACValue, MaxDACValue ) ;
         NI_CheckError( AO_Write(Device,ch,iDACValue) ) ;
         end ;
     NI_CheckError( AO_Update(Device) ) ;
     end ;

function NI_GetMaxDACVolts : single ;
var
   Err,iDACValue : Integer ;
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     { Determine D/A output voltage range (+/-5V or +/-10V) }
     NI_CheckError( AO_VScale( device, 0, 4.9, iDACValue ) ) ;
     if iDACValue > 2000 then Result := 5.
                         else Result := 10. ;
     end ;


procedure NI_StopADC ;
{ -------------------------------
  Reset A/D conversion sub-system
  -------------------------------}
var
   Err : Integer ;
begin
     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     Err := DAQ_Clear(Device) ;
     end ;


procedure NI_MemoryToDigitalPort( var DigBuf : array of Integer ; nBytes : LongInt ) ;
{ ----------------------------------------------
  Copy bytes in DigBuf to digital output port 0
  ---------------------------------------------}
const
     Group = 1 ;
     AsOutput = 1 ;
var
     PortList : Array[0..4] of Integer ;
     Err : Integer ;
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     { Clear any existing block transfers }
     Err := Dig_Block_Clear( Device, Group ) ;

     { Clear port assignments to group 0 }
     PortList[0] := 0 ;
     Err := Dig_SCAN_Setup( Device, Group, 0, @PortList, AsOutput ) ;
     { Note ... No CheckError because an error occurs when group
       is cleared and none has been assigned 4.9.0 }
     { Assign port 0 to group 1 as an output port }
     NI_CheckError(Dig_SCAN_Setup( Device, Group, 1, @PortList, AsOutput )) ;
     { Initiate a block transfer from DigBuf to port 0 }
     NI_CheckError(Dig_Block_Out(  Device, Group, @DigBuf, nBytes )) ;
     { The timing of the transfer is determined by the High/Low
       status of the ACK* pin on the I/O connector (pin PC6)
       This pin should be connected to the sync. pulse O/P (DAC1) }
     end ;

procedure NI_StopDIG( Pattern : LongInt ) ;
{ ---------------------------------------------------------------
  Stop digital port waveform output (cancels MemoryToDigitalPort)
  ---------------------------------------------------------------}
var
     Err : Integer ;
begin

     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

     { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;
     { Write default pattern to port }
     NI_WriteToDigitalOutPutPort( Pattern ) ;
     end ;


procedure NI_WriteToDigitalOutPutPort( Pattern : LongInt ) ;
{ ----------------------
  Update digital port 0
  ---------------------}
const
     Group = 1 ;
     AsOutput = 1 ;
var
   PortList : Array[0..4] of Integer ;
   err : Integer ;
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;
      if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     { Clear any existing block transfers }
     Err := Dig_Block_Clear( Device, Group ) ;
     { Clear port assignments to group 0 }
     PortList[0] := 0 ;
     Err := Dig_SCAN_Setup( Device, Group, 0, @PortList, AsOutput ) ;
     { Note ... No CheckError because an error occurs when group
       is cleared and none has been assigned 4.9.0 }

     { Set port 0 to output, mode 0 }
     NI_CheckError( DIG_Prt_Config( Device, 0, 0, 1 )) ;

     { Send the byte pattern }
     NI_CheckError(DIG_Out_Port( Device, 0, Pattern )) ;
     end ;


procedure NI_GetLabInterfaceInfo( var Supplier, Model : string ) ;
var
   Err, BoardCode,TimeBase : Integer ;
   ClockTicks : Word ;
   BoardName : array[0..60] of String[16] ;
   MinInterval : array[0..60] of single ;
   ADCBuf : Array[0..10] of Integer ;
   Min,MinHi,MinLo : double ;
begin
      if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

      NI_CheckError(Init_DA_Brds( 1, BoardCode )) ;
      BoardName[0] :=  'AT-MIO-16L-9' ;
      MinInterval[0] := 1E-5 ;
      BoardName[1] :=  'AT-MIO-16L-15' ;
      MinInterval[1] := 1E-5 ;
      BoardName[2] :=  'AT-MIO-16L-25' ;
      MinInterval[2] := 1E-5 ;
      BoardName[3] := '?' ;
      MinInterval[3] := 1E-5 ;
      BoardName[4] :=  'AT-MIO-16H-9' ;
      MinInterval[4] := 1E-5 ;
      BoardName[5] :=  'AT-MIO-16H-15' ;
      MinInterval[5] := 1E-5 ;
      BoardName[6] :=  'AT-MIO-16H-25' ;
      MinInterval[6] := 1E-5 ;
      BoardName[7] :=  'PC-DIO-24' ;
      MinInterval[7] := 1E-5 ;
      BoardName[8] :=  'AT-DIO-32F' ;
      MinInterval[8] := 1E-5 ;
      BoardName[9] := '?' ;
      MinInterval[9] := 1E-5 ;
      BoardName[10] :=  'EISA-A2000' ;
      MinInterval[10] := 1E-6 ;
      BoardName[11] :=  'AT-MIO-16F-5' ;
      MinInterval[11] := 1E-5 ;
      BoardName[12] :=  'PC-DIO-96/PnP' ;
      MinInterval[12] := 1E-5 ;
      BoardName[13] :=  'PC-LPM-16' ;
      MinInterval[13] := 2E-5 ;
      BoardName[14] :=  'PC-TIO-10' ;
      MinInterval[14] := 1E-5 ;
      BoardName[15] :=  'AT-AO-6' ;
      MinInterval[15] := 1E-5 ;
      BoardName[16] :=  'AT-A2150S' ;
      MinInterval[16] := 1E-5 ;
      BoardName[17] :=  'AT-DSP2200 ' ;
      MinInterval[17] := 1E-5 ;
      BoardName[18] :=  'AT-DSP2200 ' ;
      MinInterval[18] := 1E-5 ;
      BoardName[19] :=  'AT-MIO-16X' ;
      MinInterval[19] := 1E-5 ;
      BoardName[20] :=  'AT-MIO-64F-5' ;
      MinInterval[20] := 1E-5 ;
      BoardName[21] :=  'AT-MIO-16DL-9' ;
      MinInterval[21] := 1E-5 ;
      BoardName[22] :=  'AT-MIO-16DL-25' ;
      MinInterval[22] := 1E-5 ;
      BoardName[23] :=  'AT-MIO-16DH-9' ;
      MinInterval[23] := 1E-5 ;
      BoardName[24] :=  'AT-MIO-16DH-25' ;
      MinInterval[24] := 1E-5 ;
      BoardName[25] :=  'AT-MIO-16E-2' ;
      MinInterval[25] := 1E-5 ;
      BoardName[26] :=  'AT-AO-10' ;
      MinInterval[26] := 1E-5 ;
      BoardName[27] :=  'AT-A2150C' ;
      MinInterval[27] := 1E-5 ;
      BoardName[28] :=  'Lab-PC+' ;
      MinInterval[28] := 1./82500. ;
      BoardName[29] := '?' ;
      MinInterval[29] := 1./82500. ;
      BoardName[30] :=  'SCXI-1200' ;
      MinInterval[30] := 1E-5 ;
      BoardName[31] :=  'DAQCard-700' ;
      MinInterval[31] := 1E-5 ;
      BoardName[32] :=  'NEC-MIO-16E-4' ;
      MinInterval[32] := 1E-5 ;
      BoardName[33] :=  'DAQPad-1200' ;
      MinInterval[33] := 1E-5 ;
      BoardName[35] :=  'DAQCard-DIO-24' ;
      MinInterval[34] := 1E-5 ;
      BoardName[36] :=  'AT-MIO-16E-10' ;
      MinInterval[36] := 1E-5 ;
      BoardName[37] :=  'AT-MIO-16DE-10' ;
      MinInterval[37] := 1E-5 ;
      BoardName[38] :=  'AT-MIO-64E-3' ;
      MinInterval[38] := 1E-5 ;
      BoardName[39] :=  'AT-MIO-16XE-50' ;
      MinInterval[39] := 1E-5 ;
      BoardName[40] :=  'NEC-AI-16E-4' ;
      MinInterval[40] := 1E-5 ;
      BoardName[41] :=  'NEC-MIO-16XE-50' ;
      MinInterval[41] := 1E-5 ;
      BoardName[42] :=  'NEC-AI-16XE-50' ;
      MinInterval[42] := 1E-5 ;
      BoardName[43] :=  'DAQPad-MIO-16XE-50' ;
      MinInterval[43] := 1E-5 ;
      BoardName[44] :=  'AT-MIO-16E-1' ;
      MinInterval[44] := 1E-5 ;
      BoardName[45] :=  'PC-OPDIO-16' ;
      MinInterval[45] := 1E-5 ;
      BoardName[46] :=  'PC-AO-2DC' ;
      MinInterval[46] := 1E-5 ;
      BoardName[47] :=  'DAQCard-AO-2DC' ;
      MinInterval[47] := 1E-5 ;
      BoardName[48] :=  'DAQCard-1200' ;
      MinInterval[48] := 1E-5 ;
      BoardName[49] :=  'DAQCard-500' ;
      MinInterval[49] := 1E-5 ;
      BoardName[50] :=  'AT-MIO-16XE-10' ;
      MinInterval[50] := 1E-5 ;
      BoardName[51] :=  'AT-AI-16XE-10' ;
      MinInterval[51] := 1E-5 ;
      BoardName[52] :=  'DAQCard-AI-16XE-50' ;
      MinInterval[52] := 1E-5 ;
      BoardName[53] :=  'DAQCard-AI-16E-4' ;
      MinInterval[53] := 1E-5 ;
      BoardName[54] :=  'DAQCard-516' ;
      MinInterval[54] := 1E-5 ;
      BoardName[55] :=  'PC-516' ;
      MinInterval[55] := 1E-5 ;
      BoardName[56] :=  'PC-LPM-16PnP' ;
      MinInterval[56] := 1E-5 ;
      BoardName[57] :=  'Lab-PC-1200' ;
      MinInterval[57] := 1E-5 ;
      BoardName[58] :=  'Unknown' ;
      MinInterval[58] := 1E-5 ;
      BoardName[59] :=  'Unknown' ;
      MinInterval[59] := 1E-5 ;
      BoardName[60] :=  'Unknown' ;
      MinInterval[60] := 1E-5 ;

      Supplier := 'National Instruments Inc.' ;
      if BoardCode >= 0 then begin
         Model := format('Model: %s (%d)',[BoardName[BoardCode],BoardCode]) ;
         MinSamplingInterval := MinInterval[BoardCode] ;
         MaxSamplingInterval := 1000. ;
         end
      else Model := format('Not an N.I. card (%d)',[BoardCode]) ;
      end ;


function NI_ReadADC( Channel : Integer ; ADCVoltageRange : Single ) : Integer ;
var
   ADCREading, Gain, Err : Integer ;
begin
     if not NIDAQLoaded then NI_LoadNIDAQLibrary ;

      { If the board type is not known ... get it now }
     if DeviceNumber < 0 then Err := Init_DA_Brds( Device, DeviceNumber ) ;

     { Set internal gain for A/D converter's programmable amplifier }
     Gain := Trunc( (ADCVoltageRangeMax+0.001) / ADCVoltageRange ) ;
     NI_CheckError( AI_Read( 1, Channel, Gain, ADCReading ) ) ;
     NI_ReadADC := ADCReading ;
     end ;

procedure NI_GetChannelOffsets( var Offsets : Array of LongInt ; NumChannels : LongInt ) ;
var
   ch : Integer ;
begin
     for ch := 0 to NumChannels-1 do Offsets[ch] := NumChannels - 1 - ch ;
     end ;


procedure NI_ConfigureHardware( DisableDMA : Boolean ) ;
{ ---------------------------
  Set hardware configurations
  ---------------------------}
begin
     NIDisableDMA := DisableDMA ;
     NISetDeviceInfo := True ;
     end ;


initialization
    DeviceNumber := -2 ;
    NIDAQLoaded := False ;
    NIDisableDMA := False ;
    NISetDeviceInfo := True ;
    MinSamplingInterval := 0. ;
    MaxSamplingInterval := 1000. ;

end.
