SoftTek – Eclipse-based control and visualization for measurement equipment

Note: this is work in progress. The software described is in development and not finished.

SoftTek is an Eclipse based application that is able to communicate via GPIB with some (vintage) measurement equipment of the Company Tektronix for control of the device and for visualization of measurement data.

Supported devices

The following devices are supported:

  • Tektronix 1241 Logic Analzer (tested)
  • Tektronix 1240 Logic Analyzer (not tested)

As far as I know, the 1240 and the 1241 are identical except that 1241 offers a color screen, 1240 is monochrome only. So in the following text, I will always use „1241“ because I own that one, but the text is also valid for the 1240.
To be more precise here, the EPROMs on the analyzer pcbs have mostly identical stickers for 1240 and 1241. The only difference known by me is that the character set of the 1240 and the 1241 are slightly different. The colors are coded into the byte value for a character, I do not know what a 1240 will display if it shall display a e.g. green ‚A‘. Maybe even this makes no difference, if e.g. the color coding is interpreted as „highlighting“ on the 1240. This would need further reading in the documentation…

Implemented features, missing features

SoftTek can…

  • Load memory images from Tek device (setup images, acquisition images, reference memory images, rampack data)
  • Display loaded data in a nice way (scaling, color coding, selection of groups to display). All 72 channels in all groups can be displayed in one big canvas (as apposed to the monitor view of the Tek 1241)
    • Non-glichted and glitched data
    • Chained data
  • Display up to two time cursors to analyze displayed data
  • Load and save device data to PCs harddisk

The implementation does not contain (why) …

  • Ability to download memory images to the device (GPIB module is not yet capable of doing this, needs update of the AVR firmware used in my GPIB module, this is not related to the software SoftTek)
  • No two timebase support and thus no multiplexing support (too complicated, needs more reading in the manuals)
  • Not tested:
    • synchronous mode,
    • chained 18 bit channels (never needed),
    • Chains with more than two cards (have only 2 9 channel and 2 18 channel cards, so my maximum is 2 card chains)

Requirements to run the software

  • Java JRE 1.6 or higher. Tested with 1.6.
  • Currently only Linux support, no Windows-plattform incarnation of the Eclipse RCP (I have no windows PC to test with)
  • Some GPIB controller attached to the device and attached to the PC where this software is running on. I used for my purposes my own Implementation of a GPIB-Controller running on an AVR board. This board is attached to the PC via RS232. A description of this device can be found here [3]

The Tek 1241

Some information on the device to enable understanding of the software. A more general intro to the 1241 is given in [2].

The analyzer supports up to 72 channels. It is able to view signals in state diagrams and in timing diagrams.

It supports frequencies up to 50 Mhz (synchronous) and 100Mhz asynchronous. The device has 2 time bases and offers 15 levels of trigger conditions. The two time bases allow e.g. a slow clock on the first time base to get some first trigger close to the area that shall be analyzed and then a high frequency clock for the second time base to get a much finer view into the area to be analyzed.

The organization of the input side of the device is as follows:

  • There are 4 lines for slots at the right side of the device. For each line,  two slots are possible. These slots are numbered 0,1 for first line, 2,3 for second line, 4,5 for third line and 6,7 for fouth line.
  • There are 9 channel and 18 channel acquisition memory cards. A 9 channel card uses a single slot (e.g. 0 or 2), an 18 channel card uses two slots (e.g. 4+5 or 6+7). These cards are located inside the device.
  • A Pod is a probe with 9 data input lines (and one clock/qualifier input line for e.g. synchronous mode). The Pod is external to the device and is attached via a cable and a connector into a acquisition memory card.
  • Between a Pod and the device under test, a lead set needs to be used. The lead set can be attached to the pod and offers e.g. micro clamps to attach the data input line to the device under test.Each Pod also has an „Id-Button“ that produces a screen read out of the pod id.

For my device:

Slot Card
0 9 channel
1
2 9 channel
3
4 18 channel (card 1)
5 18 channel (card 1)
6 18 Channel (card 2)
7 18 Channel (card 2)

My device has two 9 channel cards and two 18 channel cards. The two 9 channel cards are located in slots 0 and 2. The two 18 channel cards are located in slots 4+5 and 6+7.
I have in total 6 pods of type 6460 which allow together with 9 channel acquisition cards to control the logic threshold (signal level threshold for separating HI and LO on a line). 18 channel acquisition cards have always fixed TTL level threshold.

In the analyzer, a Test Pattern Generator TPG is built in. This generator creates well defined sequences of patterns. There are two 9 channel wide pattern sequences. To easily use the TPG, a test lead set was provided by Tektronix.

Display Groups

Display groups are used to do a grouping of input data lines for display. E.g. one group can contain data bus lines called e.g. „D0-D8“ and another group can contain some lines to monitoring an SPI communication. Grouping is done by the user and includes naming the group, assigning a timebase and other things.

The Analyzer has a default grouping at startup. This depends on the aquisition cards inserted. For my device:

GRPA
Pod 0, Bits 0..7
GRPB
Pod 2, Bits 0..7
CTL1
Pod 0, Bit 8
Pod 2, Bit 8
GRPC
Pod 4, Bits 0..7
Pod 5, Bits 0..7
GRPD
Pod 6, Bits 0..7
Pod 7, Bits 0..7
CTL2
Pod 4, Bit 8
Pod 5, Bit 8
Pod 6, Bit 8
Pod 7, Bit 8

Grouping is part of the setup. Setups can be stored in a ROMPACK if available or in a NVRAM area of the analyzer.

There is much more to say on the 1241, see e.g. the user manual.

Theory of Operation of SoftTek

The 1241 can communicate with a controlling PC via a GPIB controller (if equipped with a GPIB interface module) or via RS232 (if equipped with a RS232 module). My description is based on the GPIB module approach.

For short pieces of textual information, the information is exchanged between a Controller/PC and the 1241 in human readable strings.

Some of the data is large, of binary type and transfered in a data block-oriented format. This data include:

  • Setup information
  • Aquisition and Reference Memory content
  • RAM- and ROM-Pack content.

The GPIB-Implementation of Tektronix for the devices is documented in [1]. The implementation defines „Data Blocks“ and „Memory Images“.

A data block is a container with some header and trailer information surrounding a number of payload bytes. Seen from today, a data block is quite small (maximum payload 61 bytes). The payload bytes are encodings of binary data bytes. The 1241 is able to use three different data formats for the bytes:

  • ASCII HEX: Each Byte is coded in two bytes, representing the hex value of the original byte. E.g. byte 0xa2 will be encoded as two ASCII chars ‚A“2‘.
  • TEK Binary: Each byte stands for itself. E.g. byte 0xa2 will be transfered „as is“, namely as byte 0xa2.
  • IEEE728 Binary: Close to TEK Binary, but this is an IEEE standard for transfering binary data. The definition of IEE728 can be found somewhere in the Internet

Both binary encodings require a transport way that is able to transfer binary data, i.e. bytes with values >0x7f.

My implementation uses RS232 to connect the GPIB controller to the PC. The controller is homebrew, described in [3] and supports only XON/XOFF software handshake, so only ASCII (non-binary) data can be transfered via my implementation. So I am using the ASCII HEX type for the data blocks (which is the default for the Tek 124* device). My implementation also include some code for binary formats, but this hasn’t been tested. Because the data size per memory image is not very large (some KBytes) it is ok to use ASCII encoding despite it needs nearly the double amount of bytes compared to a binary encoding.

A memory image is a sequence of binary data bytes. If the user requests the transmission of e.g. the aquisition memory via GPIB, the Tek device creates a memory image from its internal hardware memory. This memory image is then converted to a sequence of data blocks and sent to the controller. The controller transfers the data to the PC and there some software converts the data blocks back to the binary payload. By putting together the small binary payloads from the data blocks, the large memory image can be reconstructed in the PC.

The structure of the memory images is documented by Tektronix in [1], all values inside the image are defined by their offset in the memory image, their size and, of course, their meaning.

Implementation

The application is an Eclipse Rich Client. It uses a navigation view (left side of application) to display/modify configuration of the client and of the data from the 1241. It also has a big canvas that is used to display the timeline data.

My implementation offers at the bottom classes for data blocks and for memory images. The data block classes implement a reader interface. This interface encapsulates the reading method (ASCII HEX, binary). The memory image class defines methods to use a list of datablocks or a large sequence of ascii bytes to build up the binary content of the image.

There are two implementations of the reader interface, to read data from the tek device, and to read data from files.

The communication to the Tek device uses a nice java implementation for serial connectivity, called RXTX from http://rxtx.qbang.org .

A class „Tek1241RawDevice“ encapsulates all useful methods for the device and uses the memory images coming from the Tek device. Another class „Tek1241Device“ uses a raw object (from class Tek1241RawDevice) to implement own logic above the raw calls.
Both of these classes extend a class „AbstractRawDevice“ resp. „AbstractDevice“. If the software will be extended to other devices, these abstract classes are the point to extend from.
To separate Raw classes from „Not-Raw“ (cooked) classes: raw classes know about offsets in memory images and do access memory images to extract data from.
Cooked classes are implemented on raw methods only and should not know about memory memory images.

The class „DisplayGroup“ encapsulated all information about the „Group“ objects from Tektronix that can be found in the setup memory image. The class „Channel“ encapsulates information about a channel (to which pod and which bit inside the pod is this channel assigned etc.).

To have some interaction with the displayed data, I added a class „TimeCursor“ which allows to create a set of cursors that can be moved on the data time lines to get the hex and binary values of the group data at a certain cursor position, like the screen cursor of the 1241.

The class NavigationView defines all UI elements at the left side and the menues.

The class View contains all methods to create, modify and display data in the big canvas area.

File format used

To store uploaded data on the disk of the PC, I „invented“ a file format capable to store the acquisition data together with its setup data. Note that the acquisition memory image does NOT contain all the data to rebuild the complete information needed to load later on the device data, so we need the setup data valid during acquisition too. Example are all information about groups, which are stored in the setup image and not in the acquisition image.
Because I had all the data available in the format that was sent by the device I decided to use that format. The data is in ASCII HEX format as described in the GPIB manual of Tektronix. Logically, the setup and the acquisitionn memory is each a (large) single line of ASCII characters. So my file format consists of fixed two lines:
Line 1: Setup Information as raw ASCII HEX characters ending with a CR
Line 2: Acquisition memory data as raw ASCII HEX characters ending with a CR

Because the data stored in the file is identical to the data read in by the device, it will be simple to download this data to the Tek 1241.

Screenshots

Below are some random screenshots showing the state of the work.


Early version (03/2012)

 


Early version; moved view to trigger event (red line at position 0)

 


Improved version with color coded timelines, extraction of more information from memory images (April 9th 2012)

Further improved version 04/2012

 


Closeup: the time cursors. They display in numerical value the bit position and the hexadecimal and dual value of the Group value (here it is a byte).

Look at the trigger position (red vertical line, t=0)

 


UI Elements for device access

 


Info on aquisition capability of the device

 


Some more info elements

 


Display of data with glitches

 


Closeup: display of data with glitches. A glitch (the red area) means that there was at least one undisplayed transition between the acquisitions before and after the glitch.

Examples for data files from a Tektronix 1241

Here are some examples of binary data coming from the 1241. All files are in ASCII HEX format:

Further information

[1] GPIB COMM Pack 1200C02, Tektronix, revised printing November 1986, downloadadble via Internet
[2] Der Logik-Analysator Tekronix 1241, http://www.spurtikus.de/basteln/tek1241.html

[3] Communication via GPIB with AVR, Description of my software-only implementation of a simple GPIB controller:  http://www.spurtikus.de/basteln/gpib_en.html

[4] RXTX library for serial communication with Java. http://rxtx.qbang.org

Kommunikation via GPIB mit dem AVR

This Article in english: 

Im folgenden sind meine Experimente zum Thema Nutzung des GPIB-Busses mit AVR Mikrocontrollern beschrieben.

Hinweis: Beschrieben ist -wie immer auf meinen Seiten- ein Versuchsaufbau im Experimentallabor. Es handelt sich keinesfalls um produktiv nutzbare Hard- oder Software. Der Versuchsaufbau kann allerhöchstens Basis für eigene Versuche dienen. Ich schreibe dies, weil GPIB-fähige Geräte typischerweise sehr teuer in der Anschaffung sind. Man kann sich beim Experimentieren durchaus den GPIB-Anschluss kaputt machen, daher diese ausdrückliche Warnung. Sagen Sie nicht ich hätte Sie nicht gewarnt 🙂

Der GPIB (General Purpose Interface Bus) ist ein von HP in den sechziger Jahren entwickeltes Bus-System. Es wurde vorrangig für die Vernetzung komplexer Messgeräte entwickelt, um z.B. automatisiertes Testen zu ermöglichen. Das Bus-System wurde durch HP und andere Hersteller weiter entwickelt und ausgebaut und unter dem Namen IEEE 488 standardisiert. Der Bus ist auch unter den Namen HPIB und IEC 625 bekannt.

Im Bereich hoch entwickelter Messgeräte ist der GPIB-Bus auch über 40 Jahre nach seiner Entwicklung noch weit verbreitet. Insbesondere Gebrauchtgeräte aus den 80ger und 90er Jahren haben oft ein GPIB-Interface.

Mein bei eBay erstandenes digitales Speicheroszilloskop Tektronix 2432A von 1989 besitzt ein GPIB-Interface. Das Oszilloskop lässt sich mittels GPIB komplett fernsteuern, es lassen sich alle Einstellungen abfragen und die momentan gespeicherten/dargestellten Wellenformen auslesen. Die Idee war daher, vom PC aus auf diese Daten zuzugreifen.

Tektronix 2432a Cursor

Es gibt für PCI, USB, ISA und RS232 Karten und Geräte, welche mit dem Rechner verbunden werden können und die eine GPIB-Schnittstellen besitzen. Diese Karten und Geräte sind sehr teuer, ich habe keine Lösung -auch nicht gebraucht- für unter 100 Euro gesehen. Ich habe daher versucht, mit einem AVR Mikrocontroller die Funktion eines GPIB-Geräts nachzubilden.

Der GPIB-Bus ist ein paralleler Bus. Geräte können mit speziellen Kabeln, die an den Enden sowohl eine Buchse als auch einen Stecker besitzen, in einer „Daisy Chain“ verbunden werden. Jedes Gerät am Bus hat typischerweise zwei Adressen (eine Listener und eine Talker-Adresse). An einem Bus können maximal 15 Geräte adressiert werden. Eines der Geräte fungiert als Controller in Charge. Der Controller steuert den Datenfluss zwischen allen anderen Geräten mittels Bus-Kommandos. Es kann auf einem Bus immer nur einen Talker gleichzeitig geben, es kann aber immer mehrere Listener geben. Typischerweise kann man bei einem GPIB-Gerät dessen Adresse und dessen Verhalten einstellen. Man kann z.B. ein Gerät als Listener, als Talker oder als Listener+Talker einstellen.

Trivialansatz: Talker/Listener Szenario

Falls man genau einen Talker und einen Listener hat, benötigt man keinen Controller. Der Talker gibt seine Daten auf den Bus aus, ohne einen speziellen Listener zu adressieren und der Listener liest die Daten vom Bus herunter. Dieser Fall tritt auf, wenn man an ein Oszilloskop einen Drucker oder Plotter anschließt. Diesen Fall habe ich zuerst aufgebaut.

Hardware

Ich habe einen ATMega32 genommen und diesen auf übliche Weise mittels RS232 mit meinem PC verbunden. Der AVR wird dann direkt mit dem GPIB-Bus verbunden. Hierbei sind einige Dinge zu beachten, die im weiteren erwähnt werden.

Die Leitungen des GPIB-Busses

Der GPIB-Bus wird auf eine 24-polige Centronics-Buchse geführt. 8 Leitungen (DIO1..8) transportieren das Datenbyte. Drei Leitungen (DAV, NRFD und NDAC) sind für den Handshake auf dem Bus verantwortlich. 5 Leitungen (ATN, SRQ, EOI, REN, IFC) sind für das Management des Busses verantwortlich. Somit benötigt man für eine Vollbelegung 16 Leitungen. Die restlichen Pins der Buchse sind Masse-Leitungen.

Tektronix 2432a GPIB Buchse

Die Leitungen des Busses sind „Open Collector“ beschaltet, und „Active Low“. D.h. eine logische „0“ bedeutet, dass die Leitung HIGH, also „1“ ist. Eine logische „1“ bedeutet, dass die Leitung LOW, also „0“ ist. Die Open Collector-Beschaltung sorgt dafür dass mehrere Listener gleichzeitig an einer Leitung hängen können und unabhängig voneinander die Leitung auf LOW schalten können, ohne das dabei eine Ausgangsstufe eines der Geräte kaputt geht.
Solange nur eine Konfiguration Talker->Listener vorhanden ist und kein weiterer Listener im Spiel, braucht man auf das Thema Open Collector nicht zu achten.

Ich habe die 8 Datenleitungen auf ein Port (Port A) des AVR geführt. Die Management-Leitungen habe ich erstmal ignoriert und die drei Handshake-Leitungen auf weitere freie Pins des AVR geführt. Somit habe ich 11 Leitungen verwendet.

Software

Das spezielle Drei-Draht-Handshake des GPIB-Busses ist an verschiedenen Stellen im Internet beschrieben (z.B. hier). Der Handshake läuft wie folgt ab:

  1. Listener signalisieren mit NRFD (Not Ready For Data), wenn sie Daten empfangen können (sie setzen NRFD auf HIGH). Wenn der letzte Listener HIGH setzt, geht NRFD auf HIGH.
  2. Talker bemerkt dies und setzt Daten auf die Datenleitungen und zieht DAV (Data Valid) auf LOW.
  3. Listener bemerken dies und setzen für die Zeit des Lesens der Daten NRFD auf LOW. Wenn ein Listener mit dem Lesen fertig ist, setzt er NDAC auf HIGH. Wenn der letzte Listener fertig ist, geht dadurch NDAC auf HIGH. Dies bemerkt der Talker und setzt DAV auf HIGH. Daten sind nicht mehr gültig.
  4. Nach einer gewissen Zeit setzen die Listener NDAC wieder auf LOW, und NRFD auf HIGH so dass der Zyklus für das nächste Byte beginnen kann.

Dieses Protokoll habe ich zunächst in einer Schleife mittels Polling implementiert. Es wurden zwar Daten gelesen, aber dasselbe Datum gleich mehrfach, ich habe den Übergang DAV nach HIGH im Schritt 3 einfach nicht mitbekommen, egal was ich probiert habe. Es ging soweit, dass ich glaubte, dass in der TALKER/Listener-Konfiguration DAV immer LOW bleibt. Nachmessen mit dem Oszilloskop hat aber gezeigt, dass DAV sehr wohl zwischen HIGH und LOW wechselt, und zwar mit etwa 3300Hz. Das Oszilloskop sendete im Versuch den Bildschirminhalt, etwa 3000 Bytes. Möglicherweise ist Polling hier zu langsam (siehe Bemerkung weiter unten, es war ein Programmierfehler, Polling ist nicht zu langsam).

Ich habe die Software daher verändert, und zwar so, dass auf der fallenden und steigenden Flanke des DAV-Signals (das an INT0 anliegt) ein Interrupt ausgelöst wurde. Diese Interrupts habe ich zunächst einfach gezählt. Es waren etwa 6000 Interrupts, was gut zu den etwa 3000 Zeichen passte. Ich habe dann die Implementierung ganz auf Interrupts umgestellt, so dass die Zeichen in der Interrupt-Service-Routine gelesen werden. Danach wurden alle Zeichen sauber übertragen.

Im folgenden ist die zentrale Interrupt-Routine dargestellt. Da die Daten in negativer Logik ankommen, werden sie mit 0xff per XOR verknüpft, um das echte Zeichen zu erhalten. Die „dav“-Variable verwende ich außerhalb der ISR, um festzustellen ob ein neues Zeichen eingetroffen ist. Dort wird sie auch zurückgesetzt:

ISR(INT0_vect) {
   if (irq_falling_edge==1) {
         
      // handshake: clear NRFD, means i am busy now to read data
      PORTD &= ~_BV(G_NRFD);         
      // read data
      byte = PINA ^ 0xff;
      // handshake: set ndac, means i have completed/accepted the read
      PORTD |= _BV(G_NDAC); 
      // set data valid flag
      dav=1;
   
      irq_falling_edge=0; // wait for next raise
      MCUCR = (1<<ISC00)|(1<<ISC01); // raise int0 on rising edge
   } else {
      // handshake: clear ndac (this is a prerequisite for receive next byte)
      PORTD &= ~_BV(G_NDAC);
   
      irq_falling_edge=1; // wait for next fall
      MCUCR = (1<<ISC00); // raise int0 on falling edge
   }
}

Ein weiteres Problem ist, dass ich das Ende der Übertragung zunächst nicht erkannte. Der GPIB-Bus nutzt hierfür die EOI-Leitung, die der Talker beim Übertragen des letzten Bytes auf LOW zieht. Diese Leitung hatte ich aber nicht verwendet. Das Oszilloskop kann jedoch zusätzlich am Ende einer Übertragung ein CR schicken. Dies habe ich am Oszilloskop eingestellt, danach konnte ich auch das Ende der Übertragung feststellen. Für Binärübertragung muss man allerdings den Weg über die EOI-Leitung gehen.

Im folgenden ist die so gewonnene Ausgabe eine WAVEFORM dargestellt. Es handelt sich um ein Rechtecksignal. Die momentane Einstellung der Y-Achse von Kanal 1 und der Zeitachse sowie weitere Einstellungen werden als Präambel (WFRMPRE) vorangeschickt. Danach folgt ab „CURVE“ die Waveform-Beschreibung. Werte um 50 sind das HIGH, Werte um 0 sind das LOW des Signals. Die Ausgabe wird als eine lange Zeile geschickt, ganz am Ende kommt ein CR-Zeichen. Der Übersichtlichkeit halber habe ich die Ausgabe in mehrere Zeilen aufgeteilt.

WFMPRE WFID:"CH1 DC  20mV  10ms NORMAL",NR.PT:1024,PT.OFF:512,PT.FMT:Y,XUNIT:SEC,XINCR:2.000E-4,YMULT:
8.000E-4,YOFF:0,YUNIT:V,BN.FMT:RI,ENCDG:ASCII;
CURVE 0,0,-1,0,0,1,-1,0,0,0,1,1,49,50,50,50,49,49,50,51,49,50,50,50,50,51,50,50,50,50,50,50,
50,51,50,50,50,52,50,52,50,49,51,51,50,50,50,49,50,50,51,52,50,52,49,50,50,51,50,50,51,50,0,
0,1,1,-1,0,0,0,-1,0,-1,-1,-1,0,0,-2,0,0,0,0,0,-1,-1,1,0,0,-1,0,1,0,1,0,1,-1,1,1,-1,0,1,0,0,1,1,0,1,
0,-1,1,-2,0,50,51,50,50,49,51,52,50,51,51,50,50,51,50,52,49,51,52,51,50,51,50,52,50,51,52,50,
50,49,51,50,51,50,50,51,50,52,49,51,51,51,51,49,50,52,51,50,50,51,51,2,0,1,-2,0,-1,1,-1,0,1,0,
0,0,-1,-1,1,0,-1,1,0,0,-1,0,-2,1,0,0,-2,0,0,-1,-1,0,1,-1,-1,-1,0,0,-1,-2,-1,-1,1,-1,1,0,1,-2,-1,49,49,
48,51,50,50,50,50,49,52,51,51,52,50,50,51,49,49,51,50,51,50,50,49,50,51,51,51,51,51,51,50,50,
50,52,51,51,50,50,50,51,50,51,52,52,51,51,52,50,50,1,-1,0,0,1,0,-1,-1,1,-1,1,-1,-1,0,0,1,0,0,-1,
-1,-2,-1,1,0,0,-1,1,-1,1,-2,-1,-1,-1,-1,0,1,1,1,-1,1,-1,1,-1,-2,0,-1,-1,-2,1,0,50,50,50,50,50,50,50,
51,51,50,51,50,50,50,49,51,51,50,50,51,51,50,51,50,52,52,50,51,50,50,51,50,50,51,50,50,51,50,
51,51,51,50,50,51,49,49,51,49,49,50,0,0,0,-1,0,-1,1,0,0,-1,0,0,-1,0,0,1,-1,0,-2,0,0,0,-1,-1,-1,-1,
0,0,0,-1,-1,0,-2,0,-1,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-2,-1,0,49,50,49,50,49,51,50,50,51,50,51,50,50,49,
50,52,52,49,48,51,51,50,50,51,51,51,50,50,51,52,50,50,51,51,50,51,51,50,49,51,50,50,52,50,52,
50,49,50,50,50,0,0,0,0,0,0,-1,0,0,0,0,0,0,-1,-1,-1,0,1,1,-1,-1,0,0,0,-1,-2,0,0,1,0,-1,1,1,0,0,1,-1,-1,
0,-1,0,-1,2,0,0,0,-3,0,1,0,50,50,51,49,51,52,50,49,50,51,50,50,50,51,51,50,51,51,51,52,49,50,49,
50,50,49,51,53,49,51,50,48,51,51,51,50,49,51,51,49,51,50,52,51,50,50,50,51,52,50,-1,-1,0,0,-2,-1,
-1,-1,0,-2,-1,-1,-1,0,0,-2,0,-1,0,-1,-1,-1,0,-1,-1,0,0,-2,0,0,-1,1,0,-3,0,0,-2,0,-1,-1,0,0,-1,0,0,0,-2,-1,
0,-1,50,50,51,49,51,49,50,51,50,50,50,50,49,51,51,50,51,51,50,51,50,49,50,51,50,50,49,50,52,50,51,
50,49,50,51,51,50,51,50,52,50,51,52,50,50,50,50,51,51,51,-1,0,0,-1,0,0,0,0,-1,0,0,1,-2,0,0,-1,0,0,0,0,
-1,0,-1,-1,-1,0,0,0,1,-1,0,0,-1,0,-1,0,-1,-1,1,1,0,0,-1,0,-1,-1,-1,-1,1,0,50,51,50,51,51,50,50,51,50,51,49,
52,50,50,50,51,50,51,49,51,52,50,51,50,51,50,51,52,48,49,49,50,48,49,50,51,50,50,50,50,50,50,51,49,
50,51,48,50,51,52,0,0,0,0,0,0,-2,0,0,0,0,-1,-1,0,-1,0,0,-1,0,1,0,-1,-1,-2,0,0,0,0,0,-1,1,0,0,-1,0,-1,-1,1,
0,0,0,-1,0,-1,0,0,0,-1,-1,-2,49,50,50,52,50,49,50,50,49,50,52,50,49,50,51,52,49,49,49,51,51,50,50,50,50,
49,51,50,50,50,51,52,50,50,50,50,50,50,49,52,51,50,50,50,51,52,50,50,52,49,0,-1,0,-1,0,1,2,-1,-1,-1,0,1,
0,-1,0,0,1,0,0,-1,0,1,0,0,-2,0,-1,-1,0,-1,-1,-1,0,-1,0,-1,0,-1,0,0,1,-1,-2,-1,-2,1,-1,-1,1,1,50,48,49,49,49,
50,50,51,50,51,50,50,52,50,51,52,51,51,52,50,52,49,50,49,51,49,52,50,50,50,48,51,50,50,49,51,51,50,51,
50,50,48,50,52,51,49,50,50,49,51,0,-2,-1,-1,1,0,0,0,0,1,-1,-2,0,1,0,-2,-1,0,0,0,-1,-1,0,0,0,0,-2,1,0,-1,-1,-1,
-1,0,-2,-1,-1,0,-1,-1,-1,-1,0,-1,0,-2,0,0,1,-2,49,50,51,51,49,50,52,51,50,51,52,51

Im Ergebnis kann man mit dieser Konstellation Daten, die das Oszilloskop schickt, sauber zum PC übertragen. Man kann aber leider keine Kommandos zum Oszilloskop schicken. Dies würde die Erweiterung des Beispielcodes um Controller-Funktionen und die Verwendung zusätzlicher Leitungen erfordern. Außerdem muss man dann das Thema „Open Collector“ lösen, sonst geht entweder der ATMega32 oder aber das GPIB-Interface des Oszilloskops kaputt.

Ein Versuch mit HPGL-Ausgabe, die das Oszilloskop auch beherrscht, zeigt ein neues Problem: Statt in einer langen Zeile kommen die HPGL-Daten zeilenweise. Meine Erkennung des Übertragungsendes funktioniert damit nicht mehr.

Hier beispielhaft die HPGL-Ausgabe (nicht komplett):

in;sc-690,689,-512,511;
sp1;
pa-500,-400;pd;pr1000,0,0,800,-1000,0,0,-800;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl1;xt;pr100,0;
tl0,1;yt;pr0,100;
tl0,1;yt;pr0,100;
tl0,1;yt;pr0,100;
tl0,1;yt;pr0,100;
tl0,1;yt;pr0,100;
tl0,1;yt;pr0,100;

Ich habe eine HPGL-Ausgabe mit dem Tool „hp2xx“ in ein JPEG überführt. Die Ausgabe kann man in kermit mittels „log session <datei>“ in eine Datei umleiten. Das hpxx-Kommando konvertiert mit folgender Abfolge:

rm -f tmp.eps out.pdf
./hp2xx -p 22222222 -C -o 20 -O 150 -h 100 -a 2  -m eps -d 600 $1 -f tmp.eps
ps2pdf tmp.eps out.pdf
acroread out.pdf

Die Kurve und die Skala ist ok, die Beschriftung kommt viel zu klein heraus. Mit der Option „-d xxx“ kann man die DPI-Auflösung des Ergebnisbildes beeinflussen, ab 300 dpi kann man den Text lesen.

Versuche mit dem Tool hpgl2ps waren schlechter als die mit hp2xx.

hp2xx Output

Elaborierterer Ansatz: Talker/Listener Szenario

Als nächstes habe ich zusätzlich die Leitungen EOI, ATN, SRQ, IFC und REN an den AVR geführt. Die Beschaltung sieht dann tabellarisch wie folgt aus:

GPIB Pin Name  GPIB-Buchse Pin Nummer  ATMega32 Pin Name  AVR Board Buchse Pin Nummer
DIO1..8 (Datenpins) 1..4 und 13..16 PortA0..PortA7  1..8
EOI 5 PD4 30
DAV 6 PD2 28
NRFD 7 PD3 29
NDAC 8 PD5 31
SRQ 10 PD6 32
ATN 11 PD7 33
IFC 9 PB0 9
REN 17 PB1 10
GND 23 GND 39

Nutzung des EOI-Signals

Eine Untersuchung des EOI-Signals mit einem zweiten Oszilloskop ergab, dass das Signal ohne Übertragung LOW ist, während der kompletten Übertragung auf HIGH gehalten wird und nach dem letzten Zeichen wieder auf LOW geht. Dies unterscheidet sich von der GPIB-Dokumentation, die ich im Internet gefunden habe. Dort wird beschrieben, dass das EOI-Signal nur genau für das letzte Zeichen einer Übertragung  LOW gezogen wird. Entweder funktioniert mein Oszilloskop anders als der Standard oder ich lese die Beschreibungen nicht richtig.

Beschaltung der Pins des AVRs für Open Collector

Die AVR Controller sind zwar extrem flexibel, man kann aber die Port-Pins nicht direkt in eine Open Collector Modus schalten. Man könnte nun Open-Collector-fähige Bustreiber zwischenschalten, aber dafür bin ich zu faul. Eine Suche im Internet ergibt, dass man scheinbar die Open-Collector-Schaltung simulieren kann, indem man auf den Pin ein LOW schreibt und dann mittels des DDRx-Registers den Pin umschaltet. Die Idee bedeutet im einzelnen(am Beispiel PORTD und Pin PD4):

Funktion „normale“ Beschaltung Open Collector Simulation
Initialisierung des Pins DDRD |= _BV(PD4) // als Ausgang
DDRD &= ~BV(PD4) // als Eingang
PORTD &= ~_BV(PD4)
DDRD &= ~_BV(PD4)
Pin auf HIGH setzen PORTD |= _BV(PD4) DDRD &= ~_BV(PD4)
Pin auf LOW setzen PORTD &= ~_BV(PD4) PORTD &= ~_BV(PD4)
DDRD |= _BV(PD4)
Pin lesen unsigned char p = PIND & _BV(PD4) DDRD &= ~_BV(PD4)
unsigned char p = PIND & _BV(PD4)

Nach Umstellung funktioniert mein Code noch, die Simulation scheint also zu funktionieren. Als nächstes steht die Implementierung der Talker und Controller-Funktionalität an.

Nach einigem Hin- und Her bin ich völlig von der Interrupt-gesteuerten Leseroutine abgekommen. Die Probleme, die ich beim Pollen hatte, waren auf einen Fehler meinerseits (Lesen von PORTx statt von PINx, was nicht funktioniert) zurückzuführen. Nach der Implementierung der Lesefunktion gpib_read() und der Schreibfunktion gpib_write() stand die Implementierung der Controllerfunktionalität an.


Das Experimentierboard von Pollin. An die 40-polige Buchse (IDE-Buchse) ist ein altes IDE-Kabel angeschlossen, an den der GPIB-Stecker angelötet wurde.

GPIB Controller Implementierung

Der Controller übernimmt am Anfang den Bus  und steuert alle Geräte am Bus, d.h. er definiert sie als Listener, Talker oder „Taub“, also nicht am Bus partizipierend. Dazu nutzt er Kommandoleitungen (wie IFC und REN) und Bus-Kommandos (wie DCL, UNL, UNT). Ein Kommandobyte auf dem Bus unterscheidet sich von einem Datenbyte dadurch, dass der Controller die ATN-Leitung während des Sendens von Kommandobytes auf logisch 0 zieht.

Geräte werden durch das Ansprechen über ihre Listener-Adresse (=Geräteadresse + 0x20) zu Listenern und durch das Ansprechen über ihre Talker-Adresse (=Geräteadresse +0x40) zu Talkern. Es darf immer nur einen Talker geben, aber mehrere Listener. Der Controller hat üblicherweise, aber nicht zwingend, die Geräteadresse 0x00. Kommandos setzt der Controller mit der Funktion gpib_cmd() ab.

Meine simple Implementierung des Controllers sieht wie folgt aus:

  1. Übernahme des Busses durch REN und IFC (Funktion gpib_init() und gpib_controller_assign()), dann Senden von DCL (Funktion gpib_cmd()). Alle Geräte gehen dadurch in einen definierten Anfangszustand.
  2. Ausführen einer Endlosschleife:
    1. Kommandos werden von STDIN gelesen. Ein CR zeigt das Kommandoende an. Der Controller schaltet sich als Talker und und ein weiteres Gerät als Listener (z.B. mein Oszilloskop mit Geräte-Adresse 0x01). Das Kommando wird auf den Bus gelegt (gpib_cmd() + gpib_write()). Das Kommandoende wird über die Leitung EOI angezeigt (ein reale Controller würde sicher komplexer vorgehen…).
    2. Die Listener lesen das Kommando und führen es aus. Bei Kommandos ohne Antwort geht der Controller wieder in die Schleife, um neue Kommandos zu lesen. Bei Kommandos mit Antwort schaltet der Controller sich als Listener und ein weiteres Gerät (mein Oszilloskop) als Talker. Die Antwort wird vom Gerät gelesen (gpib_read()). Das Gerät zeigt das Ende der Antwort über die Leitung EOI an. Der Controller gibt jedes gelesene Zeichen auf STDOUT aus. Nach Ausgabe der Antwort geht der Controller wieder in die Schleife, um neue Kommandos zu lesen.
      Der Controller unterscheidet Kommandos mit und ohne Antwort anhand des Zeichens „?“ am Ende des Kommandos (stimmt nicht immer…).
  3. Asynchron Verarbeitung neben der Endlosschleife in Punkt 2.: Falls ein Gerät von sich aus, also ohne Einladung durch den Controller, am Bus aktiv werden will, erzeugt es einen Service Request SRQ (Ziehen der SRQ-Leitung auf logisch 0). Der Controller bekommt dies mit, ermittelt mittels eines Serial Polling genannten Vorgehens den Erzeuger des SRQs und liest dessen Status ein. Der Status wird auf STDOUT ausgegeben (Funktion gpib_serial_poll()). Serial Polling bedeutet, dass der Controller mit dem Kommando SPE (Serial Polling Enabled) das Polling einleitet. Alle Geräte werden dann hintereinander als Talker definiert, der Controller definiert sich als Listener und liest Daten vom Talker ein. Beim seriellen Polling gibt jeder Talker nur genau 1 Byte, sein Status Byte aus. Das SRQ-erzeugende Gerät hat das Bit 6 im Statusbyte gesetzt. Damit weiß der Controller, welches Gerät den Request erzeugt hat. Der Controller beendet mit SPD (Serial Polling Disable) das Polling, alle Geräte verhalten sich von da ab wieder „normal“. Das Behandeln des SRQs ist gerätespezifisch. Mein Oszilloskop teilt mit dreistelligen Errorcodes, wie z.B. „157“ Fehler bei der Kommandoerkennung („vertippt“) und ähnliches mit.
  4. Theoretisch gibt der Controller am Ende den Bus wieder frei (Funktion gpib_controller_release()).

Beim Handshake kann man den Controller mittels busy waiting auf das Ändern einer Handshake-Leitung warten lassen. Falls die Änderung nie kommt, weil z.B. das Gerät abgeschaltet wurde oder das Gerät den Handshake nicht richtig mitmacht, wartet der Controller dann ewig. Ich habe daher diese busy-waiting-Schleifen daher durch Schleifen ersetzt, die nach einem Timeout (5s) abbrechen. Damit bleibt der Controller auch in diesen Fällen benutzbar.

Im folgenden ist eine Session dargestellt. Wenn das Oszilloskop eingeschaltet wird und bereit ist, generiert es einen SRQ. Der Controller fragt bei der SRQ-Behandlung mittels „EVENT?“ den SRQ-Grund ab, es kommt die Antwort „401“. 401 bedeutet laut Oszi-Handbuch „2432A was just powered on.“. Danach folgt die User Eingabe „id?“ auf die das Gerät mit der Ausgabe seiner Typenbezeichnung antwortet. Danach werden ein paar Kommandos abgesetzt, die keine Antwort liefern:

INIT
MEASUREMENT WINDOW:ON
CURSOR FUNCTION: TIME,TARGET:CH1,UNITS:BASE
CURSOR TPOS:ONE:200,TPOS:TWO:500

Zum Schluss wird mittels „CH1?“ die aktuelle Konfiguration des Kanals 1 abgefragt.

dennis@soc:~> kermit -c
Connecting to /dev/ttyS2, speed 19200
 Escape character: Ctrl- (ASCII 28, FS): enabled
Type the escape character followed by C to get back,
or followed by ? to see other options.
----------------------------------------------------
 
SRQ detected.
status byte from device 0x41 = 0x41 (char=A)
SRQ emitter is device = 0x41
 
command: EVENT?
Query. Will check for answer.
EVENT 401
> id?
command: id?
Query. Will check for answer.
ID TEK/2432A,V81.1,"24-DEC-89  V2.30 /2.5"
> INIT
command: INIT
Command only.
> MEASUREMENT WINDOW:ON
command: MEASUREMENT WINDOW:ON
Command only.
> CURSOR FUNCTION: TIME,TARGET:CH1,UNITS:BASE
command: CURSOR FUNCTION: TIME,TARGET:CH1,UNITS:BASE
Command only.
> CURSOR TPOS:ONE:200,TPOS:TWO:500
command: CURSOR TPOS:ONE:200,TPOS:TWO:500
Command only.
> CH1?
command: CH1?
Query. Will check for answer.
CH1 VOLTS:1E-1,VARIABLE:0,POSITION:0,COUPLING:DC,FIFTY:OFF,INVERT:OFF
>

Fazit: Insgesamt funktioniert somit die Implementierung eines Controllers mit dem AVR komplett in Software recht gut. Für einen echten Praxis-Einsatz müsste man die Software noch stark erweitern und vor allen auch mit anderen Geräten testen.

Hier die Software unter GNU General Public Licence als ZIP-Datei. Die ZIP-Datei enthält auch die Dateien uart.h/uart.c von Peter Fleury für die RS232 Kommunikation, die ebenfalls unter der GNU GPL steht.

ZIP-File mit allen Dateien:  gpib-0.66.zip

Versionshistorie:
0.1: Kommunikation mit einem Gerät (Tek 2432 getestet) ok.
0.63: Kommunikation mit 2 Geräten (und vermutlich auch mit mehr als 2) möglich. Fehler in Lesefunktion entfernt.
0.66: doxygen Dokumentation hinzugefügt

Eine Aufzeichnung einer kompletten Session mit 2 Geräten ist hier zu finden: Session mit 2 Geräten mit Code Version 0.63. Die verwendeten Geräte waren ein Oszilloskop Tektronix 2432A und ein Logik Analysator Tektronix 1241. Bei den Geräten wird die ID? abgefragt, beim Oszilloskop die momentane Konfigurationen der beiden Kanäle CH1? und CH2?, die Konfiguration für die Darstellung der aktuellen angezeigten Kurve WFMPRE? und dann die Kurve selbst (CURVE?). Das Datenformat wird dabei von Binary nach ASCII verändert. Danach wird der Logic Analysator angesprochen und mit REFMEM? der momentane Inhalt des Referenzspeichers ausgelesen.

Aufbau des Geräts

Im folgenden ein paar Bilder zum aufgebauten Gerät.

 

Weiterführende Links