NI-DAQmx and NI-DAQmx Base examples

From Free Pascal wiki
Jump to navigationJump to search

INTRODUCTION

National Instruments produce a wide range of DAQ cards, which are generally used for acquiring and generating signals.

These cards usually have a few analog inputs/outputs, digital inputs/outputs, counters and a frequency generator with hardware/software timing. Exact features (type, number, parameters) depends on the card type.

NIDAQmxBase.pas and NIDAQmx.pas are provided pascal bindings to National Instruments libraries and enable control of NI DAQ cards from programs written in Free Pascal.

What National Instruments Hardware is supported

Supported hardware list by NI-DAQmx and NI-DAQ Driver for different operating systems.

NI-DAQmxBase library

NI-DAQmxBase library or driver is multiplatform library for Windows (Windows XP 32 bit, Windows 7 32/64 bit, Windows Vista 32/64 bit, Windows 8 32/64 bit), macOS 32/64 bit and Linux 32/64 bit (SUSE, Scientific Linux, RedHat, CentOS), but doesn't implement all device features. For example, digital input/output ports speed is limited by computer speed ~100kHz, because DMA data transfer is not supported.

Download library from here. Read readme file to find the list of supported hardware and hardware features.

  Latest Linux version 15.0 supports 64-bit OS, but you still need to install some 32-bit library. 
  
  LIST OF LIBRARIES:

  compat-libstdc++.i686
  expat.i686
  glibc.i686
  glibc-devel.i686
  libdrm.i686
  libgcc.i686
  libselinux.i686
  libstdc++.i686
  libX11.i686
  libXau.i686
  libxcb.i686
  libXdamage.i686
  libXext.i686
  libXinerama.i686
  libXfixes.i686
  libXxf86vm.i686
  mesa-dri-drivers.i686
  mesa-libGL.i686
  nss-softokn-freebl.i686
  zlib.i686

NI-DAQmx library

The NI-DAQmx library is only available for Windows 7 (SP1) 32/64 bit, Windows 8.1 32/64 bit, Windows 10 32/64 bit, Windows Embedded Standard 7 (SP1), Windows Server 2008 R2 (SP1) 32/64 bit). Download here)

Read readme file to find the list of supported hardware and hardware features.

HOW TO IDENTIFY PRESENT HARDWARE

To get list of devices present in your system Under Linux operation system use

1) nilsdev utility with NIDAQmx driver installed,
2) lsdaq utility with NIDAQmxBase driver installed.

Under Windows use NIMAX utility to get device list or set device alias etc.

Library documentation

You can find description of functions and properties in NI-DAQmx and NI-DAQmx Base C reference Help:

PASCAL bindings

Your can download pascal bindings nidaqmxbase.pas and nidaqmx.pas in this forum thread [1] or as a part of the LazMer project.

FUNCTION CALLING

Generally, when you need to acquire or gerenate data, call functions from nidaqmx.pas as follow

1. Create task: DAQmxCreateTask
2. Channel create: 
   a)DAQmxCreateDIChan/DAQmxCreateDOChan (digital input(s)/output(s)) 
   b)DAQmxCreateAIVoltageChan/DAQmxCreateAOVoltageChan (analog input(s)/output(s))
3. Set timing if you need hardware timimg: DAQmxCfgSamplClkTiming
4. Start task: DAQmxStartTask
5. Read or write data from/to channel: 
   a)read data from digital port: DAQmxReadDigitalU8(8-bit port),DAQmxReadDigitalU32 (32-bit port),
   b)write data to digital port:  DAQmxWriteDigitalU8(8-bit port),DAQmxWriteDigitalU32 (32-bit port), 
   c)read data from analog input(s): DAQmxReadAnalogF64,
   d)write data to analog output(s): DAQmxWriteAnalogF64.
6. Do job
7. End of program - stop and clear task: DAQmxStopTask and DAQmxClearTask

For error handling create function like ErrorCheck.

from nidaqmxbase.pas as follow

1. Create task: DAQmxBaseCreateTask
2. Channel create: 
   a)DAQmxBaseCreateDIChan/DAQmxBaseCreateDOChan (digital input(s)/output(s)) 
   b)DAQmxBaseCreateAIVoltageChan/DAQmxBaseCreateAOVoltageChan (analog input(s)/output(s))
3. Set timing if you need hardware timimg: DAQmxBaseCfgSamplClkTiming
4. Start task: DAQmxBaseStartTask
5. Read or write data from/to channel: 
   a)read data from digital port: DAQmxBaseReadDigitalU8(8-bit port),DAQmxBaseReadDigitalU32 (32-bit port),
   b)write data to digital port:  DAQmxBaseWriteDigitalU8(8-bit port),DAQmxBaseWriteDigitalU32 (32-bit port), 
   c)read data from analog input(s): DAQmxBaseReadAnalogF64,
   d)write data to analog output(s): DAQmxBaseWriteAnalogF64.
6. Do job
7. End of program - stop and clear task: DAQmxBaseStopTask and DAQmxBaseClearTask

For error handling create function like ErrorCheck.
 Probably, some examples need to add @ before variable, which use as function parameter!!!

NI-DAQmx examples

DEVICE INFO

Get device list

You can check device list names using NI MAX program under Windows or nilsdev utility under Linux.

program devicelist;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils
  { you can add units after this };
var
  devicenames:array of Char;
  infostr:string;
  DeviceNamesList:TStringList;
  buffersize:longint;
  product_type:array of char;
  devserialnum,is_simulated:longint;
  is_simulated_str:string;
  i:byte;
begin
  DeviceNamesList:=TStringList.Create;
  try
     {**************************************************************************
                      Get device names present in the system
     **************************************************************************}
     //get buffer size or length of string of device list
     buffersize:=DAQmxGetSystemInfoAttribute(DAQmx_Sys_DevNames,devicenames[0],[0]);
     //set array of Char lenght
     if Buffersize<>0 then begin
       writeln('-------------------------------------------------------------');
       writeln('|Device name | Product Type | # Serial number | Is Simulated?');
       writeln('-------------------------------------------------------------');
       SetLength(devicenames,buffersize);
       //get the device list
       DAQmxGetSystemInfoAttribute(DAQmx_Sys_DevNames,devicenames[0],[buffersize]);
       //convert it to string: names are separated by comma
       SetString(infostr,PChar(@devicenames[0]),buffersize);
       //add names to TStringList
       DeviceNamesList.CommaText:=infostr;
       {************************************************************************
                              Get Product info
        ***********************************************************************}
       for i:=0 to DeviceNamesList.Count-1 do begin
          //get buffer size or length of string of product type
          buffersize:=DAQmxGetDeviceAttribute(@DeviceNamesList.Strings[i][1],DAQmx_Dev_ProductType,Nil);
          //set lenght of array of char
          SetLength(product_type,buffersize);
          //get product type info
          DAQmxGetDeviceAttribute(@DeviceNamesList.Strings[i][1],DAQmx_Dev_ProductType,product_type[0],[buffersize]);
          {***********************************************************************
                            Get Serial Number
          **********************************************************************}
          DAQmxGetDeviceAttribute(@DeviceNamesList.Strings[i][1],DAQmx_Dev_SerialNum,devserialnum,[1]);
          //DeviceInfoGrid.Cells[3,i+1]:=IntToHex(devserialnum,8);
          {***********************************************************************
                             Is simulated?
          ***********************************************************************}
          DAQmxGetDeviceAttribute(@DeviceNamesList.Strings[i][1],DAQmx_Dev_IsSimulated,is_simulated,[1]);
          if is_simulated = 0 then is_simulated_str:='No'
          else is_simulated_str:='Yes';
          //Show info
          writeln('| ',DeviceNamesList.Strings[i],' | ',PChar(@product_type[0]),' | $',IntToHex(devserialnum,8),' |',is_simulated_str);
      end;
  end
  else writeln('any device found');
  finally
     DeviceNamesList.Free;
  end;
end.

Output example

-------------------------------------------------------------
|Device name | Product Type | # Serial number | Is Simulated?
-------------------------------------------------------------
| Dev1 | PCI-6250 | $018D0B90 |No

Get channel names

program chaninfo;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx
  { you can add units after this };
var
  buffersize,infotype:longint;
  channelnames:array of char;
  MsgChannelNames:array [0..255] of char;
  MsgNoChannelsText:array [0..255] of char;
  channeltype:byte;
  DeviceName:string;
begin
 {**********INPUT Device name and interested channel type **********************
  **********WITHOUT CHEKING IF DEVICE EXITS IN THE SYSTEM**********************}
  write('Enter device name, for example Dev1, Dev2 etc.: ');
  readln(Devicename);
  writeln('0 - analog inputs');
  writeln('1 - analog outputs');
  writeln('2 - digital inputs');
  writeln('3 - digital outputs');
  writeln('4 - counter inputs');
  writeln('5 - counter outputs');
  write('choose the number of required channel type:');
  readln(channeltype);
  {**********CHANNEL TYPE CHOSEN**********************************************}
  case channeltype of
  {********************ANALOG INPUTS NAMES*************************************}
  0: begin
      infotype:=DAQmx_Dev_AI_PhysicalChans;
      MsgChannelNames:='Analog input names';
      MsgNoChannelsText:='There are no analog inputs';
  end;
  {********************ANALOG OUTPUTS NAMES************************************}
  1: begin
      infotype:=DAQmx_Dev_AO_PhysicalChans;
      MsgChannelNames:='Analog output names';
      MsgNoChannelsText:='There are no analog outputs';
  end;
  {********************DIGITAL INPUT PORTS NAMES*******************************}
  2: begin
      infotype:=DAQmx_Dev_DI_Ports;
      MsgChannelNames:='Digital input ports';
      MsgNoChannelsText:='There are no digital input ports';
  end;
  {********************DIGITAL OUTPUT PORTS NAMES******************************}
  3:begin
     infotype:=DAQmx_Dev_DO_Ports;
     MsgChannelNames:='Digital output ports';
     MsgNoChannelsText:='There are no digital output ports';
  end;
  {********************COUNTER INPUT  NAMES******************************}
  4:begin
     infotype:= DAQmx_Dev_CI_PhysicalChans;
     MsgChannelNames:='Digital counter inputs';
     MsgNoChannelsText:='There are no counter inputs ';
  end;
  {********************COUNTER OUTPUT  NAMES******************************}
  5:begin
     infotype:= DAQmx_Dev_CO_PhysicalChans;
     MsgChannelNames:='Digital counter outputs';
     MsgNoChannelsText:='There are no counter outputs ';
  end;
 end;
{************GET CHANNEL NAMES*********************************************}
 if (channeltype>=0) and (channeltype<6) then begin
         //get buffer size or length of channelnames
         buffersize:=DAQmxGetDeviceAttribute(@DeviceName[1],infotype,Nil);
         if buffersize<>0 then begin
           SetLength(channelnames,buffersize);
           //get analog channel names separated by comma
           DAQmxGetDeviceAttribute(@DeviceName[1],infotype,channelnames[0],[buffersize]);
           //show info
           writeln(MsgChannelNames+': '+PChar(@channelnames[0]));
         end
         else writeln(MsgNoChannelsText);
  end;
end.

Output example

Enter device name, for example Dev1, Dev2 etc.: Dev1
0 - analog inputs                                   
1 - analog outputs                                  
2 - digital inputs                                  
3 - digital outputs                                 
4 - counter inputs                                  
5 - counter outputs                                 
choose the number of required channel type:0        
Analog input names: Dev1/ai0, Dev1/ai1, Dev1/ai2, Dev1/ai3, Dev1/ai4, Dev1/ai5, Dev1/ai6, Dev1/ai7, Dev1/ai8, Dev1/ai9, Dev1/ai10, Dev1/ai11, Dev1/ai12, Dev1/ai13, Dev1/ai14, Dev1/ai15

ANALOG INPUTS/OUTPUTS

Acquire finite number of samples from one analog input

program aichan;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, nidaqmx
  { you can add units after this };
var
  AITaskHandle:TaskHandle;
  data:array[1..1000] of double;
  reads,i:longint;

{************Error handling procedure *****************************************
    Example:
 ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

*******************************************************************************}

procedure ErrorCheck(AError:longint;ATaskHandle:longint);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if Aerror<>0 then begin
     DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
     writeln(PChar(@ErrBuffer[0]));
   //stop and cleat task
   if Assigned(@ATaskHandle) then begin
     DAQmxStopTask(ATaskHandle);
     DAQmxClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
{*********************MAIN PROGRAM*********************************************
 ******************* without error handling************************************}
begin
  //create task
  DAQmxCreateTask('',@AITaskHandle);
  //create analog input channel Dev1/ai0 (activate it)
  //voltage range -10 - 10
  DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL);
  //set timing
  //Sample rate = 10000 (10 kHz), sample per channel =1000
  //acquire finite samples (1000 samples) defined by DAQmx_Val_FiniteSamps
  DAQmxCfgSampClkTiming(AITaskHandle,'OnboardClock',10000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000);
  //start task
  DAQmxStartTask(AITaskHandle);
  //acquire 1000 samples
  DAQmxReadAnalogF64(AITaskHandle,1000,10.0,DAQmx_Val_GroupByChannel,@data[1],1000,@reads,NIL);
  //show first 10 samples
  writeln(reads,' samples were acquired');
  writeln('');
  writeln('Value of first 10 samples');
  for i:=1 to 10 do writeln('sample[',i,'] = ',data[i]:4:2,' V');
  //stop and clear task
  DAQmxStopTask(AITaskHandle);
  DAQmxClearTask(AITaskHandle);
end.

Output example

1000 samples were acquired

Value of first 10 samples
sample[1] = 0.23 V
sample[2] = 0.23 V
sample[3] = 0.23 V
sample[4] = 0.23 V
sample[5] = 0.23 V
sample[6] = 0.23 V
sample[7] = 0.23 V
sample[8] = 0.23 V
sample[9] = 0.23 V
sample[10] = 0.23 V

Continuously acquire samples from three analog inputs

program multiaichannel;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, nidaqmx,crt
  { you can add units after this };
var
  AITaskHandle:TaskHandle;
  data:array of double;
  reads:longint;
  totalsamplereads:longint=0;
  i:longint;

{************Error handling procedure *****************************************
    Example:
 ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

*******************************************************************************}

procedure ErrorCheck(AError:longint;ATaskHandle:longint);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if Aerror<>0 then begin
     DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
     writeln(PChar(@ErrBuffer[0]));
   //stop and cleat task
   if Assigned(@ATaskHandle) then begin
     DAQmxStopTask(ATaskHandle);
     DAQmxClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
{*********************MAIN PROGRAM*********************************************
 ******************* without error handling************************************}
begin
  //Set buffer size for data acquiring, sample per channel 1000
  //buffer size(data -array variable) = 3 channel * 1000 samples per channel
  Setlength(data,3*1000);
  //create task
  DAQmxCreateTask('',@AITaskHandle);
  //create analog input channel Dev1/ai0:2 (activate 3 channels)
  //voltage range -10 - 10
  DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0:2','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL);
  //set timing
  //Sample rate = 10000 (10 kHz), sample per channel =1000
  //acquire finite samples (1000 samples) defined by DAQmx_Val_Cont_Samps
  DAQmxCfgSampClkTiming(AITaskHandle,'OnboardClock',10000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000);
  //start task
  DAQmxStartTask(AITaskHandle);
  //acquire 10000 samples for each channel
  while totalsamplereads<10000 do begin
    //1000- sample rate, 3000 - buffer size(data -array variable) = 3 channel * 1000 samples per channel
    DAQmxReadAnalogF64(AITaskHandle,1000,10.0,DAQmx_Val_GroupByChannel,@data[0],3000,@reads,NIL);
    totalsamplereads:=totalsamplereads+reads;
    writeln('aquired number of samples: ',totalsamplereads);
  end;
  //show first 10 samples from last 1000 samples acquired from each channel
  writeln('');
  writeln('Value of first 10 samples of last 1000 samples for each channel');
  writeln('| chan0 |  chan1 |  chan2 | ');
  for i:= 0 to 9 do writeln('|',data[i]:5:2,' V| ',data[i+1000]:5:2,' V| ',data[i+2000]:5:2,' V|');
  //stop and clear task
  DAQmxStopTask(AITaskHandle);
  DAQmxClearTask(AITaskHandle);
end.

Output example

acquired number of samples: 1000                                
aquired number of samples: 2000                                
acquired number of samples: 3000                                
acquired number of samples: 4000                                
acquired number of samples: 5000                                
acquired number of samples: 6000                                
acquired number of samples: 7000                                
acquired number of samples: 8000                                
acquired number of samples: 9000                                
acquired number of samples: 10000

Value of first 10 samples of last 1000 samples for each channel
| chan0 |  chan1 |  chan2 |
| 5.07 V|  5.07 V|  5.06 V|
| 5.07 V|  5.07 V|  5.06 V|
| 5.07 V|  5.06 V|  5.07 V|
| 5.06 V|  5.06 V|  5.07 V|
| 5.06 V|  5.07 V|  5.07 V|
| 5.07 V|  5.07 V|  5.06 V|
| 5.07 V|  5.07 V|  5.07 V|
| 5.06 V|  5.06 V|  5.07 V|
| 5.06 V|  5.06 V|  5.06 V|
| 5.07 V|  5.07 V|  5.06 V|

Start acquisition on analog input by digital trigger

This example demonstrates how to start acquisition by digital trigger.

program aidigtrig;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,crt
  { you can add units after this };
var
   AITaskHandle:TaskHandle;
  channel:string = 'Dev1/ai0';
  SampleRate:double = 10000;
  SamplePerChannel:longint = 1000;
  PretriggerSamples: longint= 100; //MUST be > 2
  reads:longint = 0;
  i:longint;
  data:array of double;
procedure ErrorCheck(AError:longint;ATaskHandle:longint);
var
  errBuffer:array[0..2048] of char;
begin
  //get error description
  if Aerror<>0 then begin
     DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
     writeln(ErrBuffer);
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop acquisition
     writeln('Program stoped');
     Halt;
  end;
end;

begin
    //set buffer length
    SetLength(data,SamplePerChannel);
    //create take
    DAQmxCreateTask('',@AITaskHandle);
    //create ai channel
    ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,@channel[1],'',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,Nil),AITaskHandle);
    //configure sample clock
    ErrorCheck(DAQmxCfgSampClkTiming(AITaskHandle,'',SampleRate,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,SamplePerChannel),AITaskHandle);
    //configure triger with 100 pretriggered samples
    ErrorCheck(DAQmxCfgDigEdgeRefTrig(AITaskHandle,'/Dev1/PFI0',DAQmx_Val_Rising,PretriggerSamples),AITaskHandle);
    //start task
    ErrorCheck(DAQmxStartTask(AITaskHandle),AITaskHandle);
    //acquire data
    writeln('Press any key to stop acquisition');
    i := 0;
    repeat
      ErrorCheck(DAQmxReadAnalogF64(AITaskHandle,SamplePerChannel,10.0,DAQmx_Val_GroupByChannel,@data[0],Length(data),@reads,Nil),AITaskHandle);
      i := i + reads;
      writeln('Number of acquired samples: ', i);
    until keypressed;
    //show last readed number of samples
    for i:=Low(data) to High(data) do writeln('data[',i,'] = ', data[i]:4:2);

    //stop and clear task
    if Assigned(@AITaskHandle) then begin
      DAQmxStopTask(AITaskHandle);
      DAQmxClearTask(AITaskHandle);
    end;
end.

Analog output software controlled

This example demonstrates sinusoidal waveform generation with software timed control analog output.

program aochansw;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,Crt,SysUtils,nidaqmx
  { you can add units after this };
var
  AOTaskHandle:TaskHandle;
  //data writted to analog output
  data:array [0..999] of double;
  writtensamples:integer;
  i:integer;

 {************Error handling procedure *****************************************
    Example:
 ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);
 *******************************************************************************}
procedure ErrorCheck(AError:longint;ATaskHandle:longint);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if Aerror<>0 then begin
     DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
     writeln(PChar(@ErrBuffer[0]));
   //stop and cleat task
   if Assigned(@ATaskHandle) then begin
     DAQmxStopTask(ATaskHandle);
     DAQmxClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;

begin
  //prepare data for analog output
  //sin waveform
  for i:=Low(data) to High(data) do data[i]:=9.95*sin(i*2.0*Pi/1000.0);
  //create task
  DAQmxCreateTask('',@AOTaskHandle);
  //configure analog output channel
  ErrorCheck(DAQmxCreateAOVoltageChan(AOTaskHandle,'Dev1/ao0','',-10.0,10.0,DAQmx_Val_Volts,Nil),AOTaskHandle);
  //start task
  ErrorCheck(DAQmxStartTask(AOTaskHandle),AOTaskHandle);

  writeln('Press any key to stop generation');
  //continuosly waveform generation
  //if you call task start function (DAQmxStartTask)
  //before output write function (for example DAQmxWriteAnalogScalarF64)
  //set AutoStart parameter to 1
  //if output function berofe start AutoStart = 0
  i:=0;
  repeat
    sleep(1);
    ErrorCheck(DAQmxWriteAnalogScalarF64(AOTaskHandle,1,10.0,data[i],Nil),AOTaskHandle);
    inc(i);
    if i >=1000 then i:=0;
  until keypressed;

  //stop and clear task
  if Assigned(@AOTaskHandle) then begin
    DAQmxStopTask(AOTaskHandle);
    DAQmxClearTask(AOTaskHandle);
  end;
end.

Analog output hardware controlled

This example demonstrates how to generate finite number of samples on analog output channel.

program aochannel;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx
  { you can add units after this };

var
  AOTaskHandle:TaskHandle;
  //data writted to analog output
  data:array [0..3999] of double;
  writtensamples:integer;
  i:integer;

 {************Error handling procedure *****************************************
    Example:
 ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

*******************************************************************************}

procedure ErrorCheck(AError:longint;ATaskHandle:longint);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if Aerror<>0 then begin
     DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
     writeln(PChar(@ErrBuffer[0]));
   //stop and cleat task
   if Assigned(@ATaskHandle) then begin
     DAQmxStopTask(ATaskHandle);
     DAQmxClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;

{*********************MAIN PROGRAM*********************************************
 }
begin
  //prepare data for analog output
  for i:=Low(data) to High(data) do data[i]:= 5.0*i/4000.0;
  //create task
  DAQmxCreateTask('',@AOTaskHandle);
  //configure analog output channel
  ErrorCheck(DAQmxCreateAOVoltageChan(AOTaskHandle,'Dev1/ao0','',-10.0,10.0,DAQmx_Val_Volts,Nil),AOTaskHandle);
  //configure clock
  ErrorCheck(DAQmxCfgSampClkTiming(AOTaskHandle,'',1000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,4000),AOTaskHandle);
  //write data to buffer
  ErrorCheck(DAQmxWriteAnalogF64(AOTaskHandle,4000,0,10.0,DAQmx_Val_GroupByChannel,data,@writtensamples,Nil),AOTaskHandle);
  //start task
  ErrorCheck(DAQmxStartTask(AOTaskHandle),AOTaskHandle);
  //wait until all samples are generated
  ErrorCheck(DAQmxWaitUntilTaskDone(AOTaskHandle,10.0),AOTaskHandle);
  //stop and clear task
  if Assigned(@AOTaskHandle) then begin
    DAQmxStopTask(AOTaskHandle);
    DAQmxClearTask(AOTaskHandle);
  end;
end.

DIGITAL INPUTS/OUTPUTS

Most of DAQ cards have two types of digital ports (input/output): correlated and static. Static ports are software timed, correlated are software or hardware timed by the external or internal clock for example couter output signal (/Dev1/Ctr0InternalOutput - couter0 as clock ). Check the documention to find it, for example NI-PCI6250 have three 8-bit ports: port0 - correlated, port1,port2 - static; NI-PCI6224 two ports: 32-bit port0 - correlated, 8-bit port1 - static.

Read/Write data from/to 8-bit and 32-bit digital port use an appropriate function DAQmxReadDigitalU8, DAQmxWriteDigitalU8 and DAQmxReadDigitalU32, DAQmxWriteDigitalU32.

Different behaviour of collerated port0, when it sets as input, occured under Linux and Windows: software timed works only under Windows, with external clock works under Windows and Linux!!!

Software timed digital input

To check it, connected +5V(depend on hardware) to one of available port pins.

program diport;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils,StrUtils
  { you can add units after this };
var
  DITaskHandle:TaskHandle;
  data:array [0..9] of byte;
  reads:longint = 0;
  totalreads:longint = 0;
  i:longint ;
{************Error handling procedure *****************************************
      Example:
   ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

  *******************************************************************************}

  procedure ErrorCheck(AError:longint;ATaskHandle:longint);
  var
    errBuffer:array[0..2048] of char;
  begin
     //get error description
     if Aerror<>0 then begin
       DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
       writeln(PChar(@ErrBuffer[0]));
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop program
     Halt;
    end;
  end;
{*******************MAIN PROGRAM WITH ERROR HANDLING*************************}
begin
  //task create
  ErrorCheck(DAQmxCreateTask('',@DITaskHandle),DITaskHandle);
  //digital input port create
  ErrorCheck(DAQmxCreateDIChan(DITaskHandle,'Dev1/port1','',DAQmx_Val_ChanForAllLines),DITaskHandle);
  //Start Task
  ErrorCheck(DAQmxStartTask(DITaskHandle),DITaskHandle);
  //read 10 samples from 8-bit digital port (for example NI PCI-6250 card port1(pins P1.0-P1.7))
  //for 32-bit digital port use DAQmxReadDigitalU32 function
  ErrorCheck(DAQmxReadDigitalU8(DITaskHandle,10,10.0,DAQmx_Val_GroupByChannel,@data,10,@reads,Nil),DITaskHandle);
  writeln('number of readed samples: ',reads);
  for i:=0 to 9 do writeln('data [',i,'] = %',IntToBin(data[i],8));
  //stop and clear task
  if DITaskHandle<>0 then begin
    DAQmxStopTask(DITaskHandle);
    DAQmxClearTask(DITaskHandle);
  end;
end.

Output example: +5V connected to pin P1.2 port1 on NI-PCI6250 card.

number of readed samples: 10
data [0] = %00000100
data [1] = %00000100
data [2] = %00000100
data [3] = %00000100
data [4] = %00000100
data [5] = %00000100
data [6] = %00000100
data [7] = %00000100
data [8] = %00000100
data [9] = %00000100

Hardware timed digital input

This example shows using of counter output signal as a clock for digital inputs port for hardware timed acquire data on digital inputs. This feature is supported only for correlated digital port. In this example 8-bit port0 on NI-PCI6250 card is used to check this function.

To check it, connected +5V(depend on hardware) to one of available pins on correlated digital port.

program diporttimed;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils,StrUtils
  { you can add units after this };
var
  DITaskHandle,CtrTaskHandle:TaskHandle;
  data:array [0..9] of byte;
  reads:longint = 0;
  totalreads:longint = 0;
  i:longint ;
{************Error handling procedure *****************************************
      Example:
   ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

  *******************************************************************************}

  procedure ErrorCheck(AError:longint;ATaskHandle:longint);
  var
    errBuffer:array[0..2048] of char;
  begin
     //get error description
     if Aerror<>0 then begin
       DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
       writeln(PChar(@ErrBuffer[0]));
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop program
     Halt;
    end;
  end;
{*******************MAIN PROGRAM WITH ERROR HANDLING*************************}
begin
  {******CREATE COUNTER OUTPUT TO USE IT AS CLOCK FOR DIGITAL PORT INPUTS******
  *******INTERNALLY CONNECTED ON M-SERIES as /Dev1/Ctr0InternalOutput**********}
  //create couter task
  ErrorCheck(DAQmxCreateTask('Counter Task',@CtrTaskHandle),CtrTaskHandle);
  //create counter output channel
  //Frequency 1000 Hz, duty 0.5
  ErrorCheck(DAQmxCreateCOPulseChanFreq(CtrTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,1000,0.5),CtrTaskHandle);
  //timing 1000 samples per second
  ErrorCheck(DAQmxCfgImplicitTiming(CtrTaskHandle,DAQmx_Val_ContSamps,1000),CtrTaskHandle);
  //start counter task
  ErrorCheck(DAQmxStartTask(CtrTaskHandle),CtrTaskHandle);

  //digital input task create
  ErrorCheck(DAQmxCreateTask('Digital input task',@DITaskHandle),DITaskHandle);
  //digital input port create
  ErrorCheck(DAQmxCreateDIChan(DITaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DITaskHandle);
  //timing use counter output as clock source internally connected
  //as /Dev1/Ctr0InternalOutput
  ErrorCheck(DAQmxCfgSampClkTiming(DITaskHandle,'/Dev1/Ctr0InternalOutput',1000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000),DITaskHandle);
  //Start digital input Task
  ErrorCheck(DAQmxStartTask(DITaskHandle),DITaskHandle);
  //read 10 samples from 8-bit digital port for example correlated port0 
  //for 32-bit digital port use DAQmxReadDigitalU32 function
  ErrorCheck(DAQmxReadDigitalU8(DITaskHandle,10,10.0,DAQmx_Val_GroupByChannel,@data,10,@reads,Nil),DITaskHandle);
  writeln('number of readed samples: ',reads);
  for i:=0 to 9 do writeln('data [',i,'] = %',IntToBin(data[i],8));
  //stop and clear task
  DAQmxStopTask(DITaskHandle);
  DAQmxClearTask(DITaskHandle);
  DAQmxStopTask(CtrTaskHandle);
  DAQmxClearTask(CtrTaskHandle); 
end.

Output example: +5V connected to pin P0.5 on port0 on NI-PCI6250 card.

number of readed samples: 10
data [0] = %00100000
data [1] = %00100000
data [2] = %00100000
data [3] = %00100000
data [4] = %00100000
data [5] = %00100000
data [6] = %00100000
data [7] = %00100000
data [8] = %00100000
data [9] = %00100000

Software controlled digital output

To check it connect digital output(s) to oscilloscope or to card analog input.

program doportlpi;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils,StrUtils
  { you can add units after this };
var
  DOTaskHandle:TaskHandle;
  data: byte;
  written:longint = 0;
{************Error handling procedure *****************************************
      Example:
   ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev0/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

  *******************************************************************************}

  procedure ErrorCheck(AError:longint;ATaskHandle:longint);
  var
    errBuffer:array[0..2048] of char;
  begin
     //get error description
     if Aerror<>0 then begin
       DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
       writeln(PChar(@ErrBuffer[0]));
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop program
     Halt;
    end;
  end;
{*******************MAIN PROGRAM WITH ERROR HANDLING*************************}
begin
  //task create
  ErrorCheck(DAQmxCreateTask('',@DOTaskHandle),DOTaskHandle);
  //digital input port create
  ErrorCheck(DAQmxCreateDOChan(DOTaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DOTaskHandle);
  //task start
  //no need to start task
  ErrorCheck(DAQmxStartTask(DOTaskHandle),DOTaskHandle);
  //for 32-bit digital port use DAQmxReadDigitalU32 function
  //set 1 on all lines of digital port
  //change data:=0 to set 0 or use DAQmxResetDevice function to
  data:=0;
  ErrorCheck(DAQmxWriteDigitalU8(DOTaskHandle,1,1,10.0,DAQmx_Val_GroupByChannel,@data,@written,Nil),DOTaskHandle);
  //use DAQmxResetDevice function here to set signal on Low state
  writeln('number of written samples: ',written);
  //stop and clear task
  if DOTaskHandle<>0 then begin
    DAQmxStopTask(DOTaskHandle);
    DAQmxClearTask(DOTaskHandle);
  end;
end.

Hardware timed digital outputs with counter output used as source clock

To check it connect digital output(s) to oscilloscope or to card analog input.

program doporttimed;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils,StrUtils,Crt
  { you can add units after this };
var
  DOTaskHandle,CtrTaskHandle:TaskHandle;
  data:array[0..7] of byte = (1,2,4,8,16,32,64,128);
  written:longint = 0;
{************Error handling procedure *****************************************
      Example:
   ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev0/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

  *******************************************************************************}

  procedure ErrorCheck(AError:longint;ATaskHandle:longint);
  var
    errBuffer:array[0..2048] of char;
  begin
     //get error description
     if Aerror<>0 then begin
       DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
       writeln(PChar(@ErrBuffer[0]));
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop program
     Halt;
    end;
  end;
{*******************MAIN PROGRAM WITH ERROR HANDLING*************************}
begin
  {******CREATE COUNTER OUTPUT TO USE IT AS CLOCK FOR DIGITAL PORT OUTPUTS******
  *******INTERNALLY CONNECTED ON M-SERIES as /Dev1/Ctr0InternalOutput**********}
  //create couter task
  ErrorCheck(DAQmxCreateTask('Counter Task',@CtrTaskHandle),CtrTaskHandle);
  //create counter output channel
  //Frequency 1000 Hz, duty 0.5
  ErrorCheck(DAQmxCreateCOPulseChanFreq(CtrTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,10000,0.5),CtrTaskHandle);
  //timing 1000 samples per second
  ErrorCheck(DAQmxCfgImplicitTiming(CtrTaskHandle,DAQmx_Val_ContSamps,1000),CtrTaskHandle);
  //start task
  //start counter task
  ErrorCheck(DAQmxStartTask(CtrTaskHandle),CtrTaskHandle);

  //task create
  ErrorCheck(DAQmxCreateTask('',@DOTaskHandle),DOTaskHandle);
  //digital input port create
  ErrorCheck(DAQmxCreateDOChan(DOTaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DOTaskHandle);
  //timing
  //frequency 1000Hz
  ErrorCheck(DAQmxCfgSampClkTiming(DOTaskHandle,'/Dev1/Ctr0InternalOutput',1000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000),DOTaskHandle);
  //for 32-bit digital port use DAQmxWriteDigitalU32 function
  //function sets to start task automaticly 3-rd parameter
  //write 8 samples
  ErrorCheck(DAQmxWriteDigitalU8(DOTaskHandle,8,1,10.0,DAQmx_Val_GroupByChannel,data,@written,Nil),DOTaskHandle);
  writeln('number of written samples: ',written);
  //stop and clear task
  DAQmxStopTask(DOTaskHandle);
  DAQmxClearTask(DOTaskHandle);
  DAQmxStopTask(CtrTaskHandle);
  DAQmxClearTask(CtrTaskHandle);
end.

COUNTER INPUTS/OUTPUTS

Pulse generation

This example demonstrates pwm generaton with frequency 1000Hz and duty circle 0.5.

program doporttimed;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils,StrUtils,Crt
  { you can add units after this };
var
 CtrTaskHandle:TaskHandle;
 {************Error handling procedure *****************************************
      Example:
   ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev0/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

  *******************************************************************************}

  procedure ErrorCheck(AError:longint;ATaskHandle:longint);
  var
    errBuffer:array[0..2048] of char;
  begin
     //get error description
     if Aerror<>0 then begin
       DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
       writeln(PChar(@ErrBuffer[0]));
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop program
     Halt;
    end;
  end;
{*******************MAIN PROGRAM WITH ERROR HANDLING*************************}
begin
  //create couter task
  ErrorCheck(DAQmxCreateTask('Counter Task',@CtrTaskHandle),CtrTaskHandle);
  //create counter output channel
  //Frequency 1000 Hz, duty 0.5
  ErrorCheck(DAQmxCreateCOPulseChanFreq(CtrTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,10000,0.5),CtrTaskHandle);
  //timing 1000 samples per second
  ErrorCheck(DAQmxCfgImplicitTiming(CtrTaskHandle,DAQmx_Val_ContSamps,1000),CtrTaskHandle);
  //start task
  ErrorCheck(DAQmxStartTask(CtrTaskHandle),CtrTaskHandle);
   
  writeln('Press any key to stop generation');
  repeat
  until keypressed;  
  
  DAQmxStopTask(CtrTaskHandle);
  DAQmxClearTask(CtrTaskHandle);
end.

Event counting

In this example first counter(Dev1/ctr0) is configurated as counter output for frequency generation, second (Dev1/ctr1) as counter input for pulse counting. First counter also used as clock source for second one.

program eventcounting;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmx,SysUtils,StrUtils,Crt
  { you can add units after this };
var
  CtrOutTaskHandle,CtrInTaskHandle:TaskHandle;
  reads:cardinal=0;
  data:array [0..999] of longint;
  i:longint;
{************Error handling procedure *****************************************
      Example:
   ErrorCheck(DAQmxCreateAIVoltageChan(AITaskHandle,'Dev0/ai0','',DAQmx_Val_RSE,-10.0,10.0,DAQmx_Val_Volts,NIL),AITaskHandle);

  *******************************************************************************}
  procedure ErrorCheck(AError:longint;ATaskHandle:longint);
  var
    errBuffer:array[0..2048] of char;
  begin
     //get error description
     if Aerror<>0 then begin
       DAQmxGetExtendedErrorInfo(@ErrBuffer[0],2048);
       writeln(PChar(@ErrBuffer[0]));
     //stop and cleat task
     if Assigned(@ATaskHandle) then begin
       DAQmxStopTask(ATaskHandle);
       DAQmxClearTask(ATaskHandle);
     end;
     //stop program
     Halt;
    end;
  end;
{*******************MAIN PROGRAM WITH ERROR HANDLING*************************}
begin
  {****************CREATE COUNTER OUTPUT***************************************}
  //create couter output task
  ErrorCheck(DAQmxCreateTask('Counter output task',@CtrOutTaskHandle),CtrOutTaskHandle);
  //create counter output channel
  //Frequency 1000 Hz, duty 0.5
  ErrorCheck(DAQmxCreateCOPulseChanFreq(CtrOutTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,10000,0.5),CtrOutTaskHandle);
  //timing 1000 samples per second
  ErrorCheck(DAQmxCfgImplicitTiming(CtrOutTaskHandle,DAQmx_Val_ContSamps,1000),CtrOutTaskHandle);
  //start counter output task
  ErrorCheck(DAQmxStartTask(CtrOutTaskHandle),CtrOutTaskHandle);

  {*****************CREATE COUNTER INPUT***************************************}
  //create couter output task
  ErrorCheck(DAQmxCreateTask('Counter input task',@CtrInTaskHandle),CtrInTaskHandle);
  //create input counter for pulse counting
  ErrorCheck(DAQmxCreateCICountEdgesChan(CtrInTaskHandle,'Dev1/ctr1','',DAQmx_Val_Rising,0,DAQmx_Val_CountUp),CtrInTaskHandle);
  //timing
  ErrorCheck(DAQmxCfgSampClkTiming(CtrInTaskHandle,'/Dev1/Ctr0InternalOutput',1000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000),CtrInTaskHandle);
   //start counter input task
  ErrorCheck(DAQmxStartTask(CtrInTaskHandle),CtrInTaskHandle);
  //counting pulses
  ErrorCheck(DAQmxReadCounterU32(CtrIntaskHandle,1000,10.0,@data,1000,@reads,Nil),CtrIntaskHandle);
  writeln('acquired samples: ',reads);
  for i:=0 to 10 do writeln('sample[',i,'] = ',data[i]);

  //stop and clear tasks
  DAQmxStopTask(CtrInTaskHandle);
  DAQmxClearTask(CtrInTaskHandle);
  DAQmxStopTask(CtrOutTaskHandle);
  DAQmxClearTask(CtrOutTaskHandle);
end.

TESTED HARDWARE

Examples were tested under Linux 32-bit (kernel 2.6.32) NI-DAQmx 8.0.2 and Windows 7 32-bit NI-DAQmx 14 on cards:

  • NI PCI-6250
  • NI PCI-6224
  • NI PXIe-6612 (Windows only)
  • NI USB-6356 (Windows only)
  • NI USB-6003 (Windows only)

NI-DAQxmxBase examples

Generally, NI-DAQmxBase is a light version of NI-DAQmx, but this library is multiplatform. You can use it under all supported operation systems without modification of NI-DAQmxBase function call.

Read readme file to check card supported features!!!

DEVICE INFO

Functions, which provide device and channel information, are not implemented in this library, yet.

ANALOG INPUTS/OUTPUTS

Acquire finite number of samples from one analog channel

program aichannel;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase;
  { you can add units after this }
var
   AITaskHandle: PTaskHandle;
   NiBool:Pnibool;
   reads:longint=0;
   i:byte;
   data:array [1..1000] of double;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
  errMessage:string;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;

begin
  //create task
  ErrorCheck(DAQmxBaseCreateTask ('', AITaskHandle),AITaskHandle);
  //create analog input channel
  ErrorCheck(DAQmxBaseCreateAIVoltageChan(AITaskHandle,'Dev1/ai0','', DAQmx_Val_RSE,-10, 10,DAQmx_Val_Volts,''),AITaskHandle);
  //timing
  //clock frequency 1000 Hz, sample rate 1000 per second
  //if you use more than one active channel, generally sample rate = clock frequency/(active analog channles*10);
  ErrorCheck(DAQmxBaseCfgSampClkTiming (AITaskHandle,'OnboardClock',1000,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000),AITaskHandle);
  //start task
  ErrorCheck(DAQmxBaseStartTask(AITaskHandle),AITaskHandle);
  //read 1000 samples
  ErrorCheck(DAQmxBaseReadAnalogF64 (AITaskHandle, 1000, 10.0,DAQmx_Val_GroupByChannel,data,1000,reads,NIBool^),AITaskHandle);
  //acquired sample number
  writeln(reads,' samples were acqiured');
  //show value of first ten samples
  for i:=1 to 10 do writeln('sample[',i,'] = ',data[i]:4:2,' V');
  //stop and clear task
  DAQmxBaseStopTask(AITaskHandle);
  DAQmxBaseClearTask(AITaskHandle);
end.

Output example +5V connected to pin 68 on NI PCI-6250

1000 samples were acqiured
sample[1] = 5.07 V
sample[2] = 5.06 V
sample[3] = 5.06 V
sample[4] = 5.06 V
sample[5] = 5.06 V
sample[6] = 5.06 V
sample[7] = 5.06 V
sample[8] = 5.06 V
sample[9] = 5.06 V
sample[10] = 5.07 V

Continuously acquire samples from three analog inputs

program ai3contacq;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase;
  { you can add units after this }
var
   AITaskHandle: PTaskHandle;
   NiBool:Pnibool;
   reads:longint=0;
   totalreads:longint=0;
   i:byte;
   data:array of double;
   acqnumsamples:longint;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
  errMessage:string;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;

begin
  //enter number of total samples for each channel to be acquired
  write('Enter a number of samples for each channel to be acquired: ');
  readln(acqnumsamples);
  //Set buffer size = length of data array:=number of active channels * sample per channel
  Setlength(data,3*1000);
  //create task
  ErrorCheck(DAQmxBaseCreateTask ('', AITaskHandle),AITaskHandle);
  //create analog input channel - 3 active analog inputs
  ErrorCheck(DAQmxBaseCreateAIVoltageChan(AITaskHandle,'Dev1/ai0:2','', DAQmx_Val_RSE,-10, 10,DAQmx_Val_Volts,''),AITaskHandle);
  //timing
  //clock frequency 10000 Hz, sample rate 1000 per second
  //if you use more than one active channel, generally sample rate = clock frequency/(active analog channles*10);
  ErrorCheck(DAQmxBaseCfgSampClkTiming (AITaskHandle,'OnboardClock',10000,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000),AITaskHandle);
  //start task
  ErrorCheck(DAQmxBaseStartTask(AITaskHandle),AITaskHandle);
  //read samples
  while totalreads<acqnumsamples do begin
    ErrorCheck(DAQmxBaseReadAnalogF64(AITaskHandle, 1000, 10.0, DAQmx_Val_GroupByChannel,data,Length(data),reads,NIBool^),AITaskHandle);
    totalreads:=totalreads+reads;
    writeln('total samples readed for each channel: ',totalreads);
    //show first five values for each channel
    for i:=0 to 5 do writeln('chan1[',i,'] = ',data[i]:4:2,' V ','chan2[',i,'] = ',data[i+1000]:4:2,' V ','chan3[',i,'] = ',data[i+2000]:4:2,' V ');
  end;
  //stop and clear task
  DAQmxBaseStopTask(AITaskHandle);
  DAQmxBaseClearTask(AITaskHandle);
end.

Output example +5V connected to pin 68 on NI PCI-6250

Enter a number of samples for each channel to be acquired: 2000
total samples readed for each channel: 1000
chan1[0] = 5.07 V chan2[0] = 0.00 V chan3[0] = 0.03 V
chan1[1] = 5.06 V chan2[1] = -0.00 V chan3[1] = 0.03 V
chan1[2] = 5.07 V chan2[2] = -0.00 V chan3[2] = 0.02 V
chan1[3] = 5.07 V chan2[3] = -0.00 V chan3[3] = 0.02 V
chan1[4] = 5.06 V chan2[4] = -0.00 V chan3[4] = 0.02 V
chan1[5] = 5.06 V chan2[5] = -0.00 V chan3[5] = 0.01 V
total samples readed for each channel: 2000
chan1[0] = 5.06 V chan2[0] = -0.00 V chan3[0] = -0.02 V
chan1[1] = 5.07 V chan2[1] = -0.00 V chan3[1] = -0.02 V
chan1[2] = 5.06 V chan2[2] = -0.00 V chan3[2] = -0.02 V
chan1[3] = 5.07 V chan2[3] = -0.00 V chan3[3] = -0.02 V
chan1[4] = 5.06 V chan2[4] = -0.00 V chan3[4] = -0.02 V
chan1[5] = 5.06 V chan2[5] = -0.00 V chan3[5] = -0.02 V

DIGITAL INPUTS/OUTPUTS

Read readme file to check supported card features by NI-DAQmxBase.

In case of M-series cards, correlated digital input/output is limited to about 100kHz, because DMA data transfer is not supported.

Software timed digital input

program diport;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase,StrUtils
  { you can add units after this };
var
  DITaskHandle:PTaskHandle;
  NiBool:Pnibool;
  data:byte;
  reads:longint=0;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
begin
  //create task
  ErrorCheck(DAQmxBaseCreateTask('', DITaskHandle),DITaskHandle);
  //create digital input port
  ErrorCheck(DAQmxBaseCreateDIChan(DITaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DITaskHandle);
  //start task
  ErrorCheck(DAQmxBaseStartTask(DITaskHandle),DITaskHandle);
  //read dat form port
  //use DAQmxBaseReadDigitalU8 for 8 bit port
  //use DAQmxBaseReadDigitalU32 for 32 bit port
  ErrorCheck(DAQmxBaseReadDigitalU8(DITaskHandle,1,10.0,DAQmx_Val_GroupByChannel,data,1,reads,NiBool^),DITaskHandle);
  writeln('digital port value %',IntToBin(data,8));
  //stop and clear task
  DAQmxBaseStopTask(DITaskHandle);
  DAQmxBaseClearTask(DITaskHandle);
end.

Output example: +5V connected to line 1 of 8-bit digital port

digital port value %00000010

Hardware timed digital input

program diporthwtimed;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase,StrUtils
  { you can add units after this };
var
  DITaskHandle,CtrTaskHandle:PTaskHandle;
  NiBool:Pnibool;
  data:array of byte;
  reads:longint=0;
  i:longint;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
begin

  {********CONFIGURE COUNTER TO USE AS CLOCK FOR DIGITAL INPUT*****************}
  //create counter task
  ErrorCheck(DAQmxBaseCreateTask('', CtrTaskHandle),CtrTaskHandle);
  //create counter output channel
  //Frequency 10000 Hz, duty 0.5
  ErrorCheck(DAQmxBaseCreateCOPulseChanFreq(CtrTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,10000,0.5),CtrTaskHandle);
  //timing
  //last value is not important for continuesly sample mode, for finite sample mode = generate 10000 samples
  ErrorCheck(DAQmxBaseCfgImplicitTiming(CtrTaskHandle,DAQmx_Val_ContSamps,10000),CtrTaskHandle);
  //start counter task
  ErrorCheck(DAQmxBaseStartTask(CtrTaskHandle),CtrTaskHandle);


  {*******************HW TIMED DIGITAL INPUT***********************************
  **********LIMITED TO 10kHz,DMA IS NOT IMPLEMENTED****************************}
  //configure buffer for 1000 samples
  SetLength(data,1000);
  //create task
  ErrorCheck(DAQmxBaseCreateTask('', DITaskHandle),DITaskHandle);
  //create digital input port
  ErrorCheck(DAQmxBaseCreateDIChan(DITaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DITaskHandle);
  //timing frequency 10000Hz, 1000 sample per channel
  ErrorCheck(DAQmxBaseCfgSampClkTiming(DITaskHandle,'/Dev1/Ctr0InternalOutput',10000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,10000),DITaskHandle);
  //start task
  ErrorCheck(DAQmxBaseStartTask(DITaskHandle),DITaskHandle);
  //read dat form port
  //use DAQmxBaseReadDigitalU8 for 8 bit port
  //use DAQmxBaseReadDigitalU32 for 32 bit port
  ErrorCheck(DAQmxBaseReadDigitalU8(DITaskHandle,10000,10.0,DAQmx_Val_GroupByChannel,data,10000,reads,NiBool^),DITaskHandle);
  writeln(reads,' samples were readed');
  //show first 10 samples
  for i:=0 to 9 do writeln('sample[',i,'] = %',IntToBin(data[i],8));
  //stop and clear task
  DAQmxBaseStopTask(DITaskHandle);
  DAQmxBaseClearTask(DITaskHandle);
  DAQmxBaseStopTask(CtrTaskHandle);
  DAQmxBaseClearTask(CtrTaskHandle);
end.

Output example: +5V connected to line 1 of 8-bit digital port, executed with time command under Linux.

10000 samples were readed                                      
sample[0] = %00000010                                          
sample[1] = %00000010                                          
sample[2] = %00000010
sample[3] = %00000010
sample[4] = %00000010
sample[5] = %00000010
sample[6] = %00000010
sample[7] = %00000010
sample[8] = %00000010
sample[9] = %00000010

real    0m4.483s
user    0m4.261s
sys     0m0.100s

Software timed digital output

program doport;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase,StrUtils,SysUtils
  { you can add units after this };
var
  DOTaskHandle,CtrTaskHandle:PTaskHandle;
  NiBool:Pnibool;
  data: byte;
  written:longint=0;
  i:longint;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
begin
  //write data to buffer, produce digital waveform on all port lines
  data:=255;
  //create task
  ErrorCheck(DAQmxBaseCreateTask('', DOTaskHandle),DOTaskHandle);
  //create digital input port
  ErrorCheck(DAQmxBaseCreateDOChan(DOTaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DOTaskHandle);
  //use DAQmxBaseWriteDigitalU8 for 8 bit port
  //use DAQmxBaseWriteDigitalU32 for 32 bit port
  //set third parametr to 1 to enable autostart or set to 0 and use DAQmxBaseStartTask before
  //set digital "1"
  writeln('set digital 1 on all port lines');
  writeln('sleep 10s');
  ErrorCheck(DAQmxBaseWriteDigitalU8(DOTaskHandle,1000,1,1000,DAQmx_Val_GroupByChannel,data,written,NiTrue),DOTaskHandle);
  sleep(10000);
  writeln(written,' samples were written');
  //set digital "0"
  writeln('set digital 0 on all port lines');
  writeln('sleep 10s');
  sleep(10000);
  ErrorCheck(DAQmxBaseWriteDigitalU8(DOTaskHandle,1000,1,1000,DAQmx_Val_GroupByChannel,data,written,NiTrue),DOTaskHandle);
  writeln(written,' samples were written');
  //stop and clear task
  DAQmxBaseStopTask(DOTaskHandle);
  DAQmxBaseClearTask(DOTaskHandle);
  DAQmxBaseStopTask(CtrTaskHandle);
  DAQmxBaseClearTask(CtrTaskHandle);
end.

Hardware timed digital output

Digital output generation frequency is limited to ~100kHz, because DMA is not supported by the driver .

program doporthwtimed;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase,StrUtils
  { you can add units after this };
var
  DOTaskHandle,CtrTaskHandle:PTaskHandle;
  NiBool:Pnibool;
  data:array of byte;
  written:longint=0;
  i:longint;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
begin
  {********CONFIGURE COUNTER TO USE AS CLOCK FOR DIGITAL INPUT*****************}
  //create counter task
  ErrorCheck(DAQmxBaseCreateTask('', CtrTaskHandle),CtrTaskHandle);
  //create counter output channel
  //Frequency 10000 Hz, duty 0.5
  ErrorCheck(DAQmxBaseCreateCOPulseChanFreq(CtrTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,10000,0.5),CtrTaskHandle);
  //timing
  //last value is not important for continuesly sample mode, for finite sample mode = generate 10000 samples
  ErrorCheck(DAQmxBaseCfgImplicitTiming(CtrTaskHandle,DAQmx_Val_ContSamps,10000),CtrTaskHandle);
  //start counter task
  ErrorCheck(DAQmxBaseStartTask(CtrTaskHandle),CtrTaskHandle);
  {*******************HW TIMED DIGITAL INPUT***********************************
  **********LIMITED TO 10kHz,DMA IS NOT IMPLEMENTED****************************}
  //configure buffer for 1000 samples
  SetLength(data,1000);
  //write data to buffer, produce digital waveform on all port lines
  for i:=0 to 999 do if (i mod 2 = 0) then data[i]:=255;
  //create task
  ErrorCheck(DAQmxBaseCreateTask('', DOTaskHandle),DOTaskHandle);
  //create digital input port
  ErrorCheck(DAQmxBaseCreateDOChan(DOTaskHandle,'Dev1/port0','',DAQmx_Val_ChanForAllLines),DOTaskHandle);
  //timing frequency 10000Hz, 1000 sample per channel
  ErrorCheck(DAQmxBaseCfgSampClkTiming(DOTaskHandle,'/Dev1/Ctr0InternalOutput',10000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000),DOTaskHandle);
  //use DAQmxBaseWriteDigitalU8 for 8 bit port
  //use DAQmxBaseWriteDigitalU32 for 32 bit port
  //set third parametr to 1 to enable autostart or set to 0 and use DAQmxBaseStartTask before
  ErrorCheck(DAQmxBaseWriteDigitalU8(DOTaskHandle,1000,1,1000,DAQmx_Val_GroupByChannel,data,written,NiTrue),DOTaskHandle);
  writeln(written,' samples were written');
  //stop and clear task
  DAQmxBaseStopTask(DOTaskHandle);
  DAQmxBaseClearTask(DOTaskHandle);
  DAQmxBaseStopTask(CtrTaskHandle);
  DAQmxBaseClearTask(CtrTaskHandle);
end.

COUNTER INPUTS/OUTPUTS

Counter input of M-series cards is limited to computer speed.

COUNTER OUTPUT

Counter output can be timed by software or hardware.

Example demontrate how to use couter ouput as frequency generator(clock signal for other hardware) or as basic pwm regulation.

In this example counter output is hardware timed.

program ctrout;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase,StrUtils,SysUtils
  { you can add units after this };
var
  CtrTaskHandle:PTaskHandle;
  NiBool:Pnibool;
  data:array of byte;
  reads:longint=0;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
begin

  {********CONFIGURE COUNTER TO USE AS CLOCK FOR DIGITAL INPUT*****************}
  //create counter task
  ErrorCheck(DAQmxBaseCreateTask('', CtrTaskHandle),CtrTaskHandle);
  //create counter output channel
  //Frequency 10000 Hz, duty 0.5
  ErrorCheck(DAQmxBaseCreateCOPulseChanFreq(CtrTaskHandle,'Dev1/ctr0','',DAQmx_Val_Hz,DAQmx_Val_Low,0,10000,0.5),CtrTaskHandle);
  //timing
  //last value is not important for continuesly sample mode, for finite sample mode = generate 10000 samples
  ErrorCheck(DAQmxBaseCfgImplicitTiming(CtrTaskHandle,DAQmx_Val_ContSamps,10000),CtrTaskHandle);
  //start counter task
  ErrorCheck(DAQmxBaseStartTask(CtrTaskHandle),CtrTaskHandle);
  //generate frequency signal for 10 seconds
  sleep(10000);
  DAQmxBaseStopTask(CtrTaskHandle);
  DAQmxBaseClearTask(CtrTaskHandle);
end.

COUNTER INPUT

Counter input is only software operated. It is limited by hardware driver.

program ctrin;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,nidaqmxbase,StrUtils,SysUtils,Crt
  { you can add units after this };
var
  CtrInTaskHandle:PTaskHandle;
  NiBool:Pnibool;
  data:dword=0;
  reads:longint=0;
procedure ErrorCheck(AError:longint;ATaskHandle:PTaskHandle);
var
  errBuffer:array[0..2048] of char;
begin
   //get error description
   if DAQmxFailed(AError) = niTrue then begin
     DAQmxBaseGetExtendedErrorInfo(ErrBuffer,2048);
     writeln(ErrBuffer);
   //stop and cleat task
   if Assigned(ATaskHandle) then begin
       DAQmxBaseStopTask(ATaskHandle);
       DAQmxBaseClearTask(ATaskHandle);
   end;
   //stop program
   Halt;
  end;
end;
begin
  {*****************CREATE COUNTER INPUT***************************************}
  //create couter input task
  ErrorCheck(DAQmxBaseCreateTask('Counter input task',CtrInTaskHandle),CtrInTaskHandle);
  //create input counter for pulse counting
  ErrorCheck(DAQmxBaseCreateCICountEdgesChan(CtrInTaskHandle,'Dev1/ctr1','',DAQmx_Val_Rising,0,DAQmx_Val_CountUp),CtrInTaskHandle);
  //timing
  //ErrorCheck(DAQmxBaseCfgSampClkTiming(CtrInTaskHandle,'/Dev1/Ctr0InternalOutput',1000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000),CtrInTaskHandle);
   //start counter input task
  ErrorCheck(DAQmxBaseStartTask(CtrInTaskHandle),CtrInTaskHandle);
  //counting pulses
  writeln('press any key to stop counting');
  repeat
    ErrorCheck(DAQmxBaseReadCounterU32(CtrIntaskHandle,1,10.0,data,1,reads,NiTrue),CtrIntaskHandle);
    writeln('counter value: ',data);
  until keypressed;
  //clear and stop task
  DAQmxBaseStopTask(CtrInTaskHandle);
  DAQmxBaseClearTask(CtrInTaskHandle);
end.

TESTED HARDWARE

Examples were tested under Scientific Linux 6.4, Mageia 2 32-bit and Windows 32-bit and should work under MacOS (need test) on cards:

  • NI PCI-6250
  • NI PCI-6224
  • NI USB-6356 (Windows only)