LAST PAGE  BACK TO INDEX  NEXT PAGE

[9.0] Advanced I/O

[9.0] Advanced I/O

v5.0 / 9 of 23 / 01 sep 99 / gvg

* This chapter provides studies of advanced I/O techniques.


[9.1] ACCESSING ARBITRARY-BLOCK DATA WITH DIRECT-I/O
[9.2] BUILDING A FIXED LENGTH BINBLOCK HEADER
[9.3] SCOPE DRIVERS & TIMEOUTS
[9.4] PERFORMING DEVICE-TO-PRINTER DUMPS

 BACK TO INDEX

[9.1] ACCESSING ARBITRARY-BLOCK DATA WITH DIRECT-I/O

* The IEEE 488.2 HPIB spec defines a binary-block (BINBLOCK) transfer format that follows a simple and intelligent scheme. The header is of the form:


  #NDDDDD<byte><byte> ... <byte><byte>
 
It begins with a "#" character, followed by a single ASCII digit ("N") from 1 to 9 that specifies a number of ASCII digits to follow. These following ASCII digits ("DDDDD" in this case, though they could actually be anything from "D" to "DDDDDDDDD") give the number of actual data bytes to follow. For example:

   #3128<byte1><byte2> ... <byte128>
 
-- specifies a block of 128 data bytes.

Reading and sending such a stream with VEE is easy because VEE supports this type directly with the BINBLOCK data type. This section outlines how to use it by demonstrating how to read and store a "learn string" from a 54600 scope using VEE direct I/O (DIO) objects.

* The example performs these steps:

The architecture is shown below (source is in the file xlrnstrn.vee). A few notes:


                                 displays ID string
   +--------------+              +--------------------------------------+
   |     DIO      |              |             AlphaNumeric             |
   +--------------+              +--------------------------------------+
   | JEDI (@ 713) +------------->|     HEWLETT-PACKARD,54600A,0,2.3     |
   +------+-------+              +--------------------------------------+
          | clears scope  
          | & gets ID
          |
          | gets learn string
          | from scope
   +------+-------+          +---------+
   |     DIO      +--------->| To File | stores learn string to file
   +--------------+          +----+----+  
   | JEDI (@ 713) +--+            |
   +--------------+  |      +-----+------+      +--------------+   +-------+
                     |      | From File  +----->|     DIO      |   |  err  |
    +----------------+      +------------+      +--------------+   +-------+
    |   +-------+           reclaims string     | JEDI (@ 713) +-->|  +0   |
    |   |  err  |                               +--------------+   +-------+
    |   +-------+                                writes learn                
    +-->|  +0   |                                string to scope
        +-------+
 
* Now let's go through this and examine the important objects in detail.

The first DIO object simply clears and resets the scope, then gets the ID string. This is a standard block I put in most of my DIO programs and not of great interest:


   +-------------------------------------+
   |             JEDI (@ 713)            |
   +---------------------------------+---+
   | EXECUTE CLEAR                   |   | Clear scope interface.
   | WRITE TEXT "*RST;*CLS" EOL      |   | Reset & clear status.
   | WRITE TEXT "*IDN?"              | X | Query for ID.
   | READ TEXT X STR                 |   | Get back ID string.
   |                                 |   |
   +---------------------------------+---+
 
Note how the last transaction reads in the ID string as TEXT in string (STR) format, with a maximum string size of 50 characters (which is more than needed for the ID string).

* The second DIO object is more interesting. It performs the read of the arbitrary-block data:


   +--------------------------------------+
   |             JEDI (@ 713)             |
   +----------------------------------+---+
   | WRITE TEXT "*LRN?" EOL           | X | Query for learn string.
   | READ BINBLOCK X BYTE             |   | Get block (specify LF end byte).
   | WRITE TEXT ":SYSTEM:ERROR?" EOL  |   | Ask for error status.
   | READ TEXT Y STR                  | Y | Get back error status.
   +----------------------------------+---+
 
The transactions are simple:

* The data is stored in a file using the "To File" object as follows:


   +-------------------------------------------------+
   |                       To File                   |
   +-----+-------------------------------------------+
   |     | To File:  [        LRNSTR.BIN           ] |
   |     |          [X] Clear File At Prerun & Open  |
   |  A  +-------------------------------------------+ 
   |     | WRITE BINBLOCK A BYTE                     | Build file.
   |     |                                           | Build file.
   |     |                                           |
   +-----+-------------------------------------------+
 
Not much to this. The first transaction deletes the file and the second simply rebuilds the file and writes the binary data (the learn string) to it.

Note that it is stored in arbitrary-block format in the file and has the appropriate header information. While this is convenient, you could also store the data simply as a byte array with the transaction:


   WRITE BINARY A BYTE
 
* If you are getting other data types -- 16- or 32-bit integer or 32- or 64-bit float -- you should remember that the byte order on your instrument may not be the same as byte order on your computer. If you get numbers in the wrong order, the numeric values will look like gibberish.

The default setting for byte order (in the I/O configuration dialog under the VEE "I/O > Instrument > Edit Instrument" dialog) is MSB (most-significant byte first), and all (as far as I know) HP instruments conform to that order. VEE is smart enough to perform the proper byte-order reversal when necessary, so most of the time you'll get the proper byte ordering by default. The only time you'll run into trouble is when you get some odd instrument (I am told some Tek scopes are like this) that provides LSB byte ordering. In that rare case you will need to change the setting from the default MSB to LSB.

* So much for getting the arbitrary-block data. Now to retrieve it. A From File object gets the data from the file:


   +---------------------------------------------+
   |               From File                     |
   +-----------------------------------------+---+
   | From File:  [       LRNSTR.BIN        ] |   |
   +-----------------------------------------+   |
   | READ BINBLOCK X BYTE                    | X | Get file.
   |                                         |   |
   |                                         |   |
   +-----------------------------------------+---+
 
Not much to this, either. The object reads the data as a BINBLOCK and shoves it out the "X" pin.

If you had chosen to store the data as binary data instead of a BINBLOCK, you would use the following transaction to retrieve it:


   READ BINARY X BYTE ARRAY:*
 
* The last DIO object is relatively straightforward:

   +-------------------------------------------+
   |                 JEDI (@ 713)              |
   +---+-----------------------------------+---+
   |   | WRITE TEXT ":SYSTEM:SETUP "       |   | Send command.
   |   | WRITE BINBLOCK A BYTE EOL         |   | Send block.
   | A | WRITE TEXT ":SYSTEM:ERROR?" EOL   | X | Query for error.
   |   | READ TEXT X STR                   |   | Get back error string.
   |   |                                   |   |
   +---+-----------------------------------+---+
 
The first transaction sends the ":SYSTEM:SETUP " string to start the learn string download. Note the " " at the end of the string; if you don't use that you'll get a bad-parameter error. This is immediately followed (no intervening EOL) by the block, which is terminated by an EOL.

The last two transactions simply get the error status from the scope. If all goes well, it will return a "+0".

* The preceding steps should be enough to allow you to work with arbitrary-block data with VEE, but there are a few fine points to consider further.

The keys to these features are found in the "I/O -> Instrument -> Edit Instrument -> Direct I/O Configuration" dialog:


   +----------------------------------------+
   |         Direct I/O Configuration       |
   +----------------------------------------+
   | Read Terminator       [     "\n"     ] |
   | Write                                  |
   |  Eol Sequence         [     "\n"     ] |
   |  Multi-Field as:      [   Data Only  ] |
   |  Array Separator:     [      "."     ] |
   |  END (EOI) on EOL:    [      NO      ] |
   |                                        |
   | Conformance:          [   IEEE 488   ] |
   | Binblock:             [     None     ] |
   | State (Learn String): [ Not Config'd ] |
   |                                        |
   |      [  OK  ]            [ Help ]      |
   |                                        |
   +----------------------------------------+
 
The relevant fields are the last three -- "Conformance", "Binblock", and "State (Learn String)".

The "Conformance" field specifies the binary-block transfer standard level: "IEEE 488" or "IEEE 488.2". If you specify "IEEE 488", you can select from the available block-transfer methods allowed under the original 488 spec; if you specify "IEEE 488.2", only the block-transfer methods defined in the more-specific 488.2 spec are allowed.

Let's assume for the moment that we stay with "IEEE 488" conformance (488.2 conformance has some implications that we need to lay some groundwork for). Then you can specify the block-transfer protocol using the "Binblock" field in the configuration dialog:


   +-----------------------------------------+
   |             Select Binblock:            |
   +--------+-----------------------+--------+
   |        | None                  |        |
   |        | #A                    |        |
   |        | #I                    |        |
   |        | #T                    |        |
   |        |                       |        |
   |        |                       |        |
   +--------+-----------------------+--------+
   | None                                    |
   +-----------------------------------------+
   |        [  OK  ]        [ Cancel ]       |
   +-----------------------------------------+
 
If you specify "None", the DIO object will generate definite-length arbitrary-block data as defined at the beginning of this document. The other selections generate data in one of the "IEEE 728" block data formats:

   #A<count><data>
   #I<data><EOI>
   #T<count><data>
 
The DIO object will be able to read all these formats (as well as the so-called "indefinite-length arbitrary block format" defined as "#0<data><EOI>").

* The "State (Learn String)" field provides an additional capability for DIO objects: the ability to read and write an instrument state through learn strings.

While the example that forms the main body of this article used learn strings to demonstrate how to perform arbitrary-block transfers, it only used them as a form of data little different from any other. But the DIO object allows you to store an instrument state in a cleaner fashion: you can read the instrument state using the DIO object's "Upload State" menu entry, and you can restore the instrument to a previous state using the WRITE STATE transaction. (There is no READ STATE transaction and no way to get the "Upload State" menu entry to an input pin.)

If you leave this field at "Not Configured", this scheme will not work. If you switch this to "Configured", you get two more fields to specify the learn string commands:


   |                                        |
   | Conformance:          [   IEEE 488   ] |
   | Binblock:             [     None     ] |
   | State (Learn String): [  Configured  ] |
   |  Upload String:       [      ""      ] |
   |  Download String:     [      ""      ] |
   |                                        |
   |      [  OK  ]            [ Help ]      |
   |                                        |
   +----------------------------------------+
 
In the case of the 54600 scope, these fields could be set to:

   | State (Learn String): [  Configured  ] |
   |  Upload String:       [   "*LRN?"    ] |
   |  Download String:     [":SYST:SETUP" ] |
   |                                        |
   |      [  OK  ]            [ Help ]      |
   |                                        |
   +----------------------------------------+
 
* This is all appropriate if you leave the "Conformance" to "IEEE 488.2". If you switch to "IEEE 488", the dialog simplifies to:

   | Conformance:          [  IEEE 488.2  ] |
   |                                        |
   |      [  OK  ]            [ Help ]      |
   |                                        |
   +----------------------------------------+
 
Where did all the fields go? They went away because they were no longer necessary. The "Binblock" format now is restricted to definite-length arbitrary-block format on output, and to definite- or indefinite-length arbitrary-block format on input. The "State (Learn String)" field is assumed to be configured to the default 488.2 commands, "*LRN?" and "*LRN".

 TOP OF PAGE

[9.2] BUILDING A FIXED LENGTH BINBLOCK HEADER

* In some cases, reading arbitrary-block data with VEE using a BINBLOCK transaction won't work, for a rather subtle reason. Certain instruments will send learn strings with this scheme and will assume that "N" is 8 and pad with zeroes. For example:


   #800000128<byte1><byte2> ... <byte128>
 
This isn't a problem in itself, it's acceptable within spec. The problem is that they may not accept the learn string back unless the header is in exactly the same format. Sending back:

   #3128<byte1><byte2> ... <byte128>
 
-- gives a device error, even though it is legal.

This leads to trouble with VEE because if you read a BINBLOCK under VEE and store it to a file, any leading zeroes in the BINBLOCK header will be lost -- you can read the learn string in from the device, but you can't write it back out to the device without getting a device error. The only alternative is to reconstruct the binblock header "manually".

* The first thing to do is to make sure the learn string is not stored as a BINBLOCK, just stored as binary data without a header. Since you have to reconstruct the header anyway, no point in storing it. You could do this with:


                                          +----------------------------+
                                          |           To File          |
   +----------------------------------+   +---+------------------------+
   |            Direct I/O            |   |   | To File: [LRNSTR.BIN]  |
   +------------------------------+---+   |   +------------------------+
   | READ BINBLOCK x BYTE ARRAY:* | x +-->| a | WRITE BINARY A BYTE    |
   +------------------------------+---+   |   |                        |
                                          +---+------------------------+
 
To send it back to the device, you use the following objects:

   +--------------------------------+
   |            From File           |
   +----------------------------+---+
   |   From File: [LRNSTR.BIN]  |   |
   +----------------------------+   |      +---------+   +---------+
   | READ BINARY X BYTE ARRAY:* | X +--+-->| totSize +-->| Formula +---+
   |                            |   |  |   +---------+   +---------+   |
   +----------------------------+---+  |                               |
                                       |    +---<-- BINBLOCK header ---+
                                       |    |
                                     data   |   +--------------------------+
                                       |    |   |         Direct I/O       |
                                       |    |   +---+----------------------+
                                       |    +-->| A | WRITE TEXT A STR     |
                                       +------->| B | WRITE BINARY B BYTE  |
                                                +---+----------------------+
 
Source for an extended version of this program can be found in xfixblok.vee.

The trick here is the Formula box that creates the BINBLOCK header. It accepts the array size and outputs a header with leading zeroes of the form:


  #800000128
 
The output Direct I/O box then outputs this header, followed by the learn string data bytes.

The Formula box contains:


  "#8" + strFromLen( "00000000", 0, 8-StrLen(asText(A))) + asText(A)
 
To figure this out, please note that "+" is a string-concatenation operator under VEE, and "asText()" converts a numeric value to a string (this isn't a documented function for some odd reason). So the Formula concatenates the "#8" with a string of padding zeroes, and ends with an ASCII string giving the numeric digits.

Creating the string of padding zeroes of the proper length is the central trick, and it uses the function:


   strFromLen( <string>, <start_index>, <string_length> )
 
-- which extracts a substring of a given length from a given string, starting from a given index value. In this case we have a string of 8 zeroes ("00000000") and take a substring of it, starting at the beginning of the source string ( 0 ) and of the length of the required number of padding zeroes ( 8-StrLen(AsText(A)) ). Works like a charm.

Now there is still the problem of termination conditions for the output binary block: does it require a line-feed or not? How about an EOI? However, that concern is left as an exercise for the reader.

 TOP OF PAGE

[9.3] SCOPE DRIVERS & TIMEOUTS

* We occasionally get complaints from users concerning the operation of trace-type devices, such as oscilloscopes. For example, an HP field person reported a problem with VEE and a scope driver with a program as follows:


          +-----------------------------------+
          |               54600               | state (panel) driver
          +----------+------------------------+
  +---+   |          |                        |
  | 1 +-->| VIEW_CH1 |                        |
  +---+   |          |                        |
          |          |                        |
  +---+   |          |                        |
  | 0 +-->| VIEW_CH2 |                        |
  +---+   |          |                        |
          +----------+------+-----------------+
                            |
			 +--+--+
			 | OK  +--+
			 +-----+  |
                                  |
                          +-------+-------+
                          |     54600     | component driver
                          +------+--------+
                          |      | WF_CH2 +-->  To XY Graph
                          +------+--------+
 
Note that "VIEW_CH1" is activated, while the component driver is trying to fetch the waveform from channel 2.

If you run the program, it executes up the OK button and stops. No trouble so far. Then you hit the OK button, and the 54600 reports on its display that there is a "Setting Conflict", but VEE never gives a timeout error and VEE won't respond to user inputs.

Actually, what is happening is that the instrument driver has sent the command to get the waveform and is doing serial polls to see if the command is complete. All the serial polls work perfectly without timing out, so the driver stays stuck in that loop forever.

The solution is to either turn off the scope and let the serial poll time out (not much of a problem to do since the VEE program handles all the scope settings anyway, and little is lost), or add additional logic to the VEE program to get more control.

* In general, such a problem will occur in a scope the instrument setup sets "Sweep Mode" to "Triggered", indicating that you want the instrument to wait to make a waveform acquisition until a trigger signal of some sort occurs at its input connectors. If the trigger condition never occurs -- say, because the scope inputs aren't connected to the signal source -- then the scope driver is hung up indefinitely. The illustration below shows a simple VEE program which could potentially see this problem. Note that the DIGITIZE data input terminal is the stimulus which causes the scope to try to acquire a waveform.


           +------------------------------------------------------------+
           |                     E1426A (NOT LIVE)                      |
           +--------+----------------------------+----------------------+
           |        |                            |[Reset] [ Main Panel ]|
           |        |                            +----------------------+
           |        |                            | Timebase   [ 100 n  ]|
 +-----+   |        |                            | Sweep Mode [ Trig'd ]|
 |1    +-->|DIGITIZE|                            | Trg [1][  5  ][ Pos ]+--+
 +-----+   |  Int16 |                            | CH1 [  500 m ][  1  ]|  |
           |        +----------------------------+ CH2 [  500 m ][  1  ]|  |
           |        |[no inst]   [ZOOM IN]       | CH3 [  500 m ][  1  ]|  |
           |        |[ Dig ][ Autoscale  ][ Run ]| CH4 [  500 m ][  1  ]|  |
           |        |[ CH1 ][ CH2 ][ CH3 ][ CH3 ]|                      |  |
           +--------+----------------------------+----------------------+  |
                                                                           |
                                      +------------------------------------+
                                      |
                                      |   +-------------+ 
                                      +-->| Do Whatever |
                                          +-------------+
 
Once the driver is executed, control flow stays stuck there until it is done what is trying to do, or it times out.

The normal way to get a measurement from a scope is with a DIGITIZE command. This tells the scope to acquire a new waveform and copy it into its internal memory. Sending a new command before DIGITIZE has completed may give you an error message, and so to perform this operation, you:

These are the steps the scope IDs perform when you ask them to give you a waveform. Now if the scope never sees a signal that satisfies its trigger conditions, the DIGITIZE command will not complete, and the ID will just keep looping mindlessly for a bit that will never be set. Each serial poll works just fine, however, so you never get a timeout.

* The solution to this problem is to break the sequence of instructions into separate objects that perform the steps outlined above. This gives you the opportunity to add a timeout mechanism to keep from hanging up VEE.

The first step in doing this is to configure the scope by setting up an instrument driver, and then command it to DIGITIZE a waveform:


       +---------------------------------------------------+
       |                E1426A (NOT LIVE)                  |
       +----------------------------+----------------------+
       |                            |[Reset] [ Main Panel ]|
       |                            +----------------------+
       |                            | Timebase   [ 100 n  ]|
       |                            | Sweep Mode [ Trig'd ]| Panel
       |                            | Trg [1][  5  ][ Pos ]|   
       |                            | CH1 [  500 m ][  1  ]|   
       +----------------------------+ CH2 [  500 m ][  1  ]|   
       |[no inst]   [ZOOM IN]       | CH3 [  500 m ][  1  ]|   
       |[ Dig ][ Autoscale  ][ Run ]| CH4 [  500 m ][  1  ]|   
       |[ CH1 ][ CH2 ][ CH3 ][ CH3 ]|                      |   
       +-------------------------+--+----------------------+   
                                 |
   +-----------------------------+-----------------------------+
   |                    E1426A (NOT LIVE)                      |
   +-----------------------------------------------------------+
   | WRITE TEXT ":WAVEFORM:FORMAT:WORD;:SOURCE:CHANNEL1" EOL   |
   | WRITE TEXT "*CLS;*SRE 32;*ESE 1" EOL                      | DIO1
   | WRITE TEXT ":DIGITIZE:CHANNEL1;*OPC" EOL                  |
   |                                                           |
   |                                                           |
   +-----------------------------+-----------------------------+
                                 |
 
The instrument driver sets up the instrument to the desired general configuration, and then the DIO box sends the commands to perform the digitization. The first transaction sets up the waveform format and channel; the second one programs the scope to set a status flag when it has completed an operation; and the third instructs the scope to DIGITIZE the waveform.

The second transaction line -- WRITE TEXT "*CLS;*SRE 32;*ESE 1" EOL -- is of particular interest. The *CLS instruction causes the scope to clear its status byte, so that we don't mistake any status from an earlier scope operation for the current one. The *ESE 1 instruction tells the scope to set bit 0 of its event status register (the "operation complete" bit) when requested operations complete. The *SRE 32 instruction allows an event from the event status register to propagate to the status byte register and be reflected in bit 5 of the scope's status byte.

So, the combination of *ESE 1 and *SRE 32 tells the scope to set the event status bit in the status byte register when an operation -- a DIGITIZE in this case -- has completed so we can check for completion with a serial poll.

This configured, the third line sends the DIGITIZE command, followed by the *OPC command to tell the scope to update the contents of the status byte register when the conditions set in the event status register become true -- or, in this case, when DIGITIZE has completed, set bit 5 in the status byte.

Having sent the commands to perform the DIGITIZE, the next thing to do is perform the serial polls (and time out if we don't get the status bit set within the proper interval). These objects do the serial poll in a loop:


       transactions in 
    previous illustration
              |                                         +-----> value
          +---+---+                                     |
          | now() +-------------------------------------|-----> initial
          +---+---+                                     |        time
              |                                         |
       +------+------+                                  |
       | Until Break +----+                             |
       +-------------+    |                             |
                          |                             |
          +---------------+-----------------------+     |
          |         E1426A (NOT LIVE)             |     |
          +------------------------------+--------+     |
          | Device [ E1426A (NOT LIVE) ] |        |     |
          | Event  [      Spoll        ] |        |     |
          | Action [     NO WAIT       ] | Status +-----+          
          | Mask   [       #H0         ] |        |
          |                              |        | Spoll Scope
          +------------------------------+--------+
 
-- and these objects interpret the results:

            +--------------+     +--------------------------------+
            |   Formula    |     |         If/Then/Else           |
  spoll     +--------------+     +-------+--------------+---------+
  value  -->| bitAnd(a,32) +---->| A Any |[ A>0        ]|  Then   +-----+
            +-------+------+     |       |              |         |     |
 initial -----------|----------->| B Any |[ C>(B+5)    ]| Else if +--+  |
  time          +---+---+        |       |              |         |  |  |
                | now() +------->| C Any |     Else     |  Else   |  |  |
                +-------+        +-------+--------------+---------+  |  |
                                                                     |  |
                 +---------------------------------------------------+  |
                 |                                                      |
                 |                              +-----------------------+
                 |                              |
   +-------------+-------------+        +-------+--------+     +----------+
   |     E1426A (NOT LIVE)     |        | get scope data +---->|          |
   +---------------------------+        +-------+--------+     | XY Trace |
   | EXECUTE CLEAR             |                |           +->|          |
   |                           |            +---+---+       |  +-----+----+
   |                           |            | Break |       +--------+ 
   +-------------+-------------+            +-------+
                 |
          +------+------+
          | Raise Error |
          +-------------+
 
The status byte is ANDed with 32 to mask for bit 5, which was defined above as the bit that indicates the DIGITIZE has completed. If it is set, as determined by the If-Then-Else box, the program gets the scope data and displays it (and breaks out of the polling loop).

If it isn't set, the If-Then-Else object checks to see if the elapsed time is greater than five seconds; if it is, the program performs error handling. If not, the loop is repeated.

The "get scope data" box above is a UserObject that contains the following pieces:


  +--------------------------------------------------------------------------+
  |                              U_GetScope                                  |
  +------------------------------------------------------------------------+-+
  |                                                                        | |
  |  +-------------------------------------+                               | |
  |  |          E1426A (NOT LIVE)          |                               | |
  |  +-------------------------------------+                               | |
  |  | WRITE TEXT ":WAVEFORM:DATA?" EOL    |                               | |
  |  |                                     |                               | |
  |  |                                     |                               | |
  |  +------------------+------------------+                               | |
  |                     |                                                  | |
  |      +--------------+                                                  | |
  |      |                         +--------------+                        | |
  |  +---+---+              +----->|         Then +-----+                  | |
  |  | now() +--------------|----->| If/Then/Else |     |                  | |
  |  +---+---+              |  +-->|       ElseIf +--+  |              +-->|X|
  |      |       +-------+  |  |   +--------------+  |  |              |   | |
  |      |       | now() +--|--+                     |  |              |   | |
  |      |       +-------+  |      +-----------------+  |              |   | |
  |  +---+---+              |      |                    |              |   | |
  |  | Until |              |  +---+---+    +-----------+-----------+  |   | |
  |  | Break +--+           |  | Raise |    |   E1426A (NOT LIVE    |  |   | |
  |  +-------+  |           |  | Error |    +-----------------------+  |   | |
  |             |           |  +-------+    |[READ BINBLOCK x INT16]+--+   | |
  |             |           |               |                       |      | |
  |             |           |               +-----------+-----------+      | |
  |             |           |                           |                  | |
  |             |           +-------------------+   +---+---+              | |
  |             |                               |   | Break |              | |
  |   +---------+---------+   +--------------+  |   +-------+              | |
  |   | E1426A (NOT LIVE) +-->| bitAnd(A,16) +--+                          | |
  |   +-------------------+   +--------------+                             | |
  |         Spoll Scope                                                    | |
  +------------------------------------------------------------------------+-+
 
This UserObject requests the waveform and performs serial polls in a fashion similar to that used for the DIGITIZE. The WAVEFORM:DATA? command is sent, and then serial polls are conducted to get the status byte. The UserObject tests for bit 4 of the status byte, which indicates that a measurement is available, and the polls are conducted in a loop that tests for a timeout. When the measurement is available, it is returned with a READ BINBLOCK transaction.

Note that performing all these tasks in a UserObject allows you to create a UserFunction that could be called from other parts of the program to perform the same task. UserObjects also allow you to add an error-output pin, and so provide a clean error-output mechanism.

* After going through all this, the question is: why is it necessary? If an error would cause this problem, why not check for an error before trying to get the waveform?

The answer relates to the "use model" of the scope. In a practical test setup, the act of requesting that the scope digitize a waveform causes it to arm its trigger and wait for a trace. If the scope is reading a continuous waveform, the trigger occurs (effectively) immediately, a trace is digitized, and the waveform is returned. No problem.

But if you're trying to capture a single-trace event, the scope will arm its trigger and then wait for the trigger event to occur. In this case, assuming that the trigger will happen immediately is not valid. The trigger will occur when the user gets around to pushing a button, or another instrument generates a trigger, or whatever, something that could take much longer than the timeout period to occur. It's a "real-time" event, in the sense that it happens whenever it wants to.

This is why the serial-polling scheme is used in the scope driver. Relying on the timeout period would cause more trouble than cycling through serial polls in a continuous loop. Since the scope can in principle hang indefinitely waiting for the trigger, the design decision was made so that the driver would, too.

But then the next question arises: if there's actually an error on the scope, then the polling loop will go forever. So why not check for an error after sending the digitizing commands?

If there is an error, this would work OK. You'll get back an error code and be able to terminate the loop. However, if you don't have an error, you won't get back an error code until the scope has completed the digitizing operation. Since we're working on the assumption that the trigger is a real-time (indefinite) event, the digitizing operation can take as long as it likes, and timing out on the error query would defeat the purpose of performing serial polls.

In short, the scope ID features a design trade-off for the sake of simplicity of construction and operation. It could be argued that the driver should have options to allow the user more choice, but once we start moving up to that level of enhanced complexity then the user might as well just go to a lower level of detail (using Direct I/O and the like) and be done with it.

 TOP OF PAGE

[9.4] PERFORMING DEVICE-TO-PRINTER DUMPS

* One of the features introduced in VEE 3.0 was the ability to release the HPIB ATN line under program control. This leads to the obvious question: so who cares?

The ability to release ATN under program control is only used in two circumstances:

Under normal circumstances, all VEE HPIB transactions suppose that VEE is either the talker or listener and a single remote device is the corresponding listener or talker. The bus transactions that VEE generates with instrument drivers or Direct I/O all are designed according to this assumption.

The two circumstances outlined above, however, require that VEE set up a remote device as a talker and one or more remote devices as listeners. The standard VEE HPIB transactions won't allow this, but you can (as of VEE 3.0) use the Interface Operations object to create the proper HPIB transactions.

For example, I have a 54600 scope. I can send it a "PRINT?" query, and when I then address it to talk, it will dump its display (in HP standard printer graphics format) to the HPIB, with the assumption that there is a printer out there listening so it can print the data.

My 54600 has an HPIB address of 13. I have a ThinkJet printer with an address of 1. I can direct the 54600 to dump its display to the ThinkJet by using direct I/O to send the "PRINT?" command, and then by using an interface operations object to perform the following HPIB transactions:


   UNLISTEN
   UNTALK
   LISTEN 1
   TALK 13
   RELEASE ATN
 
This can be done with the following program (see the file xprscope.vee for the source):

   +--------------------------+
   |   54600 (54600A @ 713)   |
   +--------------------------+
   | WRITE TEXT "PRINT?" EOL  |
   |                          |
   +-------------+------------+
                 |
   +-------------+------------+
   | Interface Op's hpib7 @ 7 |
   +--------------------------+
   | SEND UNL                 |
   | SEND UNT                 |
   | SEND LISTEN 1            |
   | SEND TALK 13             |
   | SEND DATA ""             |
   +--------------------------+
 
Note how ATN is released with the Interface Operations transaction:

   SEND DATA ""
 
This was the transaction that was missing in earlier versions of VEE.

 TOP OF PAGE


 LAST PAGE  BACK TO INDEX  NEXT PAGE