9.1 - Low Speed < 100Hz Acquisition (Applies to UD-Series)
9.1.1 Introduction
Because Windows needs to occasionally use the CPU in your computer to redraw the screen, process the mouse, and perform other tasks, you generally cannot read inputs or control outputs faster than 100hz using the computer as the timer. Of course with the new multicore CPU's, you may be able to tweak extra speed out DAQFactory's polling loops, essentially putting the acquisition on one core, and letting Windows use the other core for display. Doing this sort of thing requires more advanced techniques described in the last chapter. This section describes how to do software (i.e. DAQFactory) polled reading and writing of analog and digital inputs and outputs. The techniques are essentially the same for both analog and digital inputs and outputs.
9.1.2 The Easy Way - With Channels
The easiest way to read analog and digital inputs at low speeds (i.e. < 100hz) or set analog and digital outputs is to use channels as we did in the Basic I/O chapter. No scripting is required, you can easily convert the readings using Conversions, and log it using Logging sets. For the U3, which allows the specification of the differential channel, you can simply put the channel number for the negative side in the Quick Note / Special / OPC column of the channel. If you need more advanced configuration, you can still use channels, combined with some basic scripting to send the configuration commands to the LabJack. For example, to set the UE9 resolution to 14 bit and the range on channels 2 and 3 to +/-5V:
using("device.labjack.")
include("c:\program files\labjack\drivers\labjackud.h")
AddRequest(0, LJ_ioPUT_CONFIG, LJ_chAIN_RESOLUTION, 14, 0, 0)
AddRequest(0, LJ_ioPUT_AIN_RANGE, 2, LJ_rgBIP5V, 0, 0)
AddRequest(0, LJ_ioPUT_AIN_RANGE, 3, LJ_rgBIP5V, 0, 0)
GoOne(0)
This was explained earlier in the chapter on scripting.
9.1.3 Basic Scripting using eGet
Channels, however, aren't always the best choice, and sometimes you need script. The next step up from channels is to use basic scripting and the eGet() function to retrieve a single value, or ePut() to set a single value. For example, to read channel 1 and then put the result into a channel called MyChannel we would do:
private err
private val
private string message
while(1)
err = eGet(0, LJ_ioGET_AIN, 1, @val, 0)
if (err)
ErrorToString(err, @message)
? message
else
MyChannel.AddValue(val)
endif
delay(1)
endwhile
Now truthfully, this next code snippet does the exact same thing, provided MyChannel is setup to read channel 1:
while(1)
read(MyChannel)
delayendwhile
But, this code does not allow for any direct error handling, and of course doesn't demonstrate the eGet function! eGet is also more useful when you don't know your channel numbers at runtime. In the first example, we used scalar values in our eGet() function call, but there is no reason why you couldn't use variables that could then be changed from elsewhere:
err = eGet(ID, LJ_ioGET_AIN, chan, @val, 0)
Another thing the first example shows is the AddValue() function of the channel MyChannel. This function essentially stuffs a value into a channel. This allows you to utilize the benefits of a channel (history, easy logging, etc), without actually using the channel Timing to trigger the reads. In this case, we are putting the result of the eGet call into MyChannel. MyChannel does not have to have the same channel number, I/O type, or even be Device Type "LabJack".
Note: if using the U3, you will need to configure the pins as analog inputs. With Channels, this is done automatically for you, but when scripting you have to do it yourself. It only takes one line to enable a channel:
ePut(ID, LJ_ioPUT_ANALOG_ENABLE_BIT, 1, 1, 0)
Sample file: LJGuideSamples\eGet.ctl
9.1.4 More Advanced Using Add / Go / Get
eGet can be handy for very simple scripts, but when doing a number of different requests, it is much better to use the AddRequest() / GoOne() / GetResult() functions. So, if we wanted to read channels 1 through 3 we could do:
eGet(0, LJ_ioGET_AIN, 1, @val1, 0)
eGet(0, LJ_ioGET_AIN, 2, @val2, 0)
eGet(0, LJ_ioGET_AIN, 3, @val3, 0)
or we could do:
AddRequest(0, LJ_ioGET_AIN, 1, 0, 0, 0)
AddRequest(0, LJ_ioGET_AIN, 2, 0, 0, 0)
AddRequest(0, LJ_ioGET_AIN, 3, 0, 0, 0)
GoOne(0)
GetResult(0, LJ_ioGET_AIN, 1, @val1)
GetResult(0, LJ_ioGET_AIN, 2, @val2)
GetResult(0, LJ_ioGET_AIN, 3, @val3)
OK, you may think that eGet looks much easier since its only three lines of code vs. seven, and it is easier on a basic level, but with ease you lose flexibility and efficiency. Using the second method is more efficient internally. The second method also allows you to do error handling easier using the GetNextError() function. With error handling, the eGet code ends up looking like this:
err = eGet(0, LJ_ioGET_AIN, 1, @val1, 0)
if (err)
... error!
endif
err = eGet(0, LJ_ioGET_AIN, 2, @val2, 0)
if (err)
... error!
endif
err = eGet(0, LJ_ioGET_AIN, 3, @val3, 0)
if (err)
... error!
endif
But the Add / Go / Get looks like this:
AddRequest(0, LJ_ioGET_AIN, 1, 0, 0, 0)
AddRequest(0, LJ_ioGET_AIN, 2, 0, 0, 0)
AddRequest(0, LJ_ioGET_AIN, 3, 0, 0, 0)
GoOne(0)
GetResult(0, LJ_ioGET_AIN, 1, @val1)
GetResult(0, LJ_ioGET_AIN, 2, @val2)
GetResult(0, LJ_ioGET_AIN, 3, @val3)
while (GetNextError(1,@io,@ch,@err)
... error!
endwhile
As you can see the second method is much cleaner and easier to read. The eGet() version would get worse and worse as you added more function calls. Using the second method also allows you to create a single error handler for the entire block, or as shown in the section on error handling, you can create a single function to do all your error handling for all your scripts.
Note: variable declarations are not shown in the above examples, but would be required. Likewise, ... error! would need to be replaced with script to actually do something in the case of an error.
9.1.5 Controlling outputs
We've talked a little about setting an output from a screen control in section 4.6, but this requires user input. We
also talked about setting an output based on an input in section 7.1. In this section we saw that you can easily set
an output channel by simply assigning a value to it. So, to set a DAC channel named ValvePosition to 3 volts
(assuming no Conversion exists on the channel), we would simply do:
ValvePosition = 3
You can also apply conversions to outputs like we did with inputs in section 4.3, however, the conversions work in
reverse. For inputs, the conversion takes the raw voltage (counts or other units) from the LabJack and converts it
into engineering units like temperature or pressure. For outputs, the conversion takes the engineering units and
converts into voltage. So, for if we had a proportional valve that takes a 0 to 5V signal and we want to be able to
specify the percentage open, where 0% = 0V and 100% = 5V, the conversion would be:
Value / 20
Then, to open the valve to 60% we could just do:
ValvePosition = 60
and 3 volts would be outputted from the DAC of the LabJack.
Now as a further example, lets say we'd like to ramp the valve position from 0 to 100% over 60 seconds in steps of
1%. The script is quite simple:
// initialize valve position to 0
ValvePosition = 0
while (ValvePosition < 100)
ValvePosition = ValvePosition + 1
delay(0.6)
endwhile
Doing a ramp and soak is not much harder, just split out the while loops. Lets say we want to ramp to 40% in 20
seconds, soak for 10 seconds, ramp to 80% in 40 seconds, soak for 5 seconds, then ramp back down to 0 in 60
seconds:
// initialize valve position to 0
ValvePosition = 0
// ramp to 40
while (ValvePosition < 40)
ValvePosition = ValvePosition + 1
delay(0.5)
endwhile
delay(10) // soak
// ramp to 80
ValvePosition = 40 // this is to make the graph look good
while (ValvePosition < 80)
ValvePosition++ // this is the same as VP = VP + 1
delay(1)
endwhile
delay(5) // soak again
ValvePosition = 80
// ramp down to 0
while (ValvePosition > 0)
ValvePosition--
delay(0.75)
endwhile
One important note: these sequences won't work if you try and ramp past the highest value of the output. If you try
and set an output to an invalid value you will get an Alert and the channel WILL NOT update, so the while() will
never exit because ValvePosition would never reach the desired point.
Sample file: LJGuideSamples\RampSample.ctl