Object-oriented programming examples for the GPIB (IEEE-488) bus
Written by by Stefan Zollner
Lab equipment such as digital voltmeters, temperature controllers, stepper motors, counters, etc. often can be controlled with a computer using a GPIB bus (also called IEEE-488 or HP-IB bus). Some ancient computer disk drives (Commodore C64 or HP-150) were also controlled with a GPIB bus.
What you need
- A GPIB interface card (such as an AT-GPIB/TNT card from National Instruments) to plug into the motherboard of your computer.
- Drivers for the card, sometimes (but not always) available from the manufacturer of the card. Before you buy a card, make sure that the manufacturer offers drivers for your version of the operating system and is willing to support it (fix bugs, if you find them).
- GPIB cables. (See restrictions on the length of cables).
- Some general introduction to the GPIB bus and its control commands. This is sometimes included in the documentation for the GPIB interface card. There are also two very good articles on the GPIB bus some time ago in the journal Computers in Physics (your local university library should have this journal). A quick tutorial can also be found in the 1996 Instrumental Reference and Control Catalog from National Instruments. (Other vendors may have similar offerings.)
- Optional: A GPIB analyzer in case something does not work the way you want it. Unfortunately, Hewlett-Packard no longer manufactures GPIB analyzers the way they used to. I am not aware of any other analyzers from other companies. National Instruments offers a software solution, but it only runs on DOS/WIN-based systems. (Since Microsoft dropped OS/2 as an operating system, National Instruments has not announced new products for OS/2. They are very half-hearted in the support of the OS/2 products they still sell. A support engineer once told me: "If 99% of our customers buy DOS-based products, you have to understand that 99% of my expertise is in this area.")
List of Programming Examples
constants and structure definitions (include file) - GPIB.H
/*************************************************************************\ * GPIB.H * * This file contains the data structure needed to pass GPIB info between* * processes. Created Jan 13 1994 -km * * HISTORY: * * 02/14/94 SZ added gpibinfo.device_string * \*************************************************************************/ // #ifdef gpib_extern // PVOID *pvdQMemory=NULL; // HQUEUE hqQueue=NULLHANDLE; // #else // extern PVOID *pvdQMemory; /* message queue memory pointer */ // extern HQUEUE hqQueue; /* handle to motor/ieee queue */ // #endif #ifndef MESSAGE_LEN #define MESSAGE_LEN 512 #endif #define COUNT_ADD 3 /* Tennelec counter, primary GPIB address */ #define DMM_ADD 12 /* HP voltmeter, primary GPIB address */ #define SADD 96 #define IOTO 12 #define ENDMESS 1 #define EOS 0 #define IBREAD 1 #define IBWRITE 2 #define IBDEV 3 #define IBSTA 4 #define IBONL 5 /* and so on .... */ #define GPIBINFO_defined typedef struct { int COMMAND; /* command ID */ char device_string[20]; /* holds device_string ("DEV1") etc. */ int Gpadd; /* primary address also device ident */ int Gsadd; /*secondary address */ int Gboardadd; /* gpib board address */ int GIOTO; /* I/0 timeout .25 seconds */ int GEndMess; /* send END with last byte or not */ int GEOSDetect; /* 1 for EOS detection 0 for not */ char Write_Data[MESSAGE_LEN]; /*holds data to be written to device */ char Read_Data[MESSAGE_LEN]; /*holds data written from device */ } GPIBINFO;
class definitions (include file) - GPIBCLASS.HPP
/*===========================================================================*\ * GPIBCLASS.HPP * * * * 10 June 1994 * * * * This is the class definition for the GBIBDevice class. * * Other classes are derived from this class (TENNCounter, HP_voltmeter). * * * * Prerequisites: * * A National Instruments GPIB card (we have an AT-GPIB, NI-488.2, catalog * * number 776207-51, now replaced by AT-GPIB/TNT, catalog number 776786-51) * * and OS/2 drivers NI-488.2M (NI catalog number 776763-01) for this board * * version 2.1.2 (or maybe higher). Earlier version of the drivers may not * * work. We also use OS/2 2.1 (with service pack). * * * * Notes: * * I use the OS/2 API calls for accessing the GPIB devices using the National* * instruments device drivers. Certain parameters, such as primary and * * secondary address, Send EOI with writes, etc. will therefore be configured* * using the IBCONF program. These variables can, however, be changed using * * the DosDevIOCtl calls (with DConfWrt as parameter). * \*===========================================================================*/ #ifndef __GPIBCLASS__ #define __GPIBCLASS__ class GPIBDevice { private: short Guflags; /* device configuration flags */ short Gboardadd; /* gpib board address */ short Gpadd; /* primary address */ short Gsadd; /*secondary address */ APIRET GDConfWrt(); /* modify configuration parameters */ BOOL device_present; /* TRUE for a device, FALSE for a board. */ protected: APIRET apiret; short GIOTO; /* GPIB timeout in units of 0.25 seconds */ char device_string[20]; /* DEV1 through DEV16 for board number 1 */ HFILE DevHandle; /* file handle for the device */ ULONG ActionTaken; /* returned from DosOpen */ BOOL initialized; BOOL use_queue; /* use queue for GPIB commands ? */ public: /* member variables */ BOOL SRQ_enabled; /* TRUE if SRQ generated for each response. */ BOOL GEndMess; /* send END with last byte or not */ BOOL GEOSDetect; /* terminate read on EOS detection or not */ short EOSbyte; /* character value if EOS byte (to terminate reads) */ ULONG GPIBError; /* GPIB error, see Table 3-3 */ ULONG cbwritten; /* Bytes written in DosWrite call */ ULONG cbread; /* Bytes read in DosRead call */ USHORT GStatusWord; /* 16-bit GPIB status word, see Table 3-1 */ USHORT GSPOLLWord; /* 16-bit serial poll word */ char GIdentifier[80]; /* Query identifier of the HPIB instrument */ ULONG ulReadings; /* number of readings to be taken */ /* member functions */ GPIBDevice(char* dev_string); /* constructor */ ~GPIBDevice(); /* destructor */ APIRET GInit(char *dev_string); /* initialize the device */ APIRET GDConfRd(); /* read configuration parameters */ APIRET GWrite(char* buffer); /* sends a command to the IEEE */ APIRET GWriteNoQueue(char* buffer); /* sends a command to the IEEE */ // In the Web distribution, these two functions are identical. APIRET GRead(char* buffer,ULONG ulSize); /*receives info from IEEE */ APIRET GReadUShort(USHORT *sint); /* read 16 bits from IEEE bus */ USHORT GStatus(); /* return current status of board or device */ APIRET GSelDevClear() ; /* Clears a Selected Device */ APIRET GoToLocal(); /* tell device to go to local */ APIRET GBWAIT(USHORT usmask); /* wait for GPIB event */ APIRET GSPOLL(); /* update serial poll word */ int Gwait_service_request(); /* wait for SRQ, return 0 for success */ /* board-level functions */ APIRET GSendIFC(); /* assert interface clear line */ APIRET GDevClear(); /* Clears all Devices */ APIRET GCommand(char cmd); /* board level command */ APIRET GUntalk(); /* sets a device to untalk */ APIRET GUnlisten(); /* sets a device to unlisten */ APIRET GONLINE(); /* place device online */ APIRET GsetEOS(short this_eos); /* set EOS byte */ }; /*---------------------------------------------------------------------------*/ #define HP_acquire_slow 1 #define HP_acquire_fast 2 #define us_buffer_size 75750 /* memory size of the voltmeter (number of readings in SINT format) */ class HP_voltmeter : public GPIBDevice /* Class Definition for HP Digital Multimeter */ { private: protected: public: /* member functions */ HP_voltmeter(char *dev_string, int HP_initvar); ~HP_voltmeter(); int HP_reset(int HP_initvar); APIRET HP_init(int HP_initvar); double GetVoltage(); int error(); /* return HP_voltmeter error register */ int check_for_error(); /* do serial poll and check for errors */ APIRET acquire_data(USHORT* buffer,ULONG ulSize); /* acquire data and read into buffer */ /* member variables */ double scale_voltage; /* holds the voltage scale (ISCALE ?) */ double display_voltage; /* just to store the last voltage measured */ char voltage_buffer[40]; /* hold ASCII string for voltage */ BOOL have_error; /* true if there was an error */ int init_var; int error_int; /* holds last HP error code */ char ErrStr[256]; /* holds response from ERRSTR */ }; /*---------------------------------------------------------------------------*/ class TENNCounter : public GPIBDevice { protected: public: TENNCounter(char *dev_string, int TENN_dummy); ~TENNCounter(); APIRET TENN_init(int TENN_dummy); int GGetCounts(); int display_counts; /* just to store the last count rate measured */ char counts_buffer[80]; /* hold ASCII string for message */ }; /*---------------------------------------------------------------------------*/ class SR850Lockin : public GPIBDevice /* Stanford Research SR850 DSP (digital) lock-In amplifier */ { protected: public: SR850Lockin(char *devString, int SR850_dummy); ~SR850Lockin(); APIRET SR850Init(int SR850_dummy); APIRET trig(double *x, double *y); /* send TRIG command, return x and y */ double xlockin, ylockin, rlockin, thetalockin; // updated with trig() double xNoise, yNoise, rNoise; // updated with trig() char read_buffer[80]; /* hold ASCII string for message */ }; /****************************************************************************/ /* End of Class Definitions */ /****************************************************************************/ #endif #ifndef gpib_EXTERN extern GPIBDevice gpibdevice0; extern GPIBDevice gpibdevice1; extern GPIBDevice gpibdevice2; extern TENNCounter tenncounter; extern GPIBDevice gpibdevice4; extern GPIBDevice gpibdevice5; extern GPIBDevice gpibdevice6; extern GPIBDevice gpibdevice7; extern SR850Lockin sr850lockin; extern GPIBDevice gpibdevice9; extern GPIBDevice gpibdevice10; extern GPIBDevice gpibdevice11; extern HP_voltmeter hpvoltmeter; extern GPIBDevice gpibdevice13; extern GPIBDevice gpibdevice14; extern GPIBDevice gpibdevice15; extern GPIBDevice gpibdevice16; /* This will create instances of the GPIBDevice class and open the devices *\ \* for shared access. */ extern GPIBDevice *gpibarray[17]; #endif // The following variables are not needed for the Web distribution. extern BOOL voltmeter_on /* If true, then the checkmark for the voltmeter is ON. This means that */ /* we have a second thread going sampling the voltage on the HP voltmeter. */ #ifdef gpib_EXTERN =FALSE #endif ; extern BOOL counter_on /* If true, then the checkmark for the counter is ON. This means that */ /* we have a second thread going sampling the counts on the counter. */ #ifdef gpib_EXTERN =FALSE #endif ;
Source code for the DLL - GPIB.CPP
/*==============================================================*\ * GPIB.CPP * * Stefan Zollner, Ken Myers, Kurt Jensen * * Comments are sparse here. Please refer to the National Instruments * NI-488.2M function reference manual (Chapter 3: OS/2 API interface * functions) for more information. These class functions really just * implement the methods for the API calls explained in this chapter. * * HISTORY: * * 02/16/94 SZ added read voltage function and serial poll * 02/22/94 SZ some new functions and code for the counter * 02/23/94 SZ removed TENN_init from the TENNCounter constructor * 02/23/94 SZ fixed error in TENN_init * 02/24/94 SZ fixed ptr problem (access violation) in TENN_init * 03/14/94 SZ changed aperture and resolution * 03/16/94 SZ FIXEDZ ON in voltmeter * 04/25/94 SZ new functions GDevConfRd and GDevConfWrt, other changes * 04/25/94 SZ change GRead (don't always do a serial poll) * 05/25/94 SZ changes for fast/slow data acquisition * 06/07/94 SZ change from SREAL to SINT dataformat * 06/08/94 SZ changes for rotating analyzer * 06/11/94 SZ new variable ulReadings * 12/18/94 SZ add error checking * 12/19/94 SZ reset the HP voltmeter and try again when there is a timeout * 02/01/95 SZ clear voltmeter in the destructor * 03/04/95 SZ insert calls to IResourceLock * 06/20/95 DH get noise readings from digital lockin * 07/15/95 SZ create objects for GPIB0 and DEV1 through DEV16 * SZ open and initialized DEVn only on demand * 08/09/95 SZ change traces for lockin, change initializer * 02/09/96 SZ remove the stuff for the queue (standalone) * 08/02/96 SZ change definition for trace 2 to be X/A1 \*==============================================================*/ #pragma info(none) // #define INCL_DOSQUEUES #define INCL_DOSDEVICES #define INCL_DOSFILEMGR #define INCL_DOSERRORS #define INCL_DOSPROCESS #include <stdlib.h> #include <stddef.h> #include <complex.h> #include <float.h> #include <string.h> #include <os2def.h> #include <os2.h> #include <ireslock.hpp> #pragma info(restore) #include "f:\hardware\at-gpib\api\nicode.h" // This file needs to be obtained from National Instruments // OS/2 device driver for NI GPIB-boards #define gpib_EXTERN #include "gpib.h" #include "gpibclass.hpp" // #include "pario.hpp" // #include "/spea300/queues/indxlib/indxtrn.hpp" // #include "../elipso1/elipso.hpp" GPIBINFO gpibinfo; IPrivateResource gpibKey; IPrivateResource voltmeterKey; IPrivateResource counterKey; IPrivateResource lockinKey; ULONG ulFileHandles=20UL; /* By default, we should have 20 file handles. See Programming Guide, Vol. 1, Chapter 4, Section File Handles. */ /* This number may be too small for all these GPIB devices. We therefore may have to increase the maximum number of file handles in the GPIBDevice constructor. */ // The CLIQUEUE program is NOT included in this distribution /* The following classes are needed by CLIQUEUE.EXE */ /* They allow low-level access to the GPIB devices. */ GPIBDevice gpibdevice0 ("GPIB0"); GPIBDevice gpibdevice1 ("DEV1"); GPIBDevice gpibdevice2 ("DEV2"); TENNCounter tenncounter ("DEV3",0); /* create an instance of the class TENNCounter. */ GPIBDevice gpibdevice4 ("DEV4"); GPIBDevice gpibdevice5 ("DEV5"); GPIBDevice gpibdevice6 ("DEV6"); GPIBDevice gpibdevice7 ("DEV7"); SR850Lockin sr850lockin ("DEV8",0); /* Stanford Research SR850 DSP digital lock-in amplifier */ GPIBDevice gpibdevice9 ("DEV9"); GPIBDevice gpibdevice10 ("DEV10"); GPIBDevice gpibdevice11 ("DEV11"); HP_voltmeter hpvoltmeter ("DEV12",0); /* create an instance of the class voltmeter */ /* This class needs to be available for the detector */ GPIBDevice gpibdevice13 ("DEV13"); GPIBDevice gpibdevice14 ("DEV14"); GPIBDevice gpibdevice15 ("DEV15"); GPIBDevice gpibdevice16 ("DEV16"); /* This will create instances of the GPIBDevice class and open the devices *\ \* for shared access. */ GPIBDevice *gpibarray[17]={ &gpibdevice0,&gpibdevice1,&gpibdevice2, &tenncounter,&gpibdevice4,&gpibdevice5, &gpibdevice6,&gpibdevice7,&sr850lockin, &gpibdevice9,&gpibdevice10,&gpibdevice11, &hpvoltmeter,&gpibdevice13,&gpibdevice14, &gpibdevice15,&gpibdevice16}; /*---------------------------------------------------------------------------*/ GPIBDevice::GPIBDevice(char *dev_string) { // LONG ReqCount=10L; // if (ulFileHandles<=20UL) // apiret=DosSetRelMaxFH(&ReqCount,&ulFileHandles); /* Increase the number of file handles by 10. (Default is 20.) */ initialized=FALSE; strcpy(device_string,dev_string); /* initialize private variables */ Guflags=0U; Gboardadd=0U; Gpadd=0U; Gsadd=0U; /* initialize protected variables */ apiret=0UL; GIOTO=10; DevHandle=NULLHANDLE; ActionTaken=0UL; use_queue=TRUE; /* initialize public variables */ SRQ_enabled=TRUE; GEndMess=TRUE; /* send EOS after WRITE, set at device level */ GEOSDetect=FALSE; EOSbyte=0U; GPIBError=0UL; cbwritten=0UL; cbread=0UL; GStatusWord=0U; GSPOLLWord=0U; device_present=FALSE; strcpy(GIdentifier,"UNITIALIZED"); // apiret=GInit(device_string); return; } /*---------------------------------------------------------------------------*/ GPIBDevice::~GPIBDevice() /*destructor */ { if (!initialized) return; // There is nothing to do, since we never initialized (opened) the device IResourceLock gpibLock(gpibKey,1000); /* wait for GPIB to become free, but no more than one second */ /* go to local for easier front panel debugging */ GoToLocal(); if (strlen(device_string)!=0) apiret=DosClose(DevHandle); return; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GInit(char *dev_string) { IResourceLock gpibLock(gpibKey); /* open the GPIB device at the device level using DosOpen() */ apiret=DosOpen((PSZ) dev_string, &DevHandle, &ActionTaken, 0, FILE_READONLY, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, (PEAOP2) NULL); if (apiret==ERROR_OPEN_FAILED) strcpy(GIdentifier,"DosOpen FAILED"); else if (apiret==ERROR_TOO_MANY_OPEN_FILES) strcpy(GIdentifier,"Too many open files"); if (apiret!=0) return apiret; /* read the configuration parameters and store in member variables */ apiret=GDConfRd(); /* GDConfRd() calls GStatus(), so we don't have to call it here. */ return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GDConfRd() { nidev nid; ULONG ParmLength=sizeof(nid); // This function should not be called, unless the device has been initialized. apiret=DosDevIOCtl(DevHandle,CATEGORY,DConfRd, (PVOID)&nid,ParmLength,&ParmLength, (PULONG)NULL,0UL,(PULONG)NULL); /* The apiret return value should if 0 (if no error occured), or of the form *\ * 0000FFxx hex, where xx is the GPIB specific error. See the return code * * section for DosDevIOCtl in the OS/2 Control Program Reference. It says that * * 0000FF00 through 0000FFFF are user-dependent (in this case GPIB-specific) * * error codes. The GPIB-specific error codes are listed in Table 3.5 of the * \* National Instruments Function Reference Manual. */ if (apiret==0x0000FF04) { device_present=FALSE; strcpy(GIdentifier,"BOARD, NOT DEVICE"); } /* For the call DConfRd, the only error that can occur is EARG=4. This means */ /* that the board handle DevHandle is valid, but does not refer to a device. */ /* Instead, it refers to a board. This means that we made a device-level call*/ /* to a board. This is a benign error, but the information returned should */ /* not be used. */ if (apiret==0) { device_present=TRUE; Guflags=nid.uflags; Gboardadd=nid.brdno; Gpadd=nid.pad; Gsadd=nid.sad; EOSbyte=nid.eos; GIOTO=nid.timo; if (Guflags&UREOS) { GEOSDetect=TRUE; } else { GEOSDetect=FALSE; } /* endif */ if (Guflags&UEOT) { GEndMess=TRUE; } else { GEndMess=FALSE; } /* endif */ initialized=TRUE; } GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GDConfWrt() { nidev nid; ULONG ParmLength=sizeof(nid); if (!initialized) apiret=GInit(device_string); nid.uflags=Guflags; nid.brdno=Gboardadd; nid.pad=Gpadd; nid.sad=Gsadd; nid.eos=EOSbyte; nid.timo=GIOTO; apiret=DosDevIOCtl(DevHandle,CATEGORY,DConfWrt, (PVOID)&nid,ParmLength,&ParmLength, (PULONG)NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GsetEOS(short this_eos) { if (!initialized) apiret=GInit(device_string); EOSbyte=this_eos; if (this_eos==0U) { Guflags=Guflags & ~UREOS; /* Do NOT stop on EOS detected */ } else { Guflags=Guflags | UREOS; /* STOP on EOS detected */ } /* endif */ apiret=GDConfWrt(); if (apiret!=0) return apiret; apiret=GDConfRd(); return apiret; } /*---------------------------------------------------------------------------*/ USHORT GPIBDevice::GStatus() { ULONG ParmLength=sizeof(GStatusWord); APIRET this_apiret=0UL; if (!initialized) apiret=GInit(device_string); this_apiret=DosDevIOCtl(DevHandle,CATEGORY,STATUS, (PVOID)&GStatusWord,ParmLength,&ParmLength, (PULONG)NULL,0UL,(PULONG)NULL); /* CATEGORY and STATUS are defined in nicode.h. */ /* This call was taken from the example on page 3-88 of the NI manual */ /* and from the docs of the DosDevIOCtl API in the Control Program Reference */ /* manual in the OS/2 2.0 Technical Library. */ GPIBError=this_apiret&0x00FF; /* The apiret return value should be 0 (if no error occured), or of the form *\ * 0000FFxx hex, where xx is the GPIB specific error. See the return code * * section for DosDevIOCtl in the Control Program Reference. It says that * * 0000FF00 through 0000FFFF are user-dependent (in this case GPIB-specific) * * error codes. The GPIB-specific error codes are listed in Table 3.5 of the * \* National Instruments Function Reference Manual. */ return GStatusWord; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GWrite(char *temp) /* sends a command to the IEEE */ // This function will only work if you have a queue defined that catches // the GPIB commands (as strings). // This functionality has been removed for the Web distribution /* Most write commands are to be logged in the server queue. This function */ /* will therefore use the SendGPIBQueue function to put the GPIB command */ /* on the queue. */ { ULONG qmsg=0UL; ULONG qprio=0UL; /* pvdQMemory and hqQueue are defined in the main thread as external global */ /* variables. */ if (use_queue) { gpibinfo.COMMAND=IBWRITE; gpibinfo.Gpadd=Gpadd; gpibinfo.Gboardadd=Gboardadd; strcpy(gpibinfo.device_string,device_string); strcpy(gpibinfo.Write_Data,temp); // qmsg=Q_MSG_GPIB; // qprio=Q_GPIB_NORMAL_PRIO; // SendGPIBQueue(pvdQMemory, hqQueue, gpibinfo, qmsg, qprio); // here you can put your own commands to catch the commands in a queue // (if you wish). This is NOT required. } /* this is used just to keep a log of commands in the queue memory */ return GWriteNoQueue(temp); } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GWriteNoQueue(char *temp) /* sends a command to the IEEE */ /* This write command will NOT put the command on the queue. */ { int ilen=0; if (!initialized) apiret=GInit(device_string); IResourceLock gpibLock(gpibKey); cbwritten=0; ilen=strlen(temp); apiret=DosWrite(DevHandle, (PVOID) temp, ilen, &cbwritten); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GRead(char *buffer, ULONG ulSize) { if (!initialized) apiret=GInit(device_string); IResourceLock gpibLock(gpibKey); /* For most reads, we first wait for a service request to occur telling */ /* us that data is available. */ if (SRQ_enabled) { if (Gwait_service_request()) return (APIRET) 1; /* wait for a service request from the device (or a timeout). */ /* return with error, if there is no service request. */ GSPOLL(); /* After we know that the instrument (device) has issued a service request */ /* we do a serial poll. */ } apiret=DosRead(DevHandle,(PVOID) buffer, ulSize-1, &cbread); /* Then we do the actual read. */ buffer[cbread]='\0'; /* Add a zero byte to the end of the buffer (for strings as a terminator) */ GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GReadUShort(USHORT *sint) { if (!initialized) apiret=GInit(device_string); IResourceLock gpibLock(gpibKey); /* For most reads, we first wait for a service request to occur telling */ /* us that data is available. */ if (SRQ_enabled) { if (Gwait_service_request()) return (APIRET) 1; /* wait for a service request from the device (or a timeout). */ /* return with error, if there is no service request. */ GSPOLL(); /* After we know that the instrument (device) has issued a service request */ /* we do a serial poll. */ } apiret=DosRead(DevHandle,(PVOID) sint, sizeof(USHORT), &cbread); /* Then we do the actual read. */ GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GSelDevClear() /* Clears a Selected Device */ { if (!initialized) apiret=GInit(device_string); apiret=DosDevIOCtl(DevHandle,CATEGORY,DCLEAR,NULL,0UL,(PULONG)NULL, NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GSendIFC() /* assert interface clear */ /* board level call */ { if (!initialized) apiret=GInit(device_string); apiret=DosDevIOCtl(DevHandle,CATEGORY,SENDIFC,NULL,0UL,(PULONG)NULL, NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GONLINE() /* place device or interface online */ { if (!initialized) apiret=GInit(device_string); apiret=DosDevIOCtl(DevHandle,CATEGORY,ONLINE,NULL,0UL,(PULONG)NULL, NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GCommand(char cmd) /* board level command */ { USHORT cnt=1; ULONG ParmLength=sizeof(cnt); char CmdBuf[1]; ULONG DataLength=sizeof(CmdBuf); if (!initialized) apiret=GInit(device_string); CmdBuf[0]=cmd; apiret=DosDevIOCtl(DevHandle,CATEGORY,CMD,(PVOID)&cnt,ParmLength, &ParmLength,(PVOID)CmdBuf,DataLength,&DataLength); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GoToLocal() { if (!initialized) apiret=GInit(device_string); apiret=DosDevIOCtl(DevHandle,CATEGORY,DLOCAL,NULL,0UL,(PULONG)NULL, NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GBWAIT(USHORT usmask) /* This is a VERY important function. It never really worked until National */ /* Instruments released the drivers version 2.1.2. We use it to wait for a */ /* service request or other events on the bus. It also stops when a timeout */ /* or error occurs. */ { USHORT usmymask; ULONG ParmLength = sizeof(usmymask); usmymask=usmask; if (!initialized) apiret=GInit(device_string); apiret=DosDevIOCtl(DevHandle,CATEGORY,BWAIT,&usmymask,ParmLength, &ParmLength,NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GSPOLL() { ULONG ParmLength = sizeof(GSPOLLWord); if (!initialized) apiret=GInit(device_string); apiret=DosDevIOCtl(DevHandle,CATEGORY,SPOLL,&GSPOLLWord,ParmLength, &ParmLength,NULL,0UL,(PULONG)NULL); GStatus(); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GDevClear() /* Clears all Devices */ { if (!initialized) apiret=GInit(device_string); apiret=GCommand(DCL); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GUntalk() /* sets a device to untalk */ { if (!initialized) apiret=GInit(device_string); apiret=GCommand(UNT); return apiret; } /*---------------------------------------------------------------------------*/ APIRET GPIBDevice::GUnlisten() /* sets a device to unlisten */ { if (!initialized) apiret=GInit(device_string); apiret=GCommand(UNL); return apiret; } /*---------------------------------------------------------------------------*/ int GPIBDevice::Gwait_service_request() /* wait for SRQ */ { if (!initialized) apiret=GInit(device_string); GBWAIT(BERR | BTIMO | BRQS); /* wait until the BRQS bit has been set */ if (GStatusWord&BRQS) return 0; else return 1; } /*****************************************************************************/ /* These class functions are for an HP 3845A voltmeter. */ HP_voltmeter::HP_voltmeter(char *dev_string, int HP_initvar) : GPIBDevice(dev_string) { char *ptr=(char*) NULL; IResourceLock voltmeterLock(voltmeterKey); if (!initialized) apiret=GInit(device_string); init_var=HP_initvar; error_int=0; strcpy(ErrStr,"NOTHING YET"); have_error=FALSE; apiret=GSelDevClear(); SRQ_enabled=FALSE; strcpy(voltage_buffer," "); display_voltage=_NAN; scale_voltage=0.0; apiret=GWriteNoQueue("RESET"); /* Reset the voltmeter */ apiret=GWriteNoQueue("PRESET NORM"); /* configure for remote control */ /* apiret=GWriteNoQueue("MATH OFF"); */ /* included in PRESET NORM */ apiret=GWriteNoQueue("END ALWAYS"); /* Set EOI ALWAYS */ // if (elipso_object.eps_par.chopped_probe) { apiret=GWriteNoQueue("FUNC DCV,1"); /* Measurement mode */ // } else { // apiret=GWriteNoQueue("FUNC DCV,10"); /* Measurement mode */ // } /* endif */ apiret=GWriteNoQueue("FIXEDZ ON"); /* fixed impedance, avoid error on switching range. */ apiret=GWriteNoQueue("DELAY 0"); apiret=GWriteNoQueue("TARM HOLD"); apiret=GWriteNoQueue("TRIG AUTO"); apiret=GWriteNoQueue("AZERO OFF"); apiret=GWriteNoQueue("RQS 32"); /* Enable Service Request on error */ /* Setup to take one reading at a time, after TARM SGL command */ if (apiret=GWriteNoQueue("ID?")) return; if (apiret=GRead((char *) GIdentifier, (ULONG) sizeof(GIdentifier))) return; ptr=strchr(GIdentifier,'\r'); if (ptr!=NULL) *ptr='\0'; check_for_error(); return; } /*---------------------------------------------------------------------------*/ int HP_voltmeter::HP_reset(int HP_initvar) /* This function does the same as the constructor */ { char *ptr=(char*) NULL; IResourceLock voltmeterLock(voltmeterKey); if (!initialized) return 1; init_var=HP_initvar; error_int=0; strcpy(ErrStr,"NOTHING YET"); have_error=FALSE; apiret=GSelDevClear(); SRQ_enabled=FALSE; strcpy(voltage_buffer," "); display_voltage=_NAN; scale_voltage=0.0; apiret=GWriteNoQueue("RESET"); /* Reset the voltmeter */ apiret=GWriteNoQueue("PRESET NORM"); /* configure for remote control */ /* apiret=GWriteNoQueue("MATH OFF"); */ /* included in PRESET NORM */ apiret=GWriteNoQueue("END ALWAYS"); /* Set EOI ALWAYS */ apiret=GWriteNoQueue("FUNC DCV,10"); /* Measurement mode */ apiret=GWriteNoQueue("FIXEDZ ON"); /* fixed impedance, avoid error on switching range. */ apiret=GWriteNoQueue("DELAY 0"); apiret=GWriteNoQueue("TARM HOLD"); apiret=GWriteNoQueue("TRIG AUTO"); apiret=GWriteNoQueue("AZERO OFF"); apiret=GWriteNoQueue("RQS 32"); /* Enable Service Request on error */ /* Setup to take one reading at a time, after TARM SGL command */ if (apiret=GWriteNoQueue("ID?")) return apiret; if (apiret=GRead((char *) GIdentifier, (ULONG) sizeof(GIdentifier))) return apiret; ptr=strchr(GIdentifier,'\r'); if (ptr!=NULL) *ptr='\0'; check_for_error(); return 0; } /*---------------------------------------------------------------------------*/ int HP_voltmeter::check_for_error() { int ires=0; GSPOLL(); ires=GSPOLLWord & 32; if (ires != 0) { have_error=TRUE; error(); } else have_error=FALSE; return error_int; } /*---------------------------------------------------------------------------*/ #define run_bit 1 /* Remember that you need to have the queue initialized for this function. */ /* Call HP_init during the WM_CREATE message processing of the main window. */ /* See the documentation for the divider card by Kurt Jensen for a detailed */ /* explanation of the high-speed data acquisition mode. */ APIRET HP_voltmeter::HP_init(int HP_initvar) { if (have_error) return 1; if (!initialized) return apiret; if (HP_initvar!=0) init_var=HP_initvar; if (init_var==HP_acquire_slow) { apiret=GWrite("OFORMAT SINT;MFORMAT SINT;APER 0.1;"); /* APER: A/D converter integration time (in seconds) */ apiret=GWrite("MEM OFF;NRDGS 1,AUTO;DISP ON;EXTOUT ICOMP;"); ulReadings=1; } else if (init_var==HP_acquire_fast) { // pario_object.init(); /* initialize the indexer card for parallel I/O */ // Not included for Web distribution /* There is no problem if we initialize more than once, is there ? */ // pario_object.bit_enable(run_bit); /* enable run bit for starting the measurements (Kurt's circuit) */ // pario_object.bit_reset(run_bit); /* set the run bit to low */ apiret=GWrite("OFORMAT SINT;MFORMAT SINT;APER 1.4E-6;"); /* APER: A/D converter integration time (in seconds) */ apiret=GWrite("MEM FIFO;NRDGS 45000,EXT;DISP ON;EXTOUT BCOMP;"); ulReadings=45000; } else return 1; check_for_error(); /* go to local for easier front panel debugging */ apiret=GoToLocal(); return apiret; } /*---------------------------------------------------------------------------*/ HP_voltmeter::~HP_voltmeter() { IResourceLock voltmeterLock(voltmeterKey,1000); APIRET apiret=GSelDevClear(); return; } /*---------------------------------------------------------------------------*/ int HP_voltmeter::error() { GWriteNoQueue("ERRSTR?;"); /* See the HP manual. This only reads the least significant bit in the error */ /* register or the auxiliary error register. Repeated calls may be necessary */ /* until 0,"NO ERROR" is returned. */ GRead(ErrStr,sizeof(ErrStr)-1); error_int=atoi(ErrStr); return error_int; } /*---------------------------------------------------------------------------*/ APIRET HP_voltmeter::acquire_data(USHORT *buffer,ULONG ulSize) /* See the documentation for the divider card (by Kurt Jensen) for a detailed */ /* explanation of the high-speed data acquisition mode. */ { APIRET apiret1=0UL, apiret2=0UL; USHORT USdummy=0; IResourceLock voltmeterLock(voltmeterKey); if (have_error) return 1; if (init_var!=HP_acquire_fast) HP_init(HP_acquire_fast); memset(buffer,0,(size_t)ulSize); if (apiret1=GWriteNoQueue("DISP OFF;TARM SGL")) return apiret1; // pario_object.bit_set(run_bit); apiret2=DosRead(DevHandle,(PVOID) buffer, ulSize, &cbread); /* Then we do the actual read. */ /* If there was an error, try again. */ if (apiret2) { /* more than likely, there was a timeout in the DosRead API call. */ /* We reset the HP voltmeter to prevent it from being stuck. */ // HP_reset(0); // pario_object.bit_reset(run_bit); // if (init_var!=HP_acquire_fast) HP_init(HP_acquire_fast); // memset(buffer,0,(size_t)ulSize); // apiret1=GWriteNoQueue("DISP OFF;TARM SGL"); DosBeep(100,100); // pario_object.bit_set(run_bit); apiret2=DosRead(DevHandle,(PVOID) buffer, ulSize, &cbread); } /* endif */ /* If there was an error, try a third time. */ if (apiret2) { // HP_reset(0); // pario_object.bit_reset(run_bit); // if (init_var!=HP_acquire_fast) HP_init(HP_acquire_fast); // memset(buffer,0,(size_t)ulSize); // apiret1=GWriteNoQueue("DISP OFF;TARM SGL"); DosBeep(100,100); // pario_object.bit_set(run_bit); apiret2=DosRead(DevHandle,(PVOID) buffer, ulSize, &cbread); } /* endif */ /* If we still have an error, give up, but not without resetting the voltmeter */ if (apiret2) { HP_reset(0); DosBeep(1000,1000); // pario_object.bit_reset(run_bit); return apiret2; } /* If there was an error, return */ GStatus(); GWriteNoQueue("DISP ON"); // pario_object.bit_reset(run_bit); for (int i=0; sizeof(USHORT)*i<ulSize; i++) { USdummy=buffer[i]; USdummy=(USdummy<<8) + (USdummy>>8); buffer[i]=USdummy; } /* endfor */ /* swap bytes to convert from Motorola to Intel format */ apiret=GWriteNoQueue("ISCALE?"); if (apiret=GRead(voltage_buffer,sizeof(voltage_buffer)-1)) return apiret; scale_voltage=atof(voltage_buffer); check_for_error(); return 0; } /*---------------------------------------------------------------------------*/ double HP_voltmeter::GetVoltage() { static SHORT sint_voltage=0; static USHORT USdummy=0U; IResourceLock voltmeterLock(voltmeterKey); if (have_error) return 1; if (init_var!=HP_acquire_slow) HP_init(HP_acquire_slow); apiret=GWriteNoQueue("TARM SGL"); if (apiret=GReadUShort(&USdummy)) return _NAN; USdummy=(USdummy<<8) + (USdummy>>8); /* swap bytes to convert from Motorola to Intel format */ sint_voltage=USdummy; /* now convert back to short integer */ apiret=GWriteNoQueue("ISCALE?"); if (apiret=GRead(voltage_buffer,sizeof(voltage_buffer)-1)) return _NAN; scale_voltage=atof(voltage_buffer); display_voltage=scale_voltage*sint_voltage; check_for_error(); return display_voltage; } /*****************************************************************************/ TENNCounter::TENNCounter(char *dev_string, int TENN_dummy) : GPIBDevice(dev_string) { if (!initialized) apiret=GInit(device_string); IResourceLock counterLock(counterKey); display_counts=0; strcpy(counts_buffer,"NOTHING YET"); return; } /*---------------------------------------------------------------------------*/ int TENNCounter::GGetCounts() { char *ptr; IResourceLock counterLock(counterKey); GWriteNoQueue("PRES 0.1;CLEA;STAR;*WAI;COUN?"); // GWrite("PRES 0.1"); /* set count interval */ // GWrite("CLEA"); /* clear counters/timer */ // GWrite("STAR"); /* start counter */ // GWrite("*WAI"); /* wait for end of interval */ // GWrite("COUN?"); /* read counts */ display_counts=-1; if (GRead(counts_buffer,sizeof(counts_buffer)-1)) return -1; ptr=strchr(counts_buffer,',')+1; display_counts=atoi(ptr); return display_counts; } /*---------------------------------------------------------------------------*/ TENNCounter::~TENNCounter() { IResourceLock counterLock(counterKey,1000); return; } /*---------------------------------------------------------------------------*/ /* Remember that you need to have the queue initialized for this function. */ /* Call TENN_init during the WM_CREATE message processing of the main window.*/ #define EnEOI "1" #define EnMAV "16" #define EnESB "32" /* service request enable register constants for Tennelec counter, see manual */ APIRET TENNCounter::TENN_init(int TENN_dummy) { char *ptr=(char*) NULL; IResourceLock counterLock(counterKey); if (!initialized) return apiret; display_counts=0; strcpy(counts_buffer," "); SRQ_enabled=TRUE; apiret=GWrite("*RST"); /* reset command */ apiret=GWrite("*SRE "EnMAV); /* enable SRQs */ apiret=GWrite("*CLS"); /* clear status command */ apiret=GWrite("CLEA"); /* clear counters/timer */ apiret=GWrite("*IDN?"); /* send instrument ID */ apiret=GRead((char *) GIdentifier, (ULONG) sizeof(GIdentifier)); ptr=strchr(GIdentifier,','); if (ptr==NULL) return 1; ptr=ptr+sizeof(char); ptr=strchr(ptr,','); if (ptr!=NULL) *ptr='\0'; /* set registers MODE 0 and MODE 1 */ apiret=GWrite("MODE 0,0"); /* count up, and seconds timebase */ apiret=GWrite("MODE 1,1"); /* recycle off, Tmr+Ctrs */ return apiret; } /*****************************************************************************/ SR850Lockin::SR850Lockin(char *dev_string, int SR850_dummy) : GPIBDevice(dev_string) { if (!initialized) apiret=GInit(device_string); IResourceLock lockinLock(lockinKey); SRQ_enabled=FALSE; xlockin=_NAN; ylockin=_NAN; rlockin=_NAN; thetalockin=_NAN; xNoise=_NAN; yNoise=_NAN; rNoise=_NAN; return; } /*---------------------------------------------------------------------------*/ SR850Lockin::~SR850Lockin() { IResourceLock lockinLock(lockinKey,1000); return; } /*---------------------------------------------------------------------------*/ /* Remember that you need to have the queue initialized for this function. */ /* Call SR850init during the WM_CREATE message processing of the main window.*/ APIRET SR850Lockin::SR850Init(int SR850_dummy) { IResourceLock lockinLock(lockinKey); if (!initialized) return -1; if (apiret=GWrite("TRCD 1,1,0,0,1")) return apiret; // if (apiret=GWrite("TRCD 2,2,0,0,0")) return apiret; /* Traces 1 and 2 hold x and y. */ if (apiret=GWrite("TRCD 2,1,0,8,1")) return apiret; // Define trace 2 to be X divided by A1 (aux input 1) and store it if (apiret=GWrite("TRCD 3,5,0,0,0")) return apiret; if (apiret=GWrite("TRCD 4,6,0,0,0")) return apiret; /* Traces 3 and 4 hold xNoise and yNoise. */ return apiret; } /*---------------------------------------------------------------------------*/ APIRET SR850Lockin::trig(double *x, double *y) { IResourceLock lockinLock(lockinKey); *x=_NAN; *y=_NAN; if (apiret=GWrite("TRIG")) return apiret; /* Trigger a measurement, then read the data from the lockin */ if (apiret=GWrite("OUTP ? 1")) return apiret; xlockin=_NAN; if (GRead(read_buffer,sizeof(read_buffer)-1)) return -1; xlockin=atof(read_buffer); // if (apiret=GWrite("OUTP ? 2")) return apiret; if (apiret=GWrite("TRCD ? 2")) return apiret; ylockin=_NAN; if (GRead(read_buffer,sizeof(read_buffer)-1)) return -1; ylockin=atof(read_buffer); if (apiret=GWrite("OUTP ? 3")) return apiret; rlockin=_NAN; if (GRead(read_buffer,sizeof(read_buffer)-1)) return -1; rlockin=atof(read_buffer); if (apiret=GWrite("OUTP ? 4")) return apiret; thetalockin=_NAN; if (GRead(read_buffer,sizeof(read_buffer)-1)) return -1; thetalockin=atof(read_buffer); if (apiret=GWrite("TRCD ? 3")) return apiret; xNoise=_NAN; if(GRead(read_buffer,sizeof(read_buffer)-1)) return -1; xNoise=atof(read_buffer); if(apiret=GWrite("TRCD ? 4")) return apiret; yNoise=_NAN; if(GRead(read_buffer,sizeof(read_buffer)-1)) return -1; yNoise=atof(read_buffer); rNoise=sqrt(xNoise*xNoise+yNoise*yNoise); *x=xlockin; *y=ylockin; return apiret; } /*****************************************************************************/
Compiled (object) file
GPIB.DLL dynamic link library
GPIB.LIB include library
GPIB.DEF linker definition file - GPIB.DEF
LIBRARY GPIB INITINSTANCE TERMINSTANCE DESCRIPTION 'GPIB interface library (GPIB.DLL), Version 1.01, STZO 03/11/96' PROTMODE DATA MULTIPLE READWRITE LOADONCALL NONSHARED CODE LOADONCALL STACKSIZE 65000 EXPORTS ;From object file: .\gpib.obj ;PUBDEFs (Symbols available from object file): ;HP_voltmeter::HP_voltmeter(char*,int) __ct__12HP_voltmeterFPci gpibdevice9 ;GPIBDevice::GPIBDevice(char*) __ct__10GPIBDeviceFPc ;GPIBDevice::GInit(char*) GInit__10GPIBDeviceFPc ;GPIBDevice::GRead(char*,unsigned long) GRead__10GPIBDeviceFPcUl gpibKey gpibinfo ;GPIBDevice::GBWAIT(unsigned short) GBWAIT__10GPIBDeviceFUs ;SR850Lockin::SR850Lockin(char*,int) __ct__11SR850LockinFPci ;HP_voltmeter::check_for_error() check_for_error__12HP_voltmeterFv sr850lockin gpibarray ;GPIBDevice::GoToLocal() GoToLocal__10GPIBDeviceFv voltmeterKey lockinKey ;GPIBDevice::GDConfRd() GDConfRd__10GPIBDeviceFv ;GPIBDevice::Gwait_service_request() Gwait_service_request__10GPIBDeviceFv ;TENNCounter::TENN_init(int) TENN_init__11TENNCounterFi ;HP_voltmeter::HP_reset(int) HP_reset__12HP_voltmeterFi ;SR850Lockin::SR850Init(int) SR850Init__11SR850LockinFi ;GPIBDevice::GWriteNoQueue(char*) GWriteNoQueue__10GPIBDeviceFPc ;TENNCounter::TENNCounter(char*,int) __ct__11TENNCounterFPci ;GPIBDevice::GStatus() GStatus__10GPIBDeviceFv ;GPIBDevice::~GPIBDevice() __dt__10GPIBDeviceFv ;GPIBDevice::GUntalk() GUntalk__10GPIBDeviceFv ;HP_voltmeter::acquire_data(unsigned short*,unsigned long) acquire_data__12HP_voltmeterFPUsUl voltmeter_on ;GPIBDevice::GReadUShort(unsigned short*) GReadUShort__10GPIBDeviceFPUs ;GPIBDevice::GWrite(char*) GWrite__10GPIBDeviceFPc ;TENNCounter::~TENNCounter() __dt__11TENNCounterFv counter_on ;GPIBDevice::GSelDevClear() GSelDevClear__10GPIBDeviceFv ;GPIBDevice::GSendIFC() GSendIFC__10GPIBDeviceFv ;HP_voltmeter::HP_init(int) HP_init__12HP_voltmeterFi ;HP_voltmeter::GetVoltage() GetVoltage__12HP_voltmeterFv ;HP_voltmeter::~HP_voltmeter() __dt__12HP_voltmeterFv ulFileHandles ;GPIBDevice::GsetEOS(short) GsetEOS__10GPIBDeviceFs ;GPIBDevice::GDevClear() GDevClear__10GPIBDeviceFv ;GPIBDevice::GCommand(char) GCommand__10GPIBDeviceFc hpvoltmeter ;GPIBDevice::GDConfWrt() GDConfWrt__10GPIBDeviceFv ;GPIBDevice::GONLINE() GONLINE__10GPIBDeviceFv ;HP_voltmeter::error() error__12HP_voltmeterFv ;SR850Lockin::~SR850Lockin() __dt__11SR850LockinFv gpibdevice10 gpibdevice11 gpibdevice13 gpibdevice14 gpibdevice15 tenncounter gpibdevice16 counterKey ;SR850Lockin::trig(double*,double*) trig__11SR850LockinFPdT1 ;GPIBDevice::GUnlisten() GUnlisten__10GPIBDeviceFv ;GPIBDevice::GSPOLL() GSPOLL__10GPIBDeviceFv ;TENNCounter::GGetCounts() GGetCounts__11TENNCounterFv gpibdevice0 gpibdevice1 gpibdevice2 gpibdevice4 gpibdevice5 gpibdevice6 gpibdevice7
NICODE.H is only available from National Instruments (as part of the OS/2 driver for AT-GPIB/TNT)
NICODE.H from National Instruments
GPIB.ZIP. Contains everything (except NI drivers)
Programming tools you need
I use the following programming tools to write software for controlling my GPIB devices:
- VisualAge C++ for OS/2 (version 3.0) with CSDs.
- Include files for your GPIB driver (such as at at-gpib\c\nicode.h from National Instruments).
- Borland's resource workshop (part of Borland C/C++ compiler for OS/2, version 1.5) for creating nice dialog windows.
DISCLAIMER: This sample code is for your information only. Do not attempt to use this code on your equipment without first inspecting the source code and making sure it does what you want. This code is supplied as is without warranty or liability of any kind. Most of the brand names on this page are registered trademarks. Ames Laboratory or the author have no connection with National Instruments.
Note: This code is part of a larger program which I use for controlling my experiment. Some sections in the code have been removed to make the code stand-alone. Therefore, I am not sure, if this code will run without errors on your machine. I am not too concerned about this, since you are not supposed to run this code anyway. INSTEAD, THIS CODE IS INTENDED TO SERVE AS AN EXAMPLE, what type of things you can do using GPIB drivers on an OS/2 machine.