GPIB like it’s Nineteen-Ninety-Something

The latest but probably greatest addition to the MINC support is the IBV11 and peripheral hardware that allows you to hook up to a GPIB/HPIB/IEEE-488 bus. In case you missed it, I’ve added new page in the MINC hierarchy with all the details.

There’s something like three major eras in the evolution of GPIB-enabled instruments – the first being the R2D2-period, when you could only use the bus to set the instruments to the intended function with very brief codes that usually corresponded directly to menu choices or lists in the manual – hence, R2D2, because that would conceivably be a valid command.

The next era would be the ID-period; starting when GPIB became common enough, and regular lab setups had so many instruments people had a need for a simple way to establish which instrument they were actually communicating to. Many instruments had the ‘ID?’ variant, although there is also an earlier ‘ID’ version. Instruments became more talkative – they could send out more than just the basic measurement, but also tell you their status and current setup.

Then the final period when the SCPI standard emerged, with it’s hierarchical command structure of normalized keywords separated by colons – and ID? became *IDN?, and maybe something like SYSTEM:IDENTITY too if the manufacturer felt creative.

Anyway, on to playing with it. The target of today is my trusty old PM6666 counter, and it is definitely in the middle era. Let’s ask it what kind of thing it is:

10 I%=18
20 SEND("id?",I%)
30 RECEIVE(A$,,I%)
40 PRINT "ID? ";A$

where in line 10, I’m setting the bus address of the instrument (18 is the default for PM6666, and I left mine at that). Then in line 20, the MINC sends the ID? command towards address 18, and in line 30 it reads whatever the instrument has to send – which should be the response to the query.

ID? PM6666/016/22

And it is! The PM6666, telling us that it is indeed a PM6666, and with the 016 suffix that tells us that it has the GPIB option!

One of the popular functions in the second era of GPIB instruments was the concept of what is called ‘bus-learn’ in the PM6666 documents – setting up the instrument on the front panel until you get the measurement results you’re looking for, then from the bus query the instrument setup so that you can restore that in all possible detail later. Typically, first era instruments can’t do that, but second era instruments can – and PM6666 is clearly in that latter category. Here goes:

10 I%=18
20 SEND("inpa;att off;auto off;coupl dc;trglvl 0",I%)
30 SEND("inpb;att off;auto off;coupl dc;trglvl 0",I%)
40 GOSUB 60
50 GO TO 320
60 SEND("id?",I%)
70 RECEIVE(A$,,I%)
80 PRINT "ID? ";A$
90 SEND("fnc?",I%)
100 PRINT "FNC? ";
110 GOSUB 290
120 SEND("meac?",I%)
130 PRINT "MEAC? ";
140 GOSUB 270
150 SEND("inpa?",I%)
160 PRINT "INPA? ";
170 GOSUB 250
180 SEND("inpb?",I%)
190 PRINT "INPB? ";
200 GOSUB 250
210 SEND("bus?",I%)
220 PRINT "BUS? ";
230 GOSUB 270
240 RETURN
250 RECEIVE(A$,,I%)
260 PRINT A$;";";
270 RECEIVE(A$,,I%)
280 PRINT A$;";";
290 RECEIVE(A$,,I%)
300 PRINT A$
310 RETURN
320 END

Ok, the lines 20 and 30 set the counter to a sort-of known state, and some of the rest is involved in the trickery of the PM6666 – some queries have a one line response, some two, some even three. But the output gives you the complete setup of the instrument – if you send that back, it’ll be in exactly the same state as you left it. Like so:

ID? PM6666/016/22
FNC? FREQ A
MEAC? MTIME 0.20,FRUN ON;TOUT 00.0
INPA? TRGSLP POS,ATT OFF;COUPL DC,AUTO OFF;TRGLVL +0.00,SENS 1
INPB? TRGSLP POS,ATT OFF;COUPL DC,COM OFF;TRGLVL +0.00,SENS 1
BUS? MSR 000,OUTM 000;EOI OFF,SPR 010

Ok enough meta talk, let’s measure something!

So I’ve connected a function generator to the PM6666’s A input, with a 1kHz sine wave and 250mV p-p amplitude. So we’ll want DC coupling, a trigger level of 0V, no other trickery. And let’s do one measurement every ten seconds:

10 I%=18
20 INSTR_TIME_LIMIT(300)
30 SEND("inpa;att off;auto off;coupl dc;trglvl 0",I%)
40 SEND("freq a;",I%)
50 RECEIVE(A$,,I%)
60 PRINT CLK$;" ";A$
70 PAUSE(10)
80 GO TO 40

As before, line 10 is just setting the instrument bus address. Line 20 tells the MINC how long it should wait for a reply on the RECEIVE command – in clock ticks, so 300 is 5 seconds for a 60Hz system clock.

Then lines 30 and 40 set up what we want the instrument to measure – all default really, it would probably just work if we skipped those. Then the main part: lines 50 through 80. Read a measurement, print it, sleep for 10 seconds, repeat. Simple.

23:33:13 FREQ   0009.99999E+2
23:33:23 FREQ 0009.99997E+2
23:33:34 FREQ 001.000001E+3
23:33:44 FREQ 0009.99998E+2
23:33:55 FREQ 001.000000E+3

One thing is not perfect though – we wanted one measurement every ten seconds, and what we got is a measurement, then 10 seconds wait – and as you can see from the time stamps, that’s not exactly the same. Now of course it’d be easy to guesstimate how long a measurement takes and subtract that from the value on the PAUSE statement, but where’s the fun in that?

And MINC can do better – in fact, way better. Let’s start with using serial poll to have the instrument tell us when the next measurement result is available:

10 I%=18
20 SRQ_SUBROUTINE(90)
30 SEND("inpa;att off;auto off;coupl dc;trglvl 0",I%)
40 SEND("frun off",I%)
50 SEND("msr 1",I%)
60 TRIGGER_INSTR(I%)
70 PAUSE(10)
80 GO TO 60
90 REM this is the serial poll subroutine
100 SERIAL_POLL(S1%,S2%,I%)
110 RECEIVE(A$,,I%)
120 PRINT CLK$;" ";A$
130 RETURN

Line 20 sets up a special MINC trick to deal with service requests – whenever the SRQ signal is raised on the bus, MINC will interrupt normal processing and go to a subroutine at line 90. Also, line 40 is new – it sets up the PM6666 to wait until we tell it to start counting. And line 60 is how we do just that; the GPIB bus has a method to trigger instruments, including a trick to trigger all instruments on the bus at the same time. And line 50 tells the PM6666 that it should raise a serial poll request whenever it has a new result ready to be read. The serial poll causes the lines 90 through 130 to be executed, and those will print the measurement result.

In this example we’re still using the PAUSE(10) command to get one measurement every ten seconds – but compared to the previous example, the actual reading and printing of the result is taken out of that interval. Will it make a difference? Let’s see:

23:42:19 FREQ   0009.99996E+2
23:42:30 FREQ 0009.99999E+2
23:42:40 FREQ 0009.99996E+2
23:42:50 FREQ 001.000005E+3
23:43:00 FREQ 000.999994E+3

Yes it does – in fact, I was lucky to get a jump in the seconds so soon in the output, usually that takes a couple of minutes.

Can MINC do better though, can it do a sample exactly every minute, like exactly on the clock at second :00 every time?

I’m so glad you asked. Ok, it takes some doing – what MINC can do is run a subroutine asynchronously to the main line code at a set clock time, so what we have to do is to read the clock, then calculate the time of the next minute including the next hour, schedule an event for that, then sit back and hope for the best. It works, unless you run the start too close to the turn of the minute – MINC doesn’t schedule events if the time has already past…

10 I%=18
20 SRQ_SUBROUTINE(90)
30 SEND("inpa;att off;auto off;coupl dc;trglvl 0",I%)
40 SEND("frun off",I%)
50 SEND("msr 1",I%)
60 GOSUB 180
70 PAUSE(.1)
80 GO TO 70
90 REM this is the serial poll subroutine
100 SERIAL_POLL(S1%,S2%,I%)
110 RECEIVE(A$,,I%)
120 PRINT CLK$;" ";A$
130 RETURN
140 REM this is the schedule subroutine
150 TRIGGER_INSTR(I%)
160 GOSUB 180
170 RETURN
180 REM schedule for the next minute second 0
190 C9$=CLK$
200 H9=VAL(SEG$(C9$,1,2))
210 M9=VAL(SEG$(C9$,4,5))
220 M9=M9+1
230 IF M9<60 THEN GO TO 260
240 H9=H9+1
250 M9=0
260 IF H9=24 THEN H9=0
270 C9$=STR$(H9)+":"+STR$(M9)+":0"
280 SCHEDULE('absolute',C9$,140)
290 RETURN

Lines 10-50 should be familiar by now, as well as 90-130. 70 and 80 are a wait loop – in which your program could conceivably do something computational to the data that has been collected, but for now it’s just a wait loop. Then there’s the scheduled routine at 140 – all it does is send a trigger, and a call to line 180 to schedule a next event for the next minute crossing.

The complexity is in lines 180-280. How to get 70s-style BASIC to do some trivial string manipulation? If someone has a better idea to do this, in such a way that regular humans can still understand what is going on, please let me know. Oh, the variable style of x9 was my idea at the time to show that they’re local variables – except they’re not really to the MINC BASIC.

So does this work? See for yourself:

00:10:00 FREQ   0009.99998E+2
00:11:00 FREQ 0009.99999E+2
00:12:00 FREQ 0009.99994E+2
00:13:00 FREQ 001.000008E+3
00:14:00 FREQ 000.999996E+3

and this is one of those times it really pays of to set the MINC clock to somewhere near the actual time – so that the time on your desktop tells you when the next line from the MINC is due. Or else, maybe schedule the measurement every ten seconds, like we started off with? Like so:

10 I%=18
20 SRQ_SUBROUTINE(90)
30 SEND("inpa;att off;auto off;coupl dc;trglvl 0",I%)
40 SEND("frun off",I%)
50 SEND("msr 1",I%)
60 GOSUB 180
70 PAUSE(.1)
80 GO TO 70
90 REM this is the serial poll subroutine
100 SERIAL_POLL(S1%,S2%,I%)
110 RECEIVE(A$,,I%)
120 PRINT CLK$;" ";A$
130 RETURN
140 REM this is the schedule subroutine
150 TRIGGER_INSTR(I%)
160 GOSUB 180
170 RETURN
180 REM schedule for the next 10 seconds at second 0
190 C9$=CLK$
200 H9=VAL(SEG$(C9$,1,2))
210 M9=VAL(SEG$(C9$,4,5))
220 S9=VAL(SEG$(C9$,7,7))
230 S9=S9+1
240 IF S9<6 THEN GO TO 300
250 S9=0
260 M9=M9+1
270 IF M9<60 THEN GO TO 300
280 H9=H9+1
290 M9=0
300 IF H9=24 THEN H9=0
310 C9$=STR$(H9)+":"+STR$(M9)+":"+STR$(S9)+"0"
320 SCHEDULE('absolute',C9$,140)
330 RETURN

Only some changes in the string manipulation in lines 180-310 – note the ‘clever’ trick I did to schedule every ten seconds, by just reading the first one of the seconds digits. Programming like it’s, ehh, 1979. But hey, it works!

00:20:10 FREQ   001.000003E+3
00:20:20 FREQ 000.999996E+3
00:20:30 FREQ 001.000001E+3
00:20:40 FREQ 001.000003E+3
00:20:50 FREQ 000.999993E+3
00:21:00 FREQ 000.999996E+3
00:21:10 FREQ 000.999998E+3
00:21:20 FREQ 000.999999E+3

Leave a Reply