LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

DAQmx Analog Input and Output in Binary

I am posting this in the Labview forum even though I am not using Labview because I have no earthly idea where else this question belongs. I have a working C implementation of what I am trying to accomplish with DAQmx already using the analog API for input and output on a voltage channel but I would like to accomplish the same using the binary API for input and output and my question centers around the proper initialization for doing such a thing (presumably it is not a voltage channel?). Despite my sincerest efforts I cannot seem to find examples of such anywhere on the internet, so I am resorting to asking this question on the boards here.

 

All that I am doing is reading a voltage from an analog input and writing a voltage to an analog output. The code for doing so is below:

 

  TaskHandle taskHandle_AI0 = 0;			// Analog Input 0 Task Handler
  TaskHandle taskHandle_AI1 = 0;			// Analog Input 1 Task Handler
  TaskHandle taskHandle_AO0 = 0;			// Analog Output 0 Task Handler
  TaskHandle taskHandle_AO1 = 0;			// Analog Output 1 Task Handler

  /*********************************************/
  // DAQmx Variable Declarations
  /*********************************************/
  
  float64     readValue_AI0;				// Number of samples read from Analog Input 0
  float64     readValue_AI1;				// Number of samples read from Analog Input 1
  
  /*********************************************/
  // DAQmx Configure Code
  /*********************************************/

  // Create the tasks for Analog Inputs 0 and 1, Analog Outputs 0 and 1
  DAQmxErrChk (DAQmxCreateTask("AnalogTaskIn0", &taskHandle_AI0));
  DAQmxErrChk (DAQmxCreateTask("AnalogTaskIn1", &taskHandle_AI1));
  DAQmxErrChk (DAQmxCreateTask("AnalogTaskOut0", &taskHandle_AO0));
  DAQmxErrChk (DAQmxCreateTask("AnalogTaskOut1", &taskHandle_AO1));

  // Create an analog INPUT virtual voltage channel and add it to the task
  DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle_AI0, // The task to add the channels that this function creates.
				      					"Dev1/ai0", // File Descriptor - The names of the physical channels to use to create virtual
 // channels. You can specify a list or range of physical channels.
				      					"", // The name to assign to this virtual channel (replaces the file descriptor
 // used above when referencing this channel in other functions)
				      					DAQmx_Val_RSE, // The input terminal configuration for the channel.
				      					-10.0, // The minimum value, in units, that you expect to measure (-10v minimum)
				      					10.0, // The maximum value, in units, that you expect to measure (10v maximum)
				      					DAQmx_Val_Volts, // The units to use to return the voltage measurements (Use volts)
				      					NULL)); // The name of a custom scale to apply to the channel.
				      					
  // Set OverWrite Mode for Acquisition Samples (Stop Acquisition if Unread Sample in Buffer)
  DAQmxErrChk (DAQmxSetReadOverWrite(taskHandle_AI0, DAQmx_Val_DoNotOverwriteUnreadSamps));

  // Set Read Wait Mode for Next Acquisition (Repeatedly Check for Available Samples as fast as possible)
  DAQmxErrChk (DAQmxSetReadWaitMode(taskHandle_AI0, DAQmx_Val_Poll));
				      					
  DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle_AI1, // The task to add the channels that this function creates.
				      					"Dev1/ai1", // File Descriptor - The names of the physical channels to use to create virtual
 // channels. You can specify a list or range of physical channels.
				      					"", // The name to assign to this virtual channel (replaces the file descriptor
 // used above when referencing this channel in other functions)
				      					DAQmx_Val_Cfg_Default, // The input terminal configuration for the channel.
				      					-10.0, // The minimum value, in units, that you expect to measure (-10v minimum)
				      					10.0, // The maximum value, in units, that you expect to measure (10v maximum)
				      					DAQmx_Val_Volts, // The units to use to return the voltage measurements (Use volts)
				      					NULL)); // The name of a custom scale to apply to the channel.
				      					
  // Create an analog OUTPUT virtual voltage channel and add it to the task
  DAQmxErrChk (DAQmxCreateAOVoltageChan(taskHandle_AO0, // The task to which to add the channels that this function creates.
										  "Dev1/ao0", // File Descriptor - The names of the physical channels to use to create virtual
 // channels. You can specify a list or range of physical channels.
										  "", // The name to assign to this virtual channel (replaces the file descriptor
 // used above when referencing this channel in other functions) 
										  -10.0, // The minimum value, in units, that you expect to generate (-10v minimum)
										  10.0, // The maximum value, in units, that you expect to generate (10v maximum)
										  DAQmx_Val_Volts, // The units to use to return the voltage measurements (Use volts)
										  NULL)); // The name of a custom scale to apply to the channel
										  
  DAQmxErrChk (DAQmxCreateAOVoltageChan(taskHandle_AO1, // The task to which to add the channels that this function creates.
										  "Dev1/ao1",			// File Descriptor - The names of the physical channels to use to create virtual
					      										// channels. You can specify a list or range of physical channels.
										  "",					// The name to assign to this virtual channel (replaces the file descriptor
					      										// used above when referencing this channel in other functions) 
										  -10.0,				// The minimum value, in units, that you expect to generate (-10v minimum)
										  10.0,					// The maximum value, in units, that you expect to generate (10v maximum)
										  DAQmx_Val_Volts,		// The units to use to return the voltage measurements (Use volts)
										  NULL));				// The name of a custom scale to apply to the channel.
				      
  // Sets the source of the Sample Clock, the rate of the Sample Clock, and the number of samples to acquire or generate.
  
  DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle_AI0, // The task used in this function.
				   					 NULL, // The source terminal of the Sample Clock.
 // To use the internal clock of the device, use NULL or use OnboardClock.
				   					 50000.0, // The sampling rate in samples per second per channel. If you use an external source
 // for the Sample Clock, set this value to the maximum expected rate of that clock. 
				   					 DAQmx_Val_Rising, // Specifies on which edge of the clock to acquire or generate samples.
				   					 DAQmx_Val_HWTimedSinglePoint, // Specifies whether the task acquires or generates samples continuously or if it
 // acquires or generates a finite number of samples.
				   					 1)); // The number of samples to acquire or generate for each channel in the task.
				   					 
	// Set Sample Clock Wait Mode for Next Sample Acquistion (Repeatedly Check for Sample Clock Pulses as fast as possible)
	DAQmxErrChk (DAQmxSetRealTimeWaitForNextSampClkWaitMode(taskHandle_AI0, DAQmx_Val_WaitForInterrupt));
/*				   					 
  DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle_AI1, // The task used in this function.
				   					 "", // The source terminal of the Sample Clock.
 // To use the internal clock of the device, use NULL or use OnboardClock.
				   					 10000.0, // The sampling rate in samples per second per channel. If you use an external source
 // for the Sample Clock, set this value to the maximum expected rate of that clock. 
				   					 DAQmx_Val_Rising, // Specifies on which edge of the clock to acquire or generate samples.
				   					 DAQmx_Val_FiniteSamps, // Specifies whether the task acquires or generates samples continuously or if it
 // acquires or generates a finite number of samples.
				   					 1)); // The number of samples to acquire or generate for each channel in the task.
  */

  /*********************************************/
  // DAQmx Start Code
  /*********************************************/

  // Start the tasks
  DAQmxErrChk (DAQmxStartTask(taskHandle_AI0));
  //DAQmxErrChk (DAQmxStartTask(taskHandle_AI1));
  DAQmxErrChk (DAQmxStartTask(taskHandle_AO0));
  DAQmxErrChk (DAQmxStartTask(taskHandle_AO1));

	/*********************************************/
	// DAQmx Read Code
	/*********************************************/
	
	// Reads multiple floating-point samples from a task that contains one or more analog input channels.
	DAQmxErrChk (DAQmxReadAnalogScalarF64(taskHandle_AI0, // The task to read samples from.
					1, // timeout - The amount of time, in seconds, to wait for the function to read the sample(s).
					&readValue_AI0, // OUTPUT - The sample read from the task.
					NULL)); // Reserved for future use - pass NULL.
/*				
	DAQmxErrChk (DAQmxReadAnalogScalarF64(taskHandle_AI1, // The task to read samples from.
					1, // timeout - The amount of time, in seconds, to wait for the function to read the sample(s).
					&readValue_AI1, // OUTPUT - The sample read from the task.
					NULL)); // Reserved for future use - pass NULL.
*/

// SOLVING SOME EQUATIONS WHERE THE READ IS THE INDEPENDENT VARIABLE

	// Writes multiple floating-point samples to a task that contains one or more analog output channels
	DAQmxErrChk (DAQmxWriteAnalogScalarF64(taskHandle_AO0, // The task to write samples to.
									 1, // autoStart - Specifies whether or not this function automatically starts
 // the task if you do not start it.
									 0, // The amount of time, in seconds, to wait for this function to write all the samples.
									 writeTest0, // 64-bit sample to write to the task.
									 NULL)); // Reserved for future use - pass NULL.
						 
	DAQmxErrChk (DAQmxWriteAnalogScalarF64(taskHandle_AO1, // The task to write samples to.
									 1, // autoStart - Specifies whether or not this function automatically starts
 // the task if you do not start it.
									 0, // The amount of time, in seconds, to wait for this function to write all the samples.
									 writeTest1, // 64-bit sample to write to the task.
									 NULL)); // Reserved for future use - pass NULL.

  /*********************************************/
  // DAQmx Stop Code
  /*********************************************/
  
  if(taskHandle_AI0 != 0)  {
    DAQmxStopTask(taskHandle_AI0);
    DAQmxClearTask(taskHandle_AI0);
  }
  if(taskHandle_AI1 != 0)  {
    DAQmxStopTask(taskHandle_AI1);
    DAQmxClearTask(taskHandle_AI1);
  }
  if(taskHandle_AO0 != 0)  {
    DAQmxStopTask(taskHandle_AO0);
    DAQmxClearTask(taskHandle_AO0);
  }
  if(taskHandle_AO1 != 0)  {
    DAQmxStopTask(taskHandle_AO1);
    DAQmxClearTask(taskHandle_AO1);
  }

 

The above works as I would expect. Now I would like to read and write in binary instead. Since this DAQ has an 16-bit input and 16-bit output the API calls should be:

 

   board_read = DAQmxReadBinaryU16(taskHandle_AI0, // The task to write samples to.
                                   1, // The number of samples, per channel, to read.
                                   DAQmx_Val_WaitInfinitely, // The amount of time, in seconds, to wait for the function to read the sample(s).
                                   DAQmx_Val_GroupByScanNumber, // Specifies whether or not the samples are interleaved.
                                   &readVal, // The array to read samples into, organized according to fillMode.
                                   1, // The size of the array, in samples, into which samples are read.
                                   &samplesPerChanRead, // OUTPUT - The actual number of samples read from each channel.
                                   NULL); // Reserved for future use - pass NULL.


   write_output = DAQmxWriteBinaryU16(taskHandle_AO0, // The task to write samples to.
                                      1, // The number of samples, per channel, to write.
                                      1, // Specifies whether or not this function automatically starts the task if you do not start it.
                                      DAQmx_Val_WaitInfinitely, // The amount of time, in seconds, to wait for this function to write all the samples.
                                      DAQmx_Val_GroupByScanNumber, // Specifies how the samples are arranged, either interleaved or noninterleaved. 
                                      &writeArray, // The array of 16-bit samples to write to the task.
                                      &sampsPerChanWritten, // OUTPUT - The actual number of samples per channel successfully written to the buffer.
                                      NULL); // Reserved for future use - pass NULL.

 

I am not able to test this though as I am not sure how I am supposed to initialize the board. I can no longer use "DAQmxCreateAIVoltageChan" nor "DAQmxCreateAOVoltageChan" since I am no longer reading a Voltage. From this post on the NI boards, the -10V to +10V range of this board should be interpreted across the 0 through 65,535 range as -32768 for the -10V, 0 for the 0V and +32767 for the +10V. However, what do I use to initialize? The Analog Output options in NIDAQmx.h are:

 

DAQmxCreateAOVoltageChan()

DAQmxCreateAOCurrentChan()

DAQmxCreateAOFuncGenChan()

 

There are quite a few options when it comes to Creating Analog Input channels, but none of them jump out to me for use with binary. I would expect that there is some initialization call that I can specify the range above (-32768 to +32767) and the units might be custom? I found

DAQmx_Val_Bits but that range is not in bits, so I also wouldn't know what to use there. In any case, any pointers as to what initialization call from the DAQmx API I need for this would be helpful as I cannot find anything anywhere. Thanks!

 

 

0 Kudos
Message 1 of 7
(828 Views)

There is a board for Multifunction DAQ

 

You still use the DAQmxCreateAIVoltageChan. The minimum and maximum values are used to set the analog input gain. See Set Analog Input Gain for DAQ Device

I don't think that DAQmx API allows you to specify the range in the raw integer form, but you can convert from the raw integer value to calibrated value using the formula you mentioned.

-------------------------------------------------------
Control Lead | Intelline Inc
0 Kudos
Message 2 of 7
(817 Views)

DAQmxReadAnalogScalarF64 will give you data as float64, and DAQmxReadBinaryU16 will give you as unsigned 16, which means if your min and max is set to 10 to -10, your 0 will be 32768, and you have to scale accordingly. same with write too.

 

 

0 Kudos
Message 3 of 7
(770 Views)

Ha, ha, ha!  When I whine about Posters to this Forum who put "pictures" of a large Block Diagram on their post, but no VI, I say "What if someone sent you a "picture of a listing of a C program, instead of the C program itself?"  And reading your long C program written across a long "screen scroll" with long lines broken into two pieces, and the inability to "edit" the listing to make it more "readable" really does prove my point!  What's "bad/hostile" for LabVIEW posters is also true for C posters.

 

I think your question boils down to "How can I learn about DAQmx and write good DAQmx code when there are all these functions that I don't understand?"  For LabVIEW users, I say "go find "Learn 10 functions in NI-DAQmx and Handle 80 Percent of your Data Acquisition Applications"".  For those who want to program DAQmx using C code, I send them here.  Read the whole thing, and see if some (or all?) of your questions are answered.

 

Bob Schor

Message 4 of 7
(742 Views)

I did my best to edit the code posting in such a way as to make it readable, but unfortunately the format of the website and the forum is not very good. It makes it impossible to get things lined up looking nice even when in editing mode, and believe me I tried, and tried again, and then gave up. So the fault is not my own. And my question most certainly was not "I don't understand the API, where can I find answers?" My question was much more specific than that and I am capable of searching both the header files and the API online. The problem is there are no examples anywhere specifically showing how to do what I am trying to do, which in reality is a pretty standard thing for most DAQ boards (reading and writing in binary) - and they generally don't have as convoluted an API as NI does. I am not sure how your comment contributed anything to the conversation, but thanks anyway.

0 Kudos
Message 5 of 7
(711 Views)

The analog input range represents an actual, literal voltage, so you'll still initialize the task the same as if you're using binary or floats- by telling it the actual, literal min and max voltage you want to read or generate. The Analog Read or Analog Write functions will let you operate in "Unscaled" mode, but since there's a gain in there that corresponds to the actual physical hardware configured inside your DAQ you have to specify some real Volts somewhere along the way. That happens when you initialize the function.

 

Mind if I ask why you need to work in binary, rather than scaled? I've been using DAQmx for over a decade and haven't ever needed to use raw binary in an analog IO application. Of course there are plenty of things I haven't tried to do, I'm just curious. I suppose a custom calibration routine would be one place to use raw values.

 

One thing to keep in mind is that using raw binary values will bypass all of your calibration data for your device: https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019YLoSAM&l=en-US

 

Thus, you'll lose accuracy when operating in binary mode, so yeah I'm unsure what you'd need to do using raw mode. I'm definitely curious though.

0 Kudos
Message 6 of 7
(691 Views)

Sorry for the very late response. The reason I want to do it is because my code works with several boards (once being from Measurement Computing) and the other boards use binary. There are scaling factors coming in from amplifiers that would make the code more consistent if everything was done in binary. That is basically the logic here. For consistency of the code.

0 Kudos
Message 7 of 7
(456 Views)