Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Embedded Systems

Implementing the gpib


MAY91: IMPLEMENTING THE GPIB

Don is a consulting engineer in the area of embedded systems and automation and can be contacted in care of Don Morgan Electronics, 2669 N. Wanda, Simi Valley, CA 93065.


In "Understanding the GPIB" (DDJ, April 1991), I presented an overview of the general-purpose instrumentation bus (GPIB), a short tutorial, and an example of its use in a real application. That application involved using a host computer to program an oscilloscope to trigger on predetermined signals. The oscilloscope would then alert the host that the data collection was done, whereupon the host would instruct the oscilloscope to transmit the data to the printer/plotter, which would print the data.

In this article, I continue that example, but from the point of view of an embedded system. I present the TMS9914A, a very popular chip for implementing 488.1 functionality of the GPIB, and show how to develop polled and interrupt-driven routines for talking, listening, and generating an SRQ on the bus.

The TMS9914A provides all the functionality of the IEEE 488 1975/78 standards and the IEEE 488A 1980 supplement. Although it is not capable of meeting all the requirements of the IEEE 488.2, it does provide the basic functionality of the IEEE 488.1, which every device must meet to exist on the GPIB.

The TMS9914A

As far as hardware goes, the 9914A provides almost everything necessary for a complete bus interface. The chip, two drivers, and one OR gate will do everything--including parallel polling on command. What's more, there is nothing complex about laying out the circuit from the 9914A to the bus, because the pins are named after one another and appropriately for the bus.

There are a few caveats, however. TI adopted an alternate bit-ordering convention for the chip, with D0 as the Most Significant Bit and D7 the Least Significant Bit. This can cause confusion and waste your time. So as not to create any further confusion, I will follow the same numbering system TI uses in their data sheets, with reminders where appropriate.

The remaining logic lines are fairly routine. The register select lines, RS0 through RS2, follow the usual pattern, with RS0 as the Least Significant Bit. The Chip Enable, Write Enable, and reset are active low, while the Read Enable is active high.

Beyond these points, the 9914A is fully featured. It can be clocked over a wide range, from 500KHz to 5MHz, without any real degradation of performance. In addition the duty cycle is equally forgiving, allowing it to be developed from a number of possible sources, including the clock of a local microcontroller or its ALE line.

Communications Techniques

The form of the communications driver necessary is dependent on the system and the degree of efficiency required of the instrument and the bus. The GPIB was designed to operate at data rates up to 1 MHz, and this is possible if all the electrical constraints described in the 488.1 specification are met; possible, but not always practical. Even clocking the 9914A at the minimum clock rate will only contribute microseconds to a transfer. In the end, the true pacing item for a device on the bus, and therefore the bus, is the software implementation of the Interface.

The IEEE 488 bus is a bit-parallel/ byte-serial bus employing a unique three-wire handshake that requires that the current byte be accepted by all listeners on the bus before the next can be placed there. The individual device does this by releasing the NFRD\ (Not Ready For Data) line. This, of course, has great advantages when it comes to interfacing devices of different data rates and performance, but also means that the bus can only operate as fast as its slowest acceptor.

The TMS9914A will not complete the handshake (release the NFRD\ line) until the most recent data byte has been removed from the Data In register, or any special hold-off has been removed. Assuming no auxiliary hold-off, the time it takes to handle the chip at a local level determines the speed of the transfer. And this, almost always, is dependent on software.

All other factors aside, polling the interface for data is the simplest approach but invariably results in a slower bus transfer. This can mean that the faster instrumentation must wait and that longer time-outs will be required. Still, it isn't always possible to interrupt a critical process within a device. I was recently involved in the development of a microstepping indexer designed to drive stepper motors for stage positioning. This indexer had to be optimized for speed and accuracy in its moves. Even using a fast microprocessor, there were times when allowing a bus transfer of any length could tie up the processor and distort the move.

As mentioned earlier, the bus handshake depends upon the release of the NFRD\. Normally, the 9914A releases this line after the current byte is read from the Data In register, which means that if the current talker is attempting to send a string to the current listener(s), the next byte will be placed on the bus almost as soon as the last is taken out. In an interrupt-driven scheme, this can sometimes mean that you are on your way back to the routine as soon as you leave it.

If you opt for interrupt-driven or DMA transfers, there are some things that can be done to help. The interrupt could be turned off in certain situations and the interface polled only when it is safe. Alternatively, there is an "auxiliary command" available that will allow the system to "hold off" the NFRD\ until explicitly released by the host processor. In this case, the interrupt can remain enabled and the microprocessor can pace the transfer of data over the bus, based on its needs.

The 9914A also provides for DMA. To use this feature, the My Address bit in Interrupt Register 1 must be set. This will generate an interrupt and a Data Accepted (DAC) hold-off whenever the 9914A is addressed to talk or listen. As part of the service routine, the microprocessor must look in the Command Pass-Through register to see whether it received a My Talk Address (MTA) or a My Listen Address (MLA), initialize the system according to the need, release the hold-off, and the transfer will occur. Using this technique, entire strings can be read in the shortest possible time. Alone or in combination with other approaches, this can provide for the fastest bus and fastest local processing possible.

One more note about the DMA lines, ACCRQ\ and ACCGR\. If DMA is not going to be used, ACCGR\ must be held high or the data lines will pull the bus down. In addition, ACCRQ\ can be used as a separate interrupt for BI and BO, making it unnecessary to read Interrupt Register 0 for these bits.

A Simple Listener

To begin with, we need to know how to listen on the bus. At power up (or with a hardware reset), the TMS9914A is placed in swrst, software reset. While it is in swrst, it is not capable of taking part in any activity on the bus; the device must be configured and swrst cleared first.

  1. Place the address of the device into the five LSBs of the 9914A's address register. To talk or listen, a device must be addressed to do so.
  2. See that bit 1 of the address register (DO is MSB) is cleared to allow the listener function.
  3. Clear the interrupt mask registers; these bits come up in random fashion at power on. In a polling scheme, the mask registers are not so important because the bits in the interrupt status registers are set and reset regardless of the state of the mask register. It is good practice, however, to clear them.
  4. Clear the swrst auxiliary command. The TMS9914A may now exist on the bus.
  5. Wait for the BI (Byte In) bit in the Interrupt Register 0 to go high. This bit indicates that the TMS9914A has been addressed and received a byte. In the simplest listener form, a loop can be written that reads Int 0, checks for the BI, and reads the Data In register when it is true. (Remember that reading from Int Register 0 or from the Data In register clears the BI bit and any other interrupt represented there; save this register if you are concerned about any other conditions.)
The above algorithm implements a polled listener function. To go from there to interrupt-driven, it is only necessary that in step 3, instead of just clearing both Interrupt mask registers, we set the BI mask bit afterwards. Now when the TMS9914A is addressed and receives a byte, it will lower its Int\ line to the microprocessor. This means that polling the Interrupt 0 Register is no longer required, and other processing can be done while waiting for Data In.

A Simple Talker

This talker is not much different from the listener. At power on or a hard reset:

  1. Set up the address register with the address in the five least significant bits.
  2. This time, see that bit 2 (d0 is MSB) of the address register is low to enable the talk function.
  3. Clear the interrupt mask registers.
  4. Clear swrst. As before, this is set by the hardware reset and must be cleared before the 9914A can talk or listen on the bus.
  5. Monitor the Byte Out (BO) bit in Int 0. When it goes high, the 9914A has been addressed to talk and is waiting for a byte. In polled mode, it is a simple matter to watch this bit and supply a byte whenever it goes high.
As with the listener mode, it is a short move from polled to interrupt-driven. After the interrupt mask registers have been cleared in step 3, set the BO bit. Now, Int\ on the TMS9914A will go low whenever the device has been addressed to talk and is ready for a byte. Being able to talk and listen on the bus are the two fundamental requisites of any commun cations driver. For real efficiency, though, the instrument must have a way to interrupt the controller when it needs servicing.

Serial and Parallel Polling

The 9914A allows for both parallel and serial polling and both methods involve the use of the SRQ command line. In either case, it is set low to indicate to the controller that the device requires service.

The original way to set the SRQ line low was to set bit 1 (remember that D0 is the MSB) of the Serial Poll Register, then, after the device had been serviced, reset bit 1. There was a problem with this method that resulted in the possibility of a device appearing to request service again if another device requests service before bit 1 of the serial poll register for the first device is cleared. The advisable way to set SRQ low is to use the auxiliary command, "Request Service Bit 2." Here, the bit is cleared automatically as soon as the controller accepts the serial poll status byte.

Each device is to have a serial poll status byte. It is here that current data about the state of the instrument is kept. Originally, only one bit in the serial poll status byte, the RQS, was defined, the idea being that it would be set to indicate that the device needed service. The rest of the serial poll status bits, according to 488.1, were to be defined by the manufacturer of the instrument carrying the interface. Often they are pertinent only to the device, but just as often they carry meaning common to many devices on the bus, such as message available, overrun, process error, operation complete.

The reason, in fact, for so many of the 488.2 extensions is that there were common, or "standard," events that might just as well be placed in a byte by themselves with any one of them being reason enough for setting a bit in the serial poll status byte. If you are interested in these and the many other additions to the 488.2, please see the IEEE 488.2 document listed in the bibliography.

Typically, the instrument designer will maintain a mask for the serial status byte. In it, he can set or reset a mask bit for each of the status bits. When that bit is unmasked and true, he will request service, but this is not at all automatic. IEEE.1 only allows for the status byte and the RQS; it is up to the equipment manufacturer to do the rest.

During a serial poll, the controller must poll each device individually to ascertain which one(s) need servicing. During the poll, the controller reads the serial poll status byte of each device. This byte should be kept current by the system software.

To perform a serial poll, the controller need only place a Serial Poll Enable (SPE) on the bus, make each device a talker, read the serial poll status byte from each device, issue Serial Poll Disable (SPD), and handle the request(s).

The parallel poll can be somewhat more complex to implement than the serial poll but can increase response time on the part of the controller many times over the serial poll. With the parallel poll, the controller can ask up to eight devices at a time to set a bit on the IEEE 488 bus. The controller can then read this byte and determine which device(s) needs attention.

Using the parallel poll requires that the local processor be told over the interface, or read from memory or switches, which bit to set or reset, in the parallel poll register. This done, the parallel poll may be issued, the bus read by the controller, and the appropriate devices serviced.

The advantage here is speed with an increase in the complexity of both the controller and the instrument. It has the drawback of limited information: A bit can only be made to set or reset upon condition. It is that bit that is placed on the bus during the parallel poll.

A Communications Handler

Listings One and Two (page 96) contain three functions that implement the foregoing discussion. Although the original for these drivers was written in 8OC196 assembly language, I have rewritten it in C for portability. Without a specific target, an application such as this can, at best, be somewhat sketchy because the hardware and architecture are not known. The code was written to provide a more concrete example of the contents of this article and with an eye to fitting it into the user's system.

The GPB.H header file in Listing Two provides the needed defines with indications as to where the particular address of the target system should go. Here, too, are the global variables serial_poll and serial_poll_mask that are used to record the current state of the instrument and set the conditions that will cause an SRQ.

Listing One gives an example of the initialization code for the TMS9914A, an integrated interrupt handler, and a routine for checking serial_poll and generating an SRQ when appropriate. What is missing is a function to set bits in serial_poll_mask. All that is needed is the creation of a command to set and reset individual bits in the mask. I felt that this was such an intimate part of the target system, involving the commands used and the manner in which they are parsed, that it would best be left up to the author of that software.

Bibliography

HP 54502A 40OMHz Digitizing Oscilloscope Programming Reference. First Edition. Hewlett-Packard, June 1989.

IEEE Standard Digital Interface for Programmable Instrumentation. ANSI/IEEE std 488.1-1987. New York, N.Y.: The Institute of Electrical and Electronics Engineers, 1987.

IEEE Standard Codes, Formats, Protocols, and Common Commands For Use with ANSI/IEEE Std 488.1-1987 IEEE Standard Digital Interface for Programmable Instrumentation. ANSI/IEEE std 488.2-1987. New York, N.Y.: The Institute of Electrical and Electronics Engineers, 1987.

TMS9914A General Purpose Interface Bus (GPIB) Controller. Texas Instruments, 1982.

TMS9914A GPIB Controller User's Guide, System Interface Controllers. Texas Instruments, 1985.


_IMPLEMENTING THE GPIB_
by Don Morgan


[LISTING ONE]
<a name="011d_000c">

#include <conio.h>
#include "gpib.h"    /*local header file containing declares and defines */

/*Reinitialize local interface, whether from power up or device or interface
clear. It will do nothing, however to initialize system interrupts to take
advantage of TMS9914A interrupts; you do that */
void Gpib_Init()
{
    int address;
    outp(AUXCMD,SWRST);
            /*software reset*/
    address = (inp(DIPSW) & 0x1f);
           /* Read dipswitch for address of device. Lower 5 bits carry legal
           address of device, clearing upper bits and enables talker & listener
           functions, as well as, disabling dual primary addressing mode.*/
    outp(ADDRES,address);
    outp(IMSK0,BI | BO | SPAS | END);
                 /*set the interrupts BI, BO, SPAS, and END*/
    outp(IMSK1, DCAS | IFC);
                /*and DCAS and IFC*/
    outp(SERPOL,0x0);
    serial_poll = serial_poll_mask = 0x0;
                /*clear the serial poll byte and mask*/
    outp(AUXCMD,SWRSTC);
                /*clear the software reset*/
 }
/* Each time a condition arrises in the device that sets a bit int the serial
poll status byte, this routine is called. It is passed the mask (MAV or TRG, at
the moment) for the bit to be set and does three things. 1) sets the bit in
the serial poll status byte. 2) sets the corresponding bit in global variable
we created serial_poll. This is for reference, the serial poll status byte is
write only. 3) It checks to see whether this condition is one the user has
determined should cause an SRQ.*/
void Check_Mask(mask);
{
        serial_poll |= mask;
        outp(SERPOL, serial_poll);
        if(serial_poll_mask & mask)
              outp(AUXCMD,RSV2);
}
/* Vectored to by system interrupts.*/
 void Gpib_Int_Han()
 {
    int status_byte;    /*This is where we will save interrupt status
                          registers until we are through with them*/
    int chr;            /*character holder*/
    status_byte = inp(ISTAT0);
                          /*get what is in interrupt status register 0*/
    if(status_byte & SPAS) {
                   /*place routine here to perform any maintainance necessary
                   after controller reads the serial poll byte. If you are
                   using rsv1, you would reset bit 1 (D0 is MSB), otherwise it
                   may be a reset to any bits gone active since the last SRQ.*/
                   }
     if(status_byte & INT0) {
                   /*this bit will be set if there is at least one active,
                   unhandled interrupt in int status 0. The interrupts that we
                   will be concerned about here are: BI, BO, SPAS, and END*/
     if(status_byte & BI) {
                   /*checking for a byte received from the bus*/
           chr = inp(DATIN);  /*get the character*/
           if(status_byte & EOI)
               flag = TRUE;
           else;
               flag = FALSE;
           receive(chr,flag); /*receive is your function that accepts
                              character and places it in whatever buffer
                              scheme is your favorite. flag, is set if an EOI
                              is detected signalling the end of a string.*/
           }
    else { if(status_byte & BO) {
          chr = get_byte();
          if((chr >> 0x8) == TRUE) {
                  outp(AUXCMD, FEOI);
                  serial_poll &= ~MAV;
                  outp(SERPOL,serial_poll);
                  /*since this is the last character of string, we take this
                  time to reset 'message available' bit in serial_poll byte*/
                  }
          outp(AUXCMD,chr);
                 /*Toutine for retrieving next byte from transmit buffer. If
                 you are using the EOI, include some way to indicate that this
                 is last character of string, as FEOI must be placed in
                 auxiliary command register before last character is sent. In
                 this case, I assumed upper byte of chr was used to carry a
                 flag explaining the status of that character*/
          }
       }
   if(status_byte & INT1) {
        inp(ISTAT1);   /*Get contents of interrupt status register 1. Here we
                       are interested in DCAS (device clear) and IFC
                       (interface clear*/
        if((ISTAT1 & DCAS) || (ISTAT1 & IFC)) Gpib_Init();
                       /*whether we receive a device clear or an interface
                       clear, we will do the same thing, reinitialize the
                       local interface*/
        }




<a name="011d_000d">
<a name="011d_000e">
[LISTING TWO]
<a name="011d_000e">

/*GPIB HEADER FILE*/

/* The following are defines for address mapping, they are written assuming
a linear addressing scheme and an 8 bit bus. Depending on the width and
decoding scheme you use, these defines might be different.*/

#define DIPSW ??       /*place dipswitch setting device address address here*/
#define IEEE  ??       /*place base address of TMS9914A here*/
#define IMSK0  IEEE+0  /*interrupt mask register 0*/
#define ISTAT0 IEEE+0  /*interrupt status register 0*/
#define IMASK1 IEEE+1  /*interrupt mask register 1*/
#define ISTAT1 IEEE+1  /*interrupt status register 1*/
#define ADSTAT IEEE+2  /*address status register*/
#define BUSTAT IEEE+3  /*bus status register*/
#define AUXCMD IEEE+3  /*address register*/
#define ADRSWI IEEE+4  /*address switch register*/
#define ADDRES IEEE+4  /*address register*/
#define SERPOL IEEE+5  /*serial poll register*/
#define CMDPAS IEEE+6  /*command pass through register*/
#define PARPOL IEEE+6  /*parallel poll register*/
#define DATIN  IEEE+7  /*data from bus register*/
#define DATOUT IEEE+7  /*data to bus register*/

/*next, define some bit masks to be used in the interrupt routines. These will
be AND'd with interrupt status registers to determine proper action to take*/
#define INT0   0x80   /*interrupt register 0 has an interrupt, int status 0*/
#define INT1   0x40   /*interrupt register 1 has an interrupt, int status 0*/
#define BI     0x20   /*Byte In, int status 0*/
#define B0     0x10   /*Byte Out, int status 0*/
#define DCAS   0x8    /*device clear, int status 1*/
#define EOI    0x8    /*End of Identify, int status 0*/
#define SPAS   0x4    /*serial poll active state, int status 0*/
#define IFC    0x1    /*interface clear, int status 1*/

/*now, defines for some of the auxiliary commands used*/
#define SWRST  0x80    /*sets the software reset*/
#define SWRSTC 0x0     /*clears the software reset*/
#define FEOI   0x88    /*sent prior to last byte in string to indicate end*/
#define RSV2   0x98    /*alternate and preferred method of asserting SRQ*/

/*these bits are used as definitions of serial poll status byte and mask*/
#define MAV    0x20     /*message available*/
#define TRG    0x1      /*trigger*/

/*other stuff*/
#define TRUE 0xff
#define FALSE 0x0

/*declarations*/
int serial_poll;        /*global variable containing a record of the mask
                        written to the serial poll register in the TMS9914A.*/
int serial_poll_mask;
/*for our purposes, the serial poll status byte and serial poll mask byte is
to be defined as follows, remember D0 is MSB:
D0   D1   D2   D3   D4   D5   D6   D7
X    RQS  MAV  X    X    X    X    TRG
RQS is the request for service and is read as a 1 by controller during a
serial poll when this device has asserted an SRQ. MAV is 'message available'
and is set when there is something in the transmit buffer, reset otherwise.
TRG is the trigger bit and is set when the oscilloscope has been triggered.*/

/*function prototypes, these are example only, yours may well be different*/
extern int get_byte(void);
extern void receive(int,int);
extern void Gpib_Int_Han(void);
extern void Gpib_init(void);
extern void Check_Mask(int);


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.