v5.0 / 1 of 23 / 01 sep 99 / gvg
* Just what it says!
* A user called with a problem in running a simple voltage test using VEE and
a 34401 DMM. She was running into an obscure problem, and rather than try to
debug it and figure out what was going on I suggested that she rethink how
she was running the test. I came up with a clean and simple example to
suggest alternatives.
In this example, the test checks to see if a voltage input is within 0.5
volts of a 5 VDC or 10 VDC spec, and announces if the test passes or fails --
both in the VEE program and on the display of the 34401. The test runs in a
loop, with each test initiated by the user pressing on a button in the VEE
program.
The overall block diagram of the program is as follows (source is available
in the file xtinytst.vee):
If the test passes, DIO 3 is executed to display a "PASS!" prompt on the DMM:
* This example -- see the file xspecanl.vee for source -- simulates the
analysis of a band-pass filter. You can select the amplitude of the sweep
signal, its start and stop frequencies, and the number of steps, and then
view the response curve.
The Panel view is laid out as follow:
The Sliders are "free-running" -- that is, they neither have "Wait For Input"
nor "Auto-Execute" (a dubious feature best left alone, by the way) set. They
are continuously driven by an Until Break object. The four inputs are fed
into a Concatenator, which in turn feeds a Shift Register. An If/Then/Else
Object checks the two outputs of the Shift Register and strobes the UUT
UserObject when the inputs change. This scheme allows the example to display
a trace at initial startup and when any of the inputs change.
The UUT UserObject contains a Formula object that generates the sweep using
the "ramp" function
* For a more complicated example, consider the problem of a French VEE user.
She had a dataset of spectra and wanted to perform an analysis on the set.
The idea was to take all the spectra, get a sum of all the data points
through the set that were above a particular value at each frequency, and
then plot the result as a spectrum.
I wrote an example to accomplish this task (see xspecsts.vee for source).
The design of a program necessarily includes the design of the tools for
testing, so the first thing I did was to create a dummy dataset of ten
spectra using the following code:
The "Crunch" UserFunction contains the following objects:
Note that enclosing this circuit in a UserFunction not only makes the program
neater, it also allows all the data to be accumulated and then output to the
rest of the program.
* The "MkData" UserFunction contained within "Crunch" performs the heart of
the processing:
This Formula box is the only really subtle thing in this program. It
contains the formula:
* Moving back up to the top-level program, the "BuildSpectrum" UserFunction
converts the sum array into a spectrum for display:
* This example makes good use of VEE array-processing capabilities and
UserFunctions to perform a sophisticated task with a minimum of objects.
* A user was having problems with the VEE Instrument Finder, and as an
alternative I came up with a VEE program that did the same thing.
The essential trick is to configure a dummy instrument under VEE -- call it
"DummyIO" for short -- and assignive it a short timeout, maybe half a second;
then obtain a Direct I/O box, bring out the Address pin, scan through every
available address and do a serial poll (SPOLL), and see which ones time out
(no device) do not (valid device).
It's not quite that simple, of course, the program has to scan through
primary HPIB addresses. If it doesn't find a device at the primary address,
it has to scan the "00" secondary base address for that primary address to
see if there is a secondary-addressed device there. If there is, it has to
scan all the secondary addresses for that base address.
This is particularly tricky because if the program performs an SPOLL using a
secondary base address such as:
So the scanning scheme is as follows. First, scan all primary addresses (an
asterisk -- "*" -- denotes that an instrument is found):
* The TestAddr UserFunction has the form:
* The ScanPrimary UserObject has the form:
* The list of failures is passed on to the ScanSecondary UserObject to check
for secondary base addresses. This UserObject has the form:
The counter at top simply counts from zero to the total size of the input
list of failed tests. A formula uses this count as an index into the input
array. The total size of the list is given by the "totsize" output pin of a
Get Values object. You cannot use the "totsize()" function, since if you
give it a NIL array (one with nothing in it), you would in that case still
get a "totsize" of 1 and the address would not be valid. The Get Values
object gives a "totsize" of 0 for a NIL array and the UserObject exits
without doing anything.
* Finally, the ScanFrame UserObject scans for the secondary-addressed devices
for each secondary base address. It is very similar to the ScanSecondary
UserObject:
* The Concatenator assembles all the valid device addresses together, and the
"sort()" function then puts them in a rational order. One last thing that
the program could do is then query the devices for ID. However, doing so (or
determining the wisdom of doing so) is left as an exercise for the reader.
* A VEE user wanted to know if there was some way he could select between
different traces and display them at will in a single XY Trace object.
As it turns out, this is essentially simple to do and can be demonstrated
with the following program:
This example program is available as xtrace3.vee.
* A user contacted support with an interesting question: He wanted to
have a panel in VEE that controlled whether one of three tests were performed
using VEE Toggle Controls (which one didn't matter, really) -- and have
a fourth Toggle Control that could turn the other three Toggles ON or OFF.
He also wanted to store the output of the three test Toggles in a Global
Variable.
I tinkered with this a little bit, ran into a dead end and figured it was
impossible, then tried something as a guess. It worked. It turns out to be
easy to do.
The program that does this is as follows:
However, to prevent the output from being sent on to the rest of the
program unless the switch changes, it is put into a Shift Register, which
in turn drives an If/Then/Else object that only generates an output if the
Toggle value changes.
The "Then" output of the If/Then/Else fires if the Toggle is 1. The
"Else If" pin fires if it is 0 (and in both cases fires out a "1").
Like the MASTER Toggle, the TEST Toggles are clocked by the Until Break
object and are not set to "Wait For Input" or "Auto Execute", causing them
to be strobed continuously. They are also set to "Initialize At Prerun",
with a default value of "0".
The three outputs of the TEST Toggles are fed to a Concatenator, which
converts them into an array.
The program also reads the value of "flags" back, just to display them and
show that the program is working OK.
Source for this program is available in the file xmastrsl.vee.
[17.1] A SIMPLE TEST EXAMPLE
[17.2] FREQUENCY RESPONSE
[17.3] TAKING STATISTICS FROM SPECTRA
[17.4] INSTRUMENT FINDER PROGRAM
[17.5] ALTERNATING TRACE DISPLAYS
[17.6] HANDLING TOGGLE BUTTONS
[17.1] A SIMPLE TEST EXAMPLE
+-----------------------+
| Confirm Device | The first DIO resets the DMM,
+-------+ +-----------------------+ checks the DMM's ID string, and
| DIO 1 +--->| | displays a prompt on the DMM.
+---+---+ +-----------------------+
|
+---+---+
| Until |
| Break +-----+
+-------+ |
|
+---------+
|
| OK button to run test.
+-----+-----+ +-------+ OK button to quit tests.
| Run Test +--+ | Quit +--+
+-----------+ | +-------+ |
| |
| +---+---+
| | Stop |
| +-------+
| +---------+
| | Value |
+---+---+ +---------+ The second DIO runs the
| DIO 2 +--+-->| | test and gets the results.
+-------+ | +---------+
|
+----------------+ +---------+
| | Results |
| +---------+ +---------+ The formula determines if
+-->| Formula +--+-->| | the test passes or fails.
+---------+ | +---------+
|
+----------------+
|
| +----------------------+
| | If/Then/Else |
| +---+-----------+------+
+-->| A |[A=="FAIL"]| Then +--------------+
| | | Else +--+ |
+---+-----------+------+ | |
| |
+---+---+ +---+---+
DIO 3 announces | DIO 3 | | DIO 4 | DIO 4 announces
the test passed. +---+---+ +---+---+ the test failed.
| |
| +-->+-----+
| | JCT +--+
+-------------->+-----+ |
|
DIO 5 waits 3 seconds +---+---+
and prompts for the | DIO 5 |
next test. +---+---+
|
+---+---+
Go back to run test. | Next |
+-------+
The Panel View of this program is quite simple:
+------------------------------------------------+
| |
| +--------------------------+ |
| | Confirm Device | |
| +--------------------------+ |
| | | |
| +--------------------------+ |
| |
| +----------+ +------------+ +------------+ |
| | Run Test | | Value | | Results | |
| +----------+ +------------+ +------------+ |
| | | | | |
| +------------+ +------------+ |
| |
| +----------+ |
| | Quit | |
| +----------+ |
+------------------------------------------------+
The DIO 1 object -- which clears the device, queries it for its ID, and puts
a prompt on it -- contains the transactions:
EXECUTE CLEAR
WRITE TEXT "*RST;*CLS" EOL
WRITE TEXT "*IDN?" EOL
READ TEXT X STR
WRITE TEXT "DISP:TEXT \'RUN TEST?\'" EOL
The DIO 2 object -- which runs the test -- contains:
WRITE TEXT "MEAS:VOLT:DC?" EOL
READ TEXT X REAL
The results are tested by the Formula box, which contains a nested triadic
operation:
(abs(5-A)<0.5 ? "PASS5" : (abs(10-A)<0.5 ? "PASSTEN" : "FAIL" ))
This returns "PASS5" if the result of the measurement is within half a volt
of 5 VDC, returns "PASSTEN" if it is within half a volt of 10 VDC, and
returns "FAIL" otherwise.
WRITE TEXT "DISP:TEXT \'PASS!\'" EOL
If it fails, DIO 4 is exected to display a "FAILED!" prompt on the DMM and
cause a beep:
WRITE TEXT "DISP:TEXT \'FAILED!\'" EOL
WRITE TEXT "SYST:BEEP" EOL
In either case, DIO 5 waits 3 seconds and prompts for the next test:
WAIT INTERVAL:3
WRITE TEXT "DISP:TEXT \'RUN TEST?\'" EOL
This scheme is quite flexible and can be modified and extended as needed.
[17.2] FREQUENCY RESPONSE
+--------------------------------------------------------+
| Frequency Response |
+--------------------------------------------------------+
| +-----------------+ +-----------------+ +-----------+ |
| | Num Steps | | Amplitude | | Start | |
| | slider | | slider | | Frequency | |
| +-----------------+ +-----------------+ | slider | |
| +-------------------------------------+ +-----------+ |
| | Magnitude Spectrum display | +-----------+ |
| | | | Stop | |
| | | | Frequency | |
| | | | slider | |
| +-------------------------------------+ +-----------+ |
+--------------------------------------------------------+
The program itself is laid out as follows:
+--------------+
+----------->| |
| +-------->| Concatenator +--+
| | +----->| | |
| | | +-->| | |
| | | | +--------------+ |
| | | | |
| | | | +------------------+
| | | | |
| | | | | +----------+
| | | | | | Shift +-->+--------------+
| | | | +-->| Register | | If/Then/Else +--+
| | | | | +-->+-------+------+ |
+-------+ | | | | +----------+ | |
| Until | | | | | +---+---+ |
| Break +--+ | | | | | Next | |
+-------+ | | | | | +-------+ |
| | | | | |
+------+ | | | | +------------------------------+
| | | | | |
+-----+-----+ | | | | | +--------------------------
| Amplitude +---+ | | | | | Magnitude Spectrum
+-----+-----+ | | | | | +-------------+------------
| | | | | +--+--+ | |
+-----+-----+ +--|--|--|---->| UUT | | |
| Num Steps +------+--|--|---->| +-->|[ Trace1 ] |
+-----+-----+ | | | | | |
| | | | | | |
+-----+-----+ | | +--+--+ | |
| Start Freq+---------+ | | | |
+-----+-----+ | | | |
| | | | |
+-----+-----+ | | | |
| Stop Freq +------------+ | | +--------
+-----------+ | |
| |
+----->|[Auto Scale]
|
+--------------------
The four sliders are at left, and feed the UserObject named "UUT" in the
middle. This User Object contains the program elements needed to do the
computation, and drives the results to the Magnitude Spectrum object.
ramp(numElem, from, thru)
-- plus another Formula object (fed by two constants) that implements the
filter:
f*(fo/Q)/((f^2-fo^2)+j(f*fo/Q))
Note that this generates a complex output. A Multiplier multiplies the
filter output to the proper amplitude, and a Build Spectrum object implements
the final spectrum:
+-------------------------------------------------------+
| Build Spectrum |
+----------------+---------------------------+----------+
-->| Array PComplex | [ Start/Stop Freq ] | |
+----------------+ +----------+
-->| Start Real | [ 814.7 ] [ 376.6 ] | Spectrum +-->
+----------------+ +----------+
-->| Stop Real | Freq. Sampling [ Linear ] | |
+----------------+---------------------------+----------+
This gives a UserObject with the contents:
+-------------------------------------------------------------------------+
| UUT |
+-------+-------------------------------------------------------------+---+
| | amplitude | |
| A +---------------------------+ | |
| | | start | |
| | +------------------------|---------------+ | |
| | | | | +----------+ | |
| | | +------+ +------->+---+ +->| Build | | |
|numElem+--|----->| | f +------+ | * +---->| Spectrum +-->| X |
| | +----->| ramp +------->| band +-->+---+ +->| | | |
| | | +-->| | +---->| pass | | +----------+ | |
| | | | +------+ | +-->| | | | |
| | | | | | +------+ | | |
| | | | +------+ | | | | |
| from +--+ | | Q +--+ | Q=5 | | |
| | | +------+ | | | |
| | | | | | |
| | | +------+ | | | |
| | | | fo +----+ fo=500 | | |
| | | +------+ | | |
| thru +-----+-------------- stop -----------------+ | |
| | | |
+-------+-------------------------------------------------------------+---+
The rest of the implementation is a simple matter of wiring the parts and
placing them on the display.
[17.3] TAKING STATISTICS FROM SPECTRA
+---------------+
| For Count: 10 +--+-------------------->+--------------+ +-------------+
+-------+-------+ | +-----+ | Build Record +-->| To Data Set |
| | +->| fft +-->+--------------+ +-------------+
| +-----+-----+ | +-----+
| | Noise +--+
| | Generator |
| +-----------+
|
Not much to this. It generates ten noise waveforms, converts them to spectra
with an FFT, builds the loop count and waveform into a record, and shoves the
record into a dataset. The real work is done by the following code:
| +---------------------------
| | Magnitude Spectrum
+------+ +-----------+--------------
| | |
+----+ +----+---+-->+---------------+ | |
| 90 +-->| Crunch +-->| BuildSpectrum +-->| Trace1 |
+----+ +--------+-->+-------+-------+ | |
| | |
+---------->| AutoScale |
| |
| |
+-----------+-------
The threshold value is stored in the Real constant at left. The program
itself consists mainly of two UserFunctions: "Crunch" (which does most of
the work) and "BuildSpectrum" (which converts the results into a spectrum
format for display).
+-----------------------------------------------------------------------+
| Crunch |
+-----------+---------------------------------------------------+-------+
| | +-------+ +----------+ | |
| | | Start | | | | |
| | +---+---+ +-->+---+ | | |
| | | | + +--+-->| Data |
| | +---+---+ +-->+---+ | |
| | | Until | | | |
| | | Break +--+ | | |
| | +-------+ | +----------+ | |
| | | | | |
| | +----+-----+ | | |
| | | From +------------>+----------+--+ | |
| Threshold +----+ | Data Set +--+ | U_MkData +----->| Start |
| | | +----------+ | +-->+----------+--+ | |
| | | | | | | |
| | | +---+---+ | | | |
| | | | Break | | | | |
| | | +-------+ | | | |
| | +------------------------+ +-->| Stop |
| | | |
+-----------+---------------------------------------------------+-------+
All "Crunch" does is get each spectrum out of the dataset, run it through
"MkData" to convert it into an array of 1s and 0s (where 1 means the array
value is above the Threshold and 0 means it is equal to or below it), and
then sum the arrays using the "+" object. This UserFunction provides as
outputs the final sum of the arrays ("Data") and the start and stop
frequencies of the spectra so that the data can be converted back into into a
spectrum after processing.
+---------------------------------------------------------------------+
| MkData |
+-----------+-------------------------------------------------+-------+
| | | |
| | +----------+ | |
| | +-->| UnBuild +--------->+---------+ | |
| | | | PComplex | | Formula +-->| Array |
| | | +----------+ +-->+---------+ | |
| | | | | |
| | +-----------------+ | | |
| | | | | |
| | +-----+ +----------+ | | | |
| Record +-->| A.B +-->| UnBuild +--+ | | |
| | +-----+ | Spectrum +------|---------------->| Start |
| | | +------|-------------+ | |
| | +----------+ | | | |
| | | | | |
| Threshold +-------------------------------+ +-->| Start |
| | | |
+-----------+-------------------------------------------------+-------+
This UserFunction extracts the spectrum from the record (using the Formula
box at left that contains the expression "A.B") and tears apart the spectrum
to get a linear array of data (plus the start and stop frequencies for
reconstruction later). This linear array is shoved through a second Formula
box to convert it to 1s and 0s.
signof( clipLower( A, B ) - B )
This works as follows:
+------------------------------------------------------------------+
| BuildSpectrum |
+-------+-----------------------------------------------+----------+
| | +----------+ | |
| Data +------------->| Build | | |
| | +-->| PComplex +--+ | |
| | +---+ | +----------+ | | |
| | | 0 +--+ | +----------+ | |
| | +---+ +-->| Build | | |
| Start +------------------------------->| Spectrum +-->| Spectrum |
| | +-->| | | |
| | | +----------+ | |
| Stop +----------------------------+ | |
| | | |
+-------+-----------------------------------------------+----------+
This UserFunction's operation is obvious. The only slightly tricky thing is
that the value 0 is used to restore the sum data array to a polar complex
format by adding a phase of 0. (There's no reason to need the phase data in
this case, but you can't have a spectrum without it.)
[17.4] INSTRUMENT FINDER PROGRAM
70900
-- it will find a device with the address 709 or 70900 -- while if you
perform an SPOLL on:
709
-- it will only acknowledge a device with the address 709, not one with
70900. This means that the program must screen out all the primary-address
devices and then check the remaining addresses to see if they respond
secondary-address base address, or it will get a false indication of a
secondary-address device.
700 -> *701 -> 702 -> 703 -> 704 -> 705 -> 706 ->
707 -> 708 -> 709 -> 710 -> 711 -> 712 -> *713 ->
714 -> 715 -> 716 -> 717 -> 718 -> 719 -> 720 ->
721 -> *722 -> *723 -> 724 -> 725 -> 726 -> 727 ->
728 -> 729 -> 730
This done, scan the remaining addresses as secondary base addresses:
70000 -> 70200 -> 70300 -> 70400 -> 70500 -> 70600 ->
70700 -> 70800 -> *70900 -> 71000 -> 71100 -> 71200 ->
71400 -> 71500 -> 71600 -> 71700 -> 71800 -> 71900 -> 72000 ->
72100 -> 72400 -> 72500 -> 72600 -> 72700 ->
72800 -> 72900 -> 73000
Since one secondary base address was found, then all secondary addresses
following that base address have to be scanned next:
70901 -> 70902 -> 70903 -> 70904 -> 70905 -> 70906 ->
70907 -> 70908 -> 70909 -> 70910 -> 70911 -> *70912 -> 70913 ->
70914 -> 70915 -> 70916 -> 70917 -> 70918 -> 70919 -> 70920 ->
70921 -> 70922 -> *70923 -> 70924 -> 70925 -> 70926 -> 70927 ->
70928 -> 70929 -> 70930
The results of all these tests are assembled and sorted, giving the following
output:
701
70900
70912
70923
713
722
723
* The overall program architecture is as follows (see xifind.vee for the
source):
ISC
+------+ +-------------+-------->+--------------+ +--------+
| 7 +-->| ScanPrimary | +--->| Concatenator +-->| sort() +-->
+------+ +-------------+--+ | +->+--------------+ +--------+
| | |
+----------------------+ | |
| | |
| +---------------+ | |
+-->| ScanSecondary +----+ |
+---------------+ | |
| |
+--------------------+ |
| |
| +-----------+ |
+-->| ScanFrame +------+
+-----------+
The heart of this program are the three UserObjects ScanPrimary (which checks
all the primary addresses), ScanSecondary (which checks all the remaining
addresses for valid secondary base addresses), and ScanFrame (which checks
all the secondary addresses for each valid secondary base address) -- and a
UserFunction named TestAddr, that actually does the SPOLL test. The results
of the three UserObjects are concatenated, sorted, and then output to a
display, or whatever.
+----------------------------------------------------------------------+
| UserFunction: TestAddr |
+-+------------------------------------------------------------------+-+
| | | |
| | +---+ | |
| | | 0 +-------------------------+ | |
| | +-+-+ | | |
| | | | | |
| | +--------------------+--------------------+ | | |
| | | DummyIO | | | |
+-+ +---------+-----------------------+-------+ | | |
|A+-->| Address | WAIT SPOLL:0x40 CLEAR | Error +--+ | | |
+-+ | | | | | | | |
| | +---------+-----------------------+-------+ | | | |
| | | +-->+-----+ +-+
| | +-+-+ | JCT +-->|X|
| | | 1 +---->+-----+ +-+
| | +---+ | |
| | | |
+-+------------------------------------------------------------------+-+
| [ Close ] |
+---------------------------------------------------------------------+
As noted, all this does is perform an SPOLL (to the device named DummyIO,
with the address given by the input pin). If it times out, it returns a 1
(no device), otherwise it returns a 0 (valid device).
+------------------------------------------------------------------+
| ScanPrimary |
+-+--------------------------------------------------------------+-+
| | | |
| | count primary addresses | |
| | +-----------+ | |
| | | For Count | | |
| | | [ 31 ] +----->+---------+ compute full address | |
| | +-----+-----+ | B*100+A +--+ using ISC & HPIB | |
| | | +-->+---------+ | primary address count | |
| | | | | | |
|I+--------|--------+ | | |
| | ISC | | | |
| | | +------- address ------+ | |
| | | | A==0? | |
| | | | +-----------------+ +--------------+-----+ | |
| | | +-->| Call TestAddr() +-->| If/Then/Else | | | |
| | | | +-----------------+ +--------------+--+ | | |
| | | | | | | |
| | | | address test == 0, valid device | | | |
| | | | +-----------------------------------|--+ | |
| | | | | | | |
| | | | +---+---+ valid devices | | |
| | | +-->| Gate +----->+-----------+ | +-+
| | | | +-------+ | Collector +------------|----->|Y|
| | | | +-->+-----------+ | +-+
| | | | | | | |
| | +--|--------------+ | | |
| | | | | | |
| | | | address test == 1, test failed | | |
| | | | +-----------------------------------+ | |
| | | | | | |
| | | | +---+---+ failures | |
| | | +-->| Gate +----->+-----------+ +-+
| | | +-------+ | Collector +------------------>|N|
| | | +-->+-----------+ +-+
| | | | | |
| | +-----------------+ | |
| | | |
+-+--------------------------------------------------------------+-+
This UserObject ultimately generates two arrays as outputs: one with a list
of devices it found, another with a list of failed address tests. The list
of found devices is passed on to the Concatenator.
+------------------------------------------------------------------+
| ScanSecondary |
+-+--------------------------------------------------------------+-+
| | | |
| | use totsize index list, | |
| | output pin list size convert to | |
| | +------------+ +-----------+ secondary address | |
| | +-->| Get Values +-->| For Count +----->+----------+ | |
| | | +------------+ +-----+-----+ | B[A]*100 +--+ | |
| | | | +-->+----------+ | | |
| | | +-----------------+ | | | |
+-+ | | | | | |
|N+--+--------|--------------------------+ | | |
+-+ | | | |
| | | secondary base address | | |
| | | +----------------------------------------+ | |
| | | | | |
| | | | A==0? | |
| | | | +-----------------+ +--------------+ | |
| | | +-->| Call TestAddr() +-->| If/Then/Else +--+ | |
| | | | +-----------------+ +--------------+ | | |
| | | | | | |
| | | | +-----------------------------------+ | |
| | | | | | |
| | | | +---+---+ valid devices | |
| | | +-->| Gate +----->+-----------+ +-+
| | | +-------+ | Collector +-------------->|Y|
| | | +-->+-----------+ +-+
| | | | | |
| | +------------------+ | |
| | | |
+-+--------------------------------------------------------------+-+
This UserObject gets a list of the failed tests on primary addresses as an
input. It scans through the list, converts each primary address in the list
into a secondary base address, scans for the secondary base address, and
collects all the positive tests into an array for output. This array is
output to the Concatenator.
+------------------------------------------------------------------+
| ScanFrame |
+-+--------------------------------------------------------------+-+
| | | |
| | use totsize | |
| | output pin list size | |
| | +------------+ +-----------+ | |
| | +-->| Get Values +-->| For Count +---+ | |
| | | +------------+ +-----+-----+ | | |
| | | | | | |
| | | +-----------------+ | | |
| | | | | | |
| | | | +----------------------+ | |
| | | | | | |
| | | | | index into list | |
| | | | +-->+-------+ | |
+-+ | | | B[A] +-------->+-------+ | |
|N+--+--------|------->+---+---+ | A + B +---------+ | |
+-+ | | +-->+-------+ | | |
| | | +-----+-----+ | create | | |
| | | | For Range +---+ secondary | | |
| | | | [ 1:30 ] | address | | |
| | | +-----------+ | | |
| | | count through | | |
| | | secondary addresses | | |
| | | | | |
| | | | | |
| | | secondary address | | |
| | | +----------------------------------------+ | |
| | | | | |
| | | | A==0? | |
| | | | +-----------------+ +--------------+ | |
| | | +-->| Call TestAddr() +-->| If/Then/Else +--+ | |
| | | | +-----------------+ +--------------+ | | |
| | | | | | |
| | | | +-----------------------------------+ | |
| | | | | | |
| | | | +---+---+ valid devices | |
| | | +-->| Gate +----->+-----------+ +-+
| | | +-------+ | Collector +-------------->|Y|
| | | +-->+-----------+ +-+
| | | | | |
| | +------------------+ | |
| | | |
+-+--------------------------------------------------------------+-+
The main difference from ScanSecondary is that ScanFrame gets a list of
secondary base addresses, and then scans through secondary addresses 1 to 30
for each base address. ScanFrame returns an array of valid device secondary
addresses, which is output to the Concatenator.
[17.5] ALTERNATING TRACE DISPLAYS
+-------+
| Start | Start is required on VEE 3.X, not recommended for VEE 4.X.
+---+---+
| +---------------------+
+---+---+ +----------+ | |
| Until | | | +-->+--------------+ |
| Break +--+ +-------+-------+ | Concatenator +--+---+
+-------+ | Random Number +----->+--------------+ |
+-------+-------+ |
| |
| +---------------------+ |
| | | |
| +-->+--------------+ | |
+-------+-------+ | Concatenator +--+-+ |
| Random Number +----->+--------------+ | |
+-------+-------+ | |
| | |
| +---------------------+ | |
| | | | |
| +-->+--------------+ | | |
+-------+-------+ | Concatenator +--+ | |
| Random Number +----->+--------------+ | | |
+-------+-------+ | | |
| +---------------+ | |
+-------------+ | +---------------+ |
| | | +---------------+
| +--------------+ | | | +----------+
| | AlphaNumeric | | | | +---------+ | XY Trace |
| +--------------+ | | | | Formula | +----------+
| +->| Trace3 | | | | +---+-----+ | |
+-------+-------+ | +--------------+ | | +->| A | +---->| |
| Radio Buttons | | | +--->| B | | | |
+---------------+ | +---------+ +----->| C | | +->| |
| < > Trace1 +--+------>| Ordinal +------->| X | | | +-----+----+
| < > Trace2 | +---------+ +---+-----+ | |
| <*> Trace3 | Use Ordinal pin +--------+
+---------------+ instead on VEE 4.X AutoScale |
+----+----+
| Delay 1 |
+---------+
The three Concatenators at top simulate three measurement inputs. The three
measurement traces are fed to the Formula box, which also accepts an input
from the Radio Buttons object to determine what trace to display, as per the
Formula:
( X==0 ? A : (X==1 ? B : C ))
The limitation of this example is that display and simulated data acquisition
have to occur in lockstep. In practice, to allow independent data
acquisition and display, the display part of this program would likely need
to be implemented as its own thread, with the traces provided by Global
Variables, loaded by another independent thread or threads. However, this
would make for an unmanageable and unclear example program.
[17.6] HANDLING TOGGLE BUTTONS
+-------+
| Until |
| Break +--+
+-------+ |
| +---------------------------------+
+--------+ | If/Then/Else |
| | +----------+ +---+-------------------+---------+
| +----+----+ | Shift +-->| A | (A!=B) AND (B==1) | Then +-+
| | Toggle +-->| Register +-->| B | (A!=B) AND (B==0) | Else If +-|-+
| +---------+ +----------+ +---+-------------------+---------+ | |
| MASTER | |
| +-------------- Then --------------------------------------------+ |
| | +----------- Else If -------------------------------------------+
| | |
+---|--|---------------------+
| | | |
| | | Default Value +----+----+
| | +--------------->| Toggle +-----+
| +--|--------------->| | |
| | | Reset +---------+ |
| | | TEST 1 |
+---|--|---------------------+ |
| | | | |
| | | Default Value +----+----+ |
| | +--------------->| Toggle +--+ |
| +--|--------------->| | | |
| | | Reset +---------+ | |
| | | TEST 2 | |
+---|--|---------------------+ | |
| | | | +-->+--------------+
| | Default Value +----+----+ +----->| Concatenator +--+
| +--------------->| Toggle +-------->+--------------+ |
+------------------>| | |
Reset +---------+ |
TEST 3 |
+------------------------------------------+------------------+
| |
| +-------------------+ |
| | If/Then/Else | |
| +----------+ +---+--------+------+ |
| | Shift +-->| A | (A!=B) | Then +---|-----------+
+-->| Register +-->| B | | Else | | |
+----------+ +---+--------+------+ | +-------+-------+
| | Set Variable |
| +---------------+
+-->| flags |
+-------+-------+
|
+---------------------------------+
|
| +--------------+
+-------+-------+ | AlphaNumeric |
| Get Variable | +--------------+
+---------------+ | 0: 0 |
| flags +-->| 1: 1 |
+---------------+ | 2: 2 |
+--------------+
This program is divided into three main sections, from top to bottom: