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 generally used for acquiring a generating signals. [1] These cards usually have a few numbers of analog inputs/outputs, digital inputs/ouputs, counters and frequency generator with hardware/software timing. Exiting of feature (type, number, parameters) on card depends on card type.

NIDAQmxBase.pas and NIDAQmx.pas are provided pascal bindings to National Instruments libraries and enabled control NI DAQ cards from program writing on FreePascal.

What National Instruments Hardware are supported

Supported hardware list by NI-DAQmx and NI-DAQ Driver for different operation system [2]

NI-DAQmxBase library

NI-DAQmxBase library or driver is multiplatform library for Linux, Windows and MacOS X, but doesn't implement all device features. For example, digital input/output ports speed is limited by computer speed ~100kHz, because DMA data tranfer is not supported.

Download library for Linux [3], Windows [4], MacOS X [5]. Read readme.txt file to find the list of supported hardware and hardware features.

NI-DAQmx library

Actual (newest) NI-DAQmx library is available only for Windows today.

Download it there [6]

NI-DAQmx version 8.0.2 is also available for Linux 32-bit. You can use it with enterprise RHEL 5,6 distribution or with it clones CentOS 5,6 and Scientific Linux 5,6 or you can use it with old version Linux distribution with kernel 2.6.x.

Download it there [7]

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

PASCAL bindings

Your can download pascal bindings nidaqmxbase.pas and nidaqmx.pas in this forum thread [8]

NI-DAQmx examples

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

Acquire finite 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

Continually acquire samples from 3 analog channels

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

aquired number of samples: 1000                                
aquired number of samples: 2000                                
aquired number of samples: 3000                                
aquired number of samples: 4000                                
aquired number of samples: 5000                                
aquired number of samples: 6000                                
aquired number of samples: 7000                                
aquired number of samples: 8000                                
aquired number of samples: 9000                                
aquired 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|

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 and hardware timed by the connection of external clock or internally 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.

To control 8-bit and 32-bit use an appropriate function DAQmxReadDigitalU8, DAQmxWriteDigitalU8 and DAQmxReadDigitalU8, DAQmxWriteDigitalU8.

Different behaviour of collerated port0 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 NI-PCI6250

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