background image

LABORATORIUM  SYSTEMÓW  POMIAROWYCH 

KTP   

 

 

   IR   

 

 

   PW 

 
 
 
 
 
 
 
 
 
 
 
 
 
 

PROGRAMOWANIE SYSTEMU POMIAROWEGO 

W STANDARDZIE IEEE-488.2 (IEC-625.2) 

 

(materia

ły pomocnicze do ćwiczenia nr 3) 

 
 
 
 
 
 
 

 

Opracowali:  

 

dr in

ż. W.Winiecki 

 

mgr Piotr Kluk 

 
 
 
 
 
 
 
 
 
 
 
 

Warszawa 1996 

background image

I. CEL ĆWICZENIA. 
 
 

Celem  ćwiczenia  jest  praktyczne  zapoznanie  się  z  nowoczesnymi  urządzeniami 

pomiarowymi  i  oprogramowaniem  służącym  do  budowy  komputerowych  systemów 
pomiarowych  w  standardzie  IEC-625.2.  Wykonanie  ćwiczenia  polega  na  zaprogramowaniu 
prostego zadania pomiarowego w systemie składającym się z kontrolera (komputer IBM-PC z 
zainstalowanym  pakietem  interfejsu  NI-488.2  firmy  National  Instruments),  zwanego  dalej 
skrótowo  kontrolerem  NI-488.2,  oraz  generatora  funkcyjnego  i  multimetru  firmy  Hewlett-
Packard. Zastosowany sprzęt i oprogramowanie umożliwiają zapoznanie się z językiem SCPI, 
który jest obecnie standardem w dziedzinie programowania urządzeń pomiarowych. 
 
II. WSTĘP. 
 
 

W  ćwiczeniu  wykorzystuje  się  pakiet  interfejsu  NI-488.2  firmy  National  Instruments, 

który  jest  zgodny  ze  standardem  IEEE-488.2  (IEC-625.2).  Jest  to  unowocześniony  standard 
IEEE-488  (IEC-625,  HP-IB,  GP-IB).  Pakiet  NI-488.2  składa  się  z  karty  interfejsu  i 
oprogramowania,  umożliwiających  pracę  komputera  IBM-PC  jako  kontrolera  systemu.  W 
skład  oprogramowania  pakietu  wchodzą  między  innymi  biblioteki  instrukcji  kontrolera  dla 
różnych  języków  programowania  takich,  jak  BASIC,  PASCAL,  C.  Umożliwia  to 
użytkownikowi  pisanie  programów  aplikacyjnych w  języku, który  mu  najbardziej  odpowiada. 
Do celów ćwiczenia wybrano język C ze względu na jego strukturalność i zwięzłość zapisu. W 
przypadku  tego  języka  biblioteka  instrukcji  składa  się  z  pliku  nagłówkowego  decl.h  oraz 
modułu  mcib.obj,  które  należy  dołączyć  do  programu  aplikacyjnego.  W  plikach tych  oprócz 
definicji  instrukcji  kontrolera,  znajdują się także definicje stałych oraz zmiennych, które mogą 
być wykorzystane przez użytkownika w jego programie aplikacyjnym. Zdefiniowane instrukcje 
są łatwe w zastosowaniu i  zapewniają pełną kontrolę nad kartą interfejsu oraz dołączonymi do 
niej  przyrządami  pomiarowymi.  Stosując  odpowiednią  sekwencję  wywołań  tych  instrukcji  z 
danymi parametrami, można zaprogramować dowolne zadanie pomiarowe. 
 

Standard  IEEE-488.2  jest  bazą  dla  języka  SCPI  (Standard  Commands  for 

Programmable  Instruments),  który  jest  obecnie  standardem  w  dziedzinie  programowania 
przyrządów pomiarowych. Język ten służy do porozumiewania się z przyrządem pomiarowym 
i  nie  należy  go  mylić  z  językiem  służącym  do  tworzenia  aplikacji,  czyli  w  tym  przypadku  z 
językiem  C.  Z  punktu  widzenia  języka  do  tworzenia  aplikacji,  polecenia  języka  SCPI  są 
tekstami wysyłanymi do przyrządów pomiarowych. 
 
 
 
 
 
 

background image

III.  WYBRANE  INSTRUKCJE  KONTROLERA  NI-488.2  DOSTĘPNE  Z  POZIOMU 

JĘZYKA C. 

 
 

W  tabeli  1  przedstawiono  wybrane  instrukcje  kontrolera  NI-488.2,  zwane  dalej 

skrótowo  instrukcjami  (z  punktu  widzenia  języka  C  są  to  funkcje,  a  z  punktu  widzenia 
użytkownika  systemu  pomiarowego  są  to  instrukcje  kontrolera).  Wymienione  instrukcje 
stanowią  tylko  część  zbioru  instrukcji  zdefiniowanych  w  module  mcib.obj.  Wybrany  zestaw 
instrukcji  wystarcza  jednak  do  zaprogramowania  praktycznie  dowolnego  zadania 
pomiarowego. 
 
Tab. 1. Wybrane instrukcje kontrolera NI-488.2. 

Składnia wywołania 

Opis działania 

SendIFC (board) 

Zerowanie interfejsu za pomocą IFC. 

EnableRemote (board, addresslist) 

Wprowadzenie  grupy  urządzeń  w  stan 
pracy zdalnej. 

FindLstn (board, addresslist, 
                 resultlist, limit)
 

Znalezienie 

wszystkich 

odbiorników 

dołączonych do magistrali.  

DevClear (board, address) 

Zerowanie pojedynczego urządzenia. 

DevClearList (board, addresslist) 

Zerowanie grupy urządzeń. 

Send (board, address, data, 
          count, eotmode)
 

Wysłanie bajtów danych do pojedynczego 
urządzenia. 

Receive (board, address, data, 
                count, termination)
 

Czytanie 

bajtów 

danych 

bufora 

wyjściowego urządzenia. 

Trigger (board, address) 

Wyzwolenie  pojedynczego  urządzenia 
(wysłanie komunikatu GET). 

TriggerList (board, addresslist) 

Wyzwolenie grupy urządzeń. 

TestSRQ (board, result) 

Określenie bieżącego stanu linii SRQ. 

WaitSRQ (board, result) 

Czekanie  aż  urządzenie  zgłosi  żądanie 
obsługi, czyli ustawi aktywną linię SRQ. 

ReadStatusByte 

(board, 

address, 

result) 

Odpytanie 

szeregowe 

pojedynczego 

urządzenia  w  celu  otrzymania  jego  bajtu 
statusu. 

AllSpoll (board, addresslist, resultlist) 

Odpytywanie szeregowe grupy urządzeń. 

FindRQS (board, addresslist, result) 

Określenie, 

które 

urządzenie 

żąda 

obsługi. 

 
 

Poniżej wyjaśniono znaczenie najczęściej występujących parametrów instrukcji: 

 
board  -  numer  kontrolera,  czyli  numer  karty  interfejsu  umieszczonej  w  komputerze;  w 
laboratorium  w  każdym  komputerze  znajduje  się  co  najwyżej  jedna  karta  interfejsu  IEEE-
488.2  o  domyślnym  numerze  (adresie)  0,  zatem  w  miejsce  zmiennej  board  w  wywołaniu 
instrukcji należy umieścić 0. 
 
address  -  adres  (0..30)  urządzenia  dołączonego  do  magistrali.  Adres  ten  jest  parametrem 
instrukcji, które dotyczą jednego urządzenia. Kontroler posiada adres 0. 
 

background image

addresslist  -  tablica  zawierająca  listę  adresów,  przy  czym  ostatnim  elementem  listy  musi być 
wartość  NOADDR  (stała  zdefiniowana  w  pliku  nagłówkowym  decl.h).  Lista  adresów  jest 
parametrem  instrukcji,  które  dotyczą  grupy  urządzeń.  Przykład  zdefiniowania  listy  adresów 
zawierającej dwa adresy (8 i 9): 

 

 

 

unsigned int addresslist[3] = {8, 9, NOADDR}; 

 
Kilka instrukcji wymaga bardziej szczegółowego opisu: 
 
1. FindLstn (board, addresslist, resultlist, limit) 
 
-  instrukcja  służy  do  znajdowania  urządzeń  dołączonych  do  magistrali  i  mających  zdolność 
odbierania  (większość  urządzeń  posiada  taką  zdolność).  Parametr  addresslist  powinien 
zawierać  listę  adresów,  które  mają  być  przeszukane.  Wynikiem  działania  instrukcji  jest  lista 
adresów  resultlist  znalezionych  urządzeń.  Lista  adresów  uzyskana  w  ten  sposób  może 
następnie  być  wykorzystana  jako  parametr  addresslist  innej  instrukcji  (np.  DevClearList()). 
Parametr  limit  określa  maksymalną  liczbę  urządzeń  do  znalezienia,  tzn. po  znalezieniu liczby 
urządzeń  równej  wartości  parametru  limit  instrukcja  zakończy  działanie.  Instrukcja  ta 
wprowadza  wszystkie  znalezione  urządzenia  w  stan  pracy  zdalnej,  czyli  nie  ma  potrzeby 
stosowania  instrukcji  EnableRemote.  Wykryte  urządzenia  są  najczęściej  identyfikowane  w 
celu określenia typu urządzenia.  
 
2. Send (board, address, data, count, eotmode) 
 
- instrukcja służy do wysyłania bajtów danych do jednego urządzenia o adresie address.  
Parametr  data  jest  to  tekst  programujący  wysyłany  do  urządzenia.  Parametr  count  służy  do 
sterowania liczbą przesyłanych bajtów, tzn. nie może być wysłane więcej bajtów niż określono 
to za pomocą tego parametru. Najczęściej wartość parametru count jest równa liczbie bajtów 
umieszczonych  w  tekście  programującym.  Parametr  eotmode  określa  sposób  sygnalizowania 
odbiorcy  końca  przesyłanych  danych.  Zwykle  jest  tu  używana  stała  NLend,  oznaczająca 
wysłanie  za  ostatnim  bajtem  danych  znaku  końca  linii  NL  z  aktywnym  sygnałem  EOI. 
Przykład: 

 

 

 

Send (0, 8, "*IDN?", 5L, NLend); 

 
Efektem  tego  będzie  wysłanie  do  urządzenia  o  adresie  8  tekstu  programującego  *IDN? 
liczącego 5 bajtów i zakończonego znakiem końca linii NL z aktywnym sygnałem EOI. Należy 
zwrócić  uwagę  na  format  parametru  count  -  liczbę  bajtów  do  przesłania  należy  zakończyć 
znakiem L (konwersji do typu long w języku C). 
 
3. Receive (board, address, data, count, termination)    
 
-  instrukcja  służy  do  odbierania  bajtów  danych  od  urządzenia  o  adresie  address.  Parametr 
data  jest  tablicą  tekstową,  do  której  wpisywane  są  odbierane  bajty.  Parametr  count  określa 
maksymalną  liczbę  bajtów  do  odebrania.  Parametr  termination  służy  do  określenia  bajtu, 
którego  odebranie  sygnalizuje  koniec transmisji. Najczęściej jest tu używana stała STOPend
oznaczająca zakończenie czytania po odebraniu komunikatu END. Przyk³ad: 

 

 

 

unsigned char data[100]; 

 

 

Receive (0, 8, data, 100L, STOPend); 

background image

4. TestSRQ (board, result) 
 
- instrukcja służy do testowania stanu linii SRQ. Przyk³ad: 

 

 

 

short result; 

 

 

TestSRQ (0, &result); 

 

 

if (result ==1) {  /* SRQ jest aktywna */ } 

 

 

else { /* SRQ jest nieaktywna */ } 

 
5. WaitSRQ (board, result) 
 
- instrukcja  służy  do  oczekiwania  na  stan aktywny linii SRQ,  czyli  oczekiwania na zgłoszenie 
przerwania. Przyk³ad: 

 

 

 

unsigned short addresslist[4] = {8, 9, 10, NOADDR}; 

 

 

unsigned short resultlist[3]; 

 

 

short result; 

 

 

WaitSRQ (0, &result); 

 

 

if (result == 1) 

 

 

    AllSpoll (0, addresslist, resultlist); 

 
6. ReadStatusByte (board, address, result) 
 
- instrukcja służy do odpytania szeregowego pojedynczego urządzenia w celu otrzymania jego 
bajtu statusu. Przyk³ad: 

 

 

 

unsigned short result; 

 

 

ReadStatusByte (0, 8, &result); 

 
Odebrany bajt statusu jest zwracany w zmiennej result
 
7. AllSpoll (board, addresslist, resultlist) 
 
-  instrukcja  służy  do  odpytywania  szeregowego  wszystkich urządzeń umieszczonych  na  liście 
addresslist w celu otrzymania od nich bajtów statusu. Przyk³ad: 

 

 

 

unsigned short addresslist[3] = {8, 9, NOADDR}; 

 

 

unsigned short resultlist[2]; 

 

 

AllSpoll (0, addresslist, resultlist); 

 
Odebrane bajty statusu są zwracane w zmiennej resultlist
 
8. FindRQS (board, addresslist, result) 
 
-  instrukcja  służy  do  znalezienia  jednego  z  urządzeń  żądających  obsługi.  Urządzenia  są 
odpytywane  szeregowo  w  kolejności  zgodnej  z  listą  addresslist.  Odpytywanie  trwa  do 
momentu  znalezienia  pierwszego urządzenia, które zgłasza żądanie obsługi. Bajt statusu tego 
urządzenia  jest  zwracany  w  zmiennej  result.  Dodatkowo,  w  zmiennej  globalnej  ibcnt
zwracany jest  indeks w tablicy addresslist dla znalezionego urządzenia. Można w ten sposób 
otrzymać adres znalezionego urządzenia. W przypadku gdy żadne z urządzeń z umieszczonych 

background image

na  liście  addresslist  nie  żąda  obsługi,  zwracany  jest  kod  błędu  ETAB  w  zmiennej  globalnej 
iberr,  natomiast  zmienna  ibcnt  zawiera  indeks  pozycji  NOADDR,  czyli  ostatniej  pozycji  na 
liście addresslist. Przyk³ad: 

 

 

 

unsigned  short addresslist[3] = {8, 9, 10, NOADDR}; 

 

 

unsigned short result; 

 

 

FindRQS (0, addresslist, &result); 

 
 

Wspomniane zmienne globalne ibcntiberr zdefiniowano w module mcib.obj. Ponadto 

istotna  jest  zmienna  globalna ibsta.  Jest  to  bajt  statusu  określający stan interfejsu  (nie należy 
go mylić z bajtem statusu, który można odebrać od urządzenia). Jeden z bitów zmiennej ibsta 
przeznaczony  jest  do  sygnalizowania  wystąpienia  błędu.  Jeżeli  bit  ten  jest  ustawiony,  to 
znaczy,  że  wystąpił  jakiś  błąd.  Jaki  jest  to  błąd  określa  wartość  zmiennej  iberr.  Znaczenie 
poszczególnych bitów  wyjaśniono w przykładowym programie aplikacyjnym w punkcie VII. 
 

Program aplikacyjny powinno się zakończyć wywołaniem instrukcji ibonl(). Instrukcja 

ta  wywołana  z  dwoma  zerowymi  parametrami  ibonl(0,  0),  wprowadza  kartę  i 
oprogramowanie  interfejsu  w  stan  spoczynku  (offline);  między  innymi  linia  REN  staje  się 
nieaktywna. 
 
UWAGA
:  Język  C  rozróżnia  małe  i  duże  litery.  Należy  mieć  to  na  uwadze  używając  nazw 
instrukcji  (np.  SendIFC)  i  nazw  zmiennych  (np.  ibsta,  iberr)  zdefiniowanych  w  module 
mcib.obj,  czy  też  nazw  stałych  (np.  NOADDR,  NLend,  STOPend)  zdefiniowanych  w  pliku 
nagłówkowym decl.h. 
 
 
IV. WPROWADZENIE DO JĘZYKA SCPI. 
 
 

Język  SCPI  (Standard  Commands  for  Programmable  Instruments)  zaprojektowano  do 

celów sterowania przyrządami pomiarowymi za pośrednictwem interfejsu w standardzie IEEE-
488.2.  Standard  ten  jest  bazą  dla  języka  SCPI.  Oznacza  to,  że  urządzenia,  które  mają 
zaimplementowany język SCPI są zgodne ze standardem IEEE-488.2.  
 
IV.1 SKŁADNIA JĘZYKA SCPI. 
 
 

Język  SCPI  używa  hierarchicznej  struktury  podobnej  do  tej  używanej  przez  system 

plików  w  systemie  operacyjnym  DOS  lub  UNIX.  Przykładowe  drzewo  rozkazów  (poleceń) 
przedstawiono na rysunku 1. 
 

Podsystem "A"

Podsystem "B"

Podsystem"C"

:H

:E

:D

:F

:G

:I

:J

:K

:M

:L = :C:L

:N = :B:H:N

 

 

Rys.1. Hierarchiczna struktura systemu rozkazów języka SCPI. 

background image

 

Korzeń  drzewa  rozkazów  zawiera  tzw.  rozkazy  poziomu  korzenia  (root-level 

commands)  zwane  też  podsystemami  (np.  A,  B  C  na  rys.1).  Poniżej  każdego  podsystemu 
znajdują  się  kolejne  poziomy,  czyli  tzw.  rozkazy  niższego  poziomu  (lower-level  commands). 
W  celu  wykonania  danego  polecenia  niższego  poziomu  konieczne  jest  podanie  kompletnej 
ścieżki dostępu do niego; np. w celu wykonania polecenia N należy wydać polecenie :B:H:N. 
Pierwszy dwukropek oznacza poziom korzenia. Można go pominąć jeżeli jest to pierwszy znak 
tekstu  programującego  wysyłanego  instrukcją  Send().  Ponadto  niektóre  rozkazy  wymagają 
podania parametrów. 
 
Separatory używane w rozkazach: 
 
dwukropek (:
Oddziela rozkazy niższego poziomu od rozkazów wyższego poziomu; 
 
spacja lub tabulacja ( ) 
Oddziela parametry od rozkazu; 
 
przecinek (,
Oddziela parametry, czyli służy do tworzenia listy parametrów; 
 
średnik (;
Oddziela polecenia z różnych podsystemów; 
 
Przykład: 
 
 

 

"CONF:VOLT:DC 10,0.01V" 

 

 

"TRIG:SOUR EXT" 

 
W  pierwszym  z  tych  rozkazów  podano  dwa  parametry:  10  oraz  0.01V  .  W  drugim  rozkazie 
występuje jeden parametr EXT . Można te rozkazy połączyć za pomocą średnika: 
 
 

 

"CONF:VOLT:DC 10,0.01V; :TRIG:SOUR EXT" 

 
Dzięki  temu  można  wysłać  jeden  tekst  programujący  zamiast  dwóch.  Zbyt  długi  tekst 
programujący  jest  jednak  nieczytelny  i  łatwiej  popełnić  błąd  zwłaszcza,  że  instrukcja  Send() 
wymaga  podania  długości  wysyłanego  tekstu.  W  przypadku  długich  tekstów  programujących 
nie jest więc zalecane łączenie ich w jeden tekst. 
 
znak zapytania (?
Kontroler  może  wysłać  polecenia  w  dowolnym  momencie,  natomiast  urządzenie  z 
zaimplementowanym językiem SCPI może tylko wtedy wysłać odpowiedź, gdy zostanie o tym 
specjalnie  powiadomione.  Jedynie  rozkazy  zakończone  znakiem  zapytania  '?',  czyli  rozkazy 
typu zapytanie, zezwalają urządzeniu na wysłanie odpowiedzi, a właściwie na umieszczenie jej 
w buforze wyjściowym urządzenia. Informację znajdującą się w buforze urządzenia odbiera się 
za  pomocą  instrukcji  Receive().  Ogólnie  większość  rozkazów  języka  SCPI  można  użyć  w 
formie zapytania przez dodanie znaku '?' na końcu rozkazu. 
 
UWAGA:  Nie  powinno  się  wysyłać  następnego  rozkazu  zapytania  jeżeli  nie  odebrano 
(odczytano)  całego  wyniku  (tzn.  wszystkich  jego  bajtów)  poprzedniego  rozkazu  zapytania. 

background image

Wysłanie  kolejno  dwóch  rozkazów  zapytania  bez  odbierania  wyników  od  urządzenia 
spowoduje,  że  w  buforze  wyjściowym  urządzenia  znajdować  się  będzie  część  odpowiedzi  na 
pierwsze  zapytanie  i  cała  odpowiedź  na  drugie  zapytanie.  W  przypadku,  gdy  nie  chcemy 
odbierać  wyniku  pierwszego  zapytania  należy  wyzerować  bufor  wyjściowy  urządzenia  za 
pomocą  instrukcji  DevClear()  i  wtedy  dopiero  wysłać  następny  rozkaz  typu  zapytanie. 
Dotyczy to także sytuacji, gdy nie odebrano wszystkich bajtów wyniku pierwszego zapytania, 
np.  znaku  nowej  linii  umieszczanego  na  końcu  wyniku  pomiaru  (jednym  z  parametrów 
instrukcji Receive()  jest  liczba  bajtów  do  odebrania, co umożliwia celowe bądź przypadkowe 
odebranie części zamiast całego wyniku z bufora wyjściowego). 
 
gwiazdka (*
Rozkazy  zaczynające  się  gwiazdką  (*)  należą  do  grupy  tzw.  ogólnych  rozkazów  standardu 
IEEE-488.2.  Są  to  rozkazy,  które  spełniają  identyczne  funkcje    w  odniesieniu  do  wszystkich 
przyrządów,  które są zgodne ze standardem IEEE-488.2. Rozkazy te służą między innymi do 
identyfikacji  urządzenia  (*IDN?),  samotestowania  (*TST?),  inicjalizacji  urządzenia  (*RST), 
zerowania  bajtu  statusu  (*CLS),  odbioru  bajtu  statusu  (*STB?),  wyzwalania  urządzenia 
(*TRG)  itd.  Inne  przydatne  rozkazy  tego  typu,  służące  do  konfigurowania  systemu  statusu 
omówiono  w  następnym  punkcie  (IV.2).  Przed  rozkazami  tego  typu  nie  umieszcza  się 
dwukropka. 
 
IV.2. SYSTEM STATUSU W STANDARDZIE SCPI. 
 
 

Wszystkie  urządzenia  w  standardzie  SCPI  mają  tak  samo  zorganizowany  system 

statusu. System ten przedstawiono na rysunku 2. 
 

0
1
2
3
4
5
6
7

0
1
2
3
4
5
6
7

Bufor wyj

ściowy

Rejestr Zdarze

ń

Rejestr Maski

Rejestr Sumaryczny

Rejestr Maski

Bajt Statusu

Zdarzenie Standardowe

OR

OR

Odpyt. Szereg.
*STB?

*SRE <warto

ść>

*SRE?

*ESR?

*ESE <warto

ść>

*ESE?

Nie Wykorzystany

Nie Wykorzystany

Nie Wykorzystany

Nie Wykorzystany

Nie Wykorzystany

Nie Wykorzystany

Nie Wykorzystany

Zdarzenie Standard.

Dost

ępny Komunikat

Żądanie Obsługi

Zako

ńcz. Operację

B

łąd Zapytania

B

łąd Urządzenia

B

łąd Wykonania

B

łąd Rozkazu

Zasilanie

 

 

Rys.2. System statusu. 

 

 

System  statusu  składa  się  z  rejestru  zdarzeń  standardowych  i  z  rejestru  bajtu  statusu 

(zwanego  też  sumarycznym  rejestrem  bajtu  statusu).  Są  to  rejestry  przeznaczone  tylko  do 
odczytu.  Ponadto,  każdemu  z  tych  rejestrów  przydzielono  rejestr  maski.  Rejestry  maski 

background image

umożliwiają  maskowanie  poszczególnych  bitów  rejestru  bajtu  statusu  i  rejestru  zdarzeń 
standardowych. Dowolny rejestr maski można zarówno odczytać, jak i zapisać. 
 

Suma  logiczna  niezamaskowanych  bitów  rejestru  zdarzeń  standardowych  daje  w 

rezultacie  jeden  bit,  który  jest  następnie  umieszczany  w  rejestrze  bajtu  statusu  jako  bit  o 
numerze 5. Jest to tzw. bit zdarzenia standardowego
 

Niezamaskowane  bity  rejestru  bajtu statusu także  są sumowane logicznie. Wynik tego 

sumowania  jest  umieszczany  w  rejestrze  bajtu  statusu  jako  bit  o  numerze  6.  Jest  to  tzw. bit 
żądania  obsługi
.  Bit  ten  bezpośrednio  steruje  linią  SRQ  interfejsu.  Ustawienie  się  tego  bitu 
jest  więc  równoważne  ze  zgłoszeniem  przerwania.  Bit  ten  nie  jest  sumowany  logicznie  z 
innymi  niezamaskowanymi  bitami  rejestru  bajtu  statusu,  ponieważ  odpowiadający  mu  bit  w 
rejestrze maski jest zawsze równy zeru (próba ustawienia tego bitu zostanie zignorowana). 
 

Oprócz  bitu  żądania  obsługi  i  bitu  zdarzenia  standardowego  w  rejestrze  bajtu  statusu 

istotny  jest  bit,  który  sygnalizuje  dostępność  komunikatu  (np.  wyniku  pomiaru)  w  buforze 
wyjściowym.  Jest  to  tzw.  bit  dostępności  komunikatu.  Komunikat  w  buforze  wyjściowym 
pojawia  się  w  wyniku  wykonania  rozkazu  typu  zapytanie.  Wykonanie  np.  rozkazu  *STB? 
spowoduje  odczytanie  bajtu  statusu  i  umieszczenie  go  w buforze wyjściowym. Pojawienie się 
komunikatu  w  buforze  wyjściowym  (w  tym  przypadku  bajtu  statusu)  zostaje  następnie 
zasygnalizowane przez ustawienie bitu dostępności komunikatu w rejestrze bajtu statusu. 
 

Pozostałe  niewykorzystane  bity  rejestru  bajtu  statusu  (zwanego  też  sumarycznym 

rejestrem  bajtu  statusu)  mogą  być  wykorzystane  przez  producentów  urządzeń  zgodnych  z 
SCPI.  Przykładowo  w  multimetrze  HP-34401A  znajduje  się  dodatkowy  rejestr  zdarzeń,  a 
mianowicie  rejestr  służący  do  sygnalizacji  niepewności  danych  pomiarowych  (rys.3).  Rejestr 
ten  jest  16-bitowy,  z  czego  wykorzystano  tylko  5  bitów.  Stan  tych  bitów  informuje  między 
innymi  o  przekroczeniu  zakresu  pomiarowego.  Z  rejestrem  tym  stowarzyszony  jest  rejestr 
maski  i  sumator  logiczny.  Wynik  sumowania  umieszczany  jest  w  sumarycznym  rejestrze 
statusu jako bit o numerze 3. Informację dostarczaną przez ten rejestr można wykorzystać np. 
do programowej zmiany zakresu pomiarowego multimetru. 
 

Rejestr Zdarze

ń

Rejestr Maski

OR

STAT:QUES:EVEN?

STAT:QUES:ENAB?

STAT:QUES:ENAB <warto

ść>

Niepewno

ść Danych

0
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15

Bit nr 3
Rejestru
Sumarycznego

Voltage Overload

Current Overload

Ohms Overload

Limit Test Fail LO

Limit Test Fail HI

Not Used

Not Used

Not Used

Not Used

Not Used

Not Used

Not Used

Not Used

Not Used

Not Used

Not Used

 

 

Rys.3. Dodatkowy rejestr zdarzeń zastosowany w multimetrze HP-34401. 

 

background image

10 

 

Zastosowany  system  bajtu  statusu  jest  więc  bardzo  elastyczny.  Rejestry  maski 

umożliwiają  np.  zgłaszanie  żądania  obsługi  spowodowane  różnymi  zdarzeniami.  Żądanie 
obsługi  może  być  zgłoszone  np.  po  pojawieniu  się  komunikatu  w  buforze  wyjściowym 
urządzenia albo np. po zakończeniu wykonywania rozkazu *OPC (bit nr 0 w rejestrze zdarzeń 
standardowych). 
 
 
 

Praktyczne  uwagi  dotyczące  wykorzystania poszczególnych rejestrów systemu statusu 

w programach aplikacyjnych: 
 

###

 Rejestry zdarzeń. 

 

Można je tylko czytać. Bity w rejestrach zdarzeń są zatrzaskiwane, tzn. raz ustawiony 

bit  jest  pamiętany  niezależnie  od  zmian  zdarzenia.  Bity  te  są  automatycznie  zerowane  w 
następujących przypadkach: 
 
-poprzez odczytanie danego rejestru zdarzeń za pomocą rozkazu zapytania np. *ESR? (rejestr 
zdarzeń standardowych) lub STAT:QUES:EVEN? (rejestr niepewności danych w multimetrze 
HP-34401A); 
-za pomocą rozkazu *CLS (clear status). 
 
 

Reset  (*RST)  lub  zerowanie  urządzenia  (DevClear())  nie  zerują  bitów  w  rejestrach 

zdarzeń. Odczytanie  rejestru zdarzeń daje dziesiętną wartość odpowiadającą ważonej binarnie 
sumie bitów ustawionych w rejestrze. 
 

###

 Rejestry maski. 

 

Można  je  czytać  i  zapisywać.  Do  czytania  poszczególnych  rejestrów  maski  służą 

rozkazy typu zapytanie: *SRE?, *ESE?, STAT:QUES:ENAB?. Każdy z nich dotyczy jednego 
rejestru  maski  zgodnie  z  rysunkami  rys.2  i  rys.3.  Odczytanie  dowolnego  rejestru  maski  nie 
zmienia  jego  zawartości.  Rozkaz  *CLS  (clear  status)  także  nie  ma  wpływu  na  zawartość 
rejestrów maski. Do zapisywania rejestrów maski służą następujące rozkazy: *SRE <wartość>, 
*ESE <wartość>, STAT:QUES:ENAB <wartość>. 
 

###

 Sumaryczny rejestr statusu. 

 

Zawiera  sumaryczną  informację  zgłaszaną  przez  pozostałe  grupy  rejestrów.  Bity  w 

sumarycznym  rejestrze  statusu  nie  są  zatrzaskiwane.  Wyzerowanie  np.  któregoś  z  rejestrów 
zdarzeń spowoduje wyzerowanie odpowiadającego mu bitu w sumarycznym rejestrze statusu. 
W szczególności: 
 
-odczytanie  rejestru  standardowego  zdarzenia  lub  rejestru  niepewności  danych  zeruje 
odpowiadające tym rejestrom bity w sumarycznym rejestrze bajtu statusu; 
-odebranie  (odczytanie)  wszystkich  komunikatów  z  bufora  wyjściowego  wyzeruje  bit 
dostępności komunikatu; 
 
 

Wyzerowanie  tych  bitów  może  oznaczać  zniknięcie  przyczyny  żądania  obsługi,  czyli 

dodatkowo  wyzerowanie  bitu  żądania  obsługi,  a  to  oznacza  zmianę  stanu  linii  SRQ  z 
aktywnego  na  nieaktywny.  Przeprowadzenie  odpytywania  szeregowego  także  zeruje  bit 
żądania  obsługi.  Rozkaz  *STB?  zwraca  ten  sam  rezultat  (bajt  statusu)  co  odpytywanie 
szeregowe, nie zeruje jednak bitu żądania obsługi. 
 

Wszystkie bity bajtu statusu można wyzerować rozkazem *CLS. 

background image

11 

UWAGA: Standard IEEE-488.2 nie zapewnia synchronizacji między programem aplikacyjnym 
i  urządzeniem.  Jeżeli  kontroler  (komputer)  jest  za  szybki  to  urządzenie  może  nie  nadążyć  z 
wykonywaniem  wysyłanych  do  niego  rozkazów.  Można  tego  uniknąć  używając  rozkazu 
*OPC?  oraz  instrukcji  Receive().  Rezultatem  wykonania  rozkazu  *OPC?  jest  umieszczenie 
"1<NL>" (dwa bajty: liczba 1 i znak nowej linii) w buforze wyjściowym urządzenia. Instrukcja 
Receive()  charakteryzuje  się  tym,  że  czeka  na  pojawienie  się  wyniku  w  buforze  urządzenia. 
Chcąc  więc  zatrzymać  wysyłanie  kolejnych  rozkazów  dopóki  nie  wykonają  się  np.  rozkazy 
*RST i *CLS, należy wysłać do urządzenia następujący tekst programujący: 
 

"*RST; *CLS; *OPC?" 

 
i  następnie  użyć  instrukcji  Receive().  Wykonywanie  programu  zostaje  wtedy  zatrzymane  na 
instrukcji  Receive()  aż  do  momentu  pojawienia  się  wyniku  w  buforze  urządzenia.  Pojawienie 
się wyniku w buforze sygnalizuje, że urządzenie wykonało rozkaz *OPC? (czyli także rozkazy 
wysłane przed rozkazem *OPC?). Instrukcja Receive() odbiera z bufora wynik zapytania (dwa 
bajty)  i  przekazuje  sterowanie  do  dalszej  części  programu.  Zapewnia  to  wspomnianą 
synchronizację  pomiędzy  programem  i  urządzeniem.  Podobnie  można  użyć  rozkazu  *OPC
którego wykonanie powoduje ustawienie bitu numer 0 w rejestrze zdarzeń standardowych, co 
przy  odpowiednio  skonfigurowanym  systemie  statusu  zostanie  zinterpretowane  jako żądanie 
obsługi. Po wysłaniu sekwencji rozkazów zakończonej rozkazem *OPC wystarczy zaczekać na 
zgłoszenie  przerwania  (instrukcja  WaitSRQ())  a  następnie  usunąć  przyczynę  przerwania,  tzn. 
wyzerować bit zakończenia operacji w rejestrze zdarzeń standardowych (*ESR? albo *CLS). 
 
 
 
IV.3. FORMAT WYNIKU POMIARU W STANDARDZIE IEEE-488.2. 
 
 

Dla pojedynczego wyniku pomiaru przyjęto następujący format: 

 
 

 

 

SD.DDDDDDDDESDD<NL> 

(16 bajtów) 

 

 

-znak wyniku; 

 

D  

-cyfry dziesiętne; 

 

E  

-eksponent; 

 

<nl> 

-znak nowej linii. 

 
Przykład: 

 

+4.00000000E+00. 

 
 
V. PROGRAMOWANIE MULTIMETRU HP-34401A. 
 
 

Funkcje i zakresy pomiarowe multimetru HP-34401A przedstawiono w tabeli 2. 

 
 
 
 
 
 
 

background image

12 

Tab.2. Funkcje i zakresy pomiarowe multimetru HP-34401A. 

Funkcja 

Zakresy pomiarowe 

DC V, AC V 

100mV, 1V, 10V, 100V, 1000V (750Vac) 

###

 2W, 

###

 4W 

100

###

,  1k

###

,  10k

###

,  100k

###

,  1M

###

,  10M

###

100M

###

 

DC I, AC I 

10mA (tylko dc), 100mA (tylko dc), 1A, 3A 

Freq (period) 

3Hz do 300kHz (0.33s do 3.3 

###

s) 

 
 
 

Multimetr  HP-34401A  posiada rozbudowany system wyzwalania. System ten pozwala 

na  automatyczne  generowanie  wyzwolenia,  wykonanie  wielu  pomiarów  po  jednym 
wyzwoleniu  i  umieszczenie  opóźnienia  przed  wykonaniem  każdego  pomiaru.  Po  włączeniu 
zasilania lub po rozkazie *RST, system wyzwalania jest skonfigurowany na wykonanie jednego 
pomiaru  za  każdym  razem,  kiedy  odbierze  wyzwolenie.  System  wyzwalania  można 
skonfigurować  na  wielokrotne  wykonanie  pomiaru  po  każdym  wyzwoleniu  (do  50000 
pomiarów  na  jedno wyzwolenie). Po włączeniu zasilania lub po rozkazie *RST multimetr jest 
skonfigurowany na wewnętrzne źródło wyzwalania i po wprowadzeniu go w stan oczekiwania 
na  wyzwolenie,  zostanie  wykonany  jeden  pomiar,  po  którym  system  wyzwalania  wróci  do 
stanu spoczynkowego. System wyzwalania multimetru HP-34401A przedstawiono na rys. 4. 
 

Idle

State

Wait-for

Trigger

State

Dealy

Measurement

Sample

Sample Trigger
Count

1 Count

1

INITiate

READ?

MEASure?

Initiate Triggering:

TRIGger:SOURce BUS

TRIGger:SOURce EXTernal

TRIGger:SOURce IMMediate

Trigger Source:

TRIGger:DELay

Trigger Delay:

Annunciator

Sample (*)

 

 

Rys.4. System wyzwalania multimetru HP-34401A. 

 
 
 

Wyzwolenie multimetru dające w efekcie wynik pomiaru jest kilkuetapowym procesem, 

na który składają się następujące czynności: 
 
1. Konfiguracja multimetru do pomiaru, tzn. wybranie funkcji, zakresu, rozdzielczości etc; 

background image

13 

2.  Wybór  źródła  wyzwalania,  z  którego  multimetr  będzie  akceptował  wyzwalanie.  Multimetr 
akceptuje trzy źródła wyzwalania: 
 
-bezpośrednie - z wewnętrznego generatora wyzwalającego, oznaczone skrótem IMM; 
-programowe - poprzez magistralę (rozkaz *TRG, instrukcja Trigger()), oznaczone BUS; 
-wyzwalanie  sprzętowe  poprzez  zewnętrzne  wejście  wyzwalające,  oznaczone  skrótem  EXT 
(nie używane w ćwiczeniu); 
 
3.  Wprowadzenie  systemu  wyzwalania  w  stan  oczekiwania  na  wyzwolenie  (wait-for-trigger 
state). Wyzwolenie nie będzie zaakceptowane jeżeli multimetr nie jest w stanie oczekiwania na 
wyzwolenie. 
 
4. Wyzwolenie multimetru. 
 
UWAGA:  Multimetr  HP-34401A  potrzebuje  około  20ms  czasu  na  przejście  do  stanu 
oczekiwania  na  wyzwolenie.  Dowolne  sygnały  wyzwalające,  które  wystąpią  w  tym  okresie 
czasu  zostaną  zignorowane.  W  przypadku  szybkiego  kontrolera  należy  więc  zapewnić 
opóźnienie po wysłaniu rozkazu wprowadzenia w stan oczekiwania na wyzwolenie. W języku 
C można do tego celu wykorzystać funkcję delay(). Jedynym parametrem tej funkcji jest czas 
opóźnienia  w  milisekundach,  przy  czym  funkcja  działa  z  dokładnością  do  1ms.  Prototyp  tej 
funkcji  znajduje  się  w  pliku  nagłówkowym  dos.h,  czyli  przed  użyciem  tej  funkcji  należy 
dołączyć ten plik za pomocą dyrektywy #include na początku programu aplikacyjnego. 
 
 

###

 Konfiguracja multimetru. 

  
 

Do konfiguracji multimetru służy podsystem CONFigure. 

 
UWAGA:  zapis  typu  CONFigure  oznacza,  że  można  używać  skrótu  CONF  zamiast  całej 
nazwy CONFIGURE. 
 
Składnia wybranych rozkazów z podsystemu CONFigure: 
 
-konfiguracja do pomiaru napięcia stałego (DC) albo zmiennego (AC); 
 
CONFigure:VOLTage:DC {<range>|MIN|MAX|DEF}, 
 

 

 

 

 

 

 

{<resolution>|MIN|MAX|DEF} 

 
CONFigure:VOLTage:AC {<range>|MIN|MAX|DEF}, 
 

 

 

 

 

 

 

{<resolution>|MIN|MAX|DEF} 

 
-konfiguracja do pomiaru prądu stałego (DC) albo zmiennego (AC); 
 
CONFigure:CURRent:DC {<range>|MIN|MAX|DEF}, 
 

 

 

 

 

 

 

{<resolution>|MIN|MAX|DEF} 

 
CONFigure:CURRent:AC {<range>|MIN|MAX|DEF}, 
 

 

 

 

 

 

 

{<resolution>|MIN|MAX|DEF} 

 

background image

14 

 
Przykłady: 
 
-konfiguracja do pomiaru napięcia stałego, zakres 10V, rozdzielczość 0.003V: 
 

 

"CONF:VOLT:DC 10, 0.003" 

 
-konfiguracja do pomiaru prądu zmiennego, zakres 1A, rozdzielczość 0.1mA: 
 

 

"CONF:CURR:DC 1, 0.1M" 

 

###

 Wybór źródła wyzwalania. 

 
 

Do wyboru źródła wyzwalania służą rozkazy z podsystemu TRIGger: 

 
 

 

TRIGger:SOURce {BUS|IMMediate|EXTernal} 

 
Przyk³ad: 
 
 

 

"TRIG:SOUR BUS" 

 
Po  włączeniu  zasilania  ustawiane  jest  bezpośrednie  źródło  wyzwalania  (IMM).  Rozkazy  z 
podsystemu CONFIGURE także automatycznie ustawiają źródło wyzwalania na bezpośrednie. 
 

###

 Wprowadzenie systemu wyzwalania w stan oczekiwania na wyzwolenie. 

 
 

Następujące  rozkazy  wprowadzają  system  wyzwalania  w  stan  oczekiwania  na 

wyzwolenie: 
 

 

 

 

 

 

READ? 

 

 

 

 

INITiate 

 

 

 

 

MEASure? 

 

Rozkaz  READ?  działa  tylko  przy źródle  wyzwalania  ustawionym  na  IMM  albo  EXT, 

nie  działa  przy  źródle  BUS.  W  przypadku  źródła  wewnętrznego  (IMM)  wykonanie  rozkazu 
READ?  jest  praktycznie  równoważne  z  wyzwoleniem  multimetru.  Wynik  pomiaru  jest 
umieszczany w buforze wyjściowym multimetru. Przykład: 
 
 

 

 

 

"CONF:VOLT:DC 10, 0.003" 

 

 

 

 

"READ?" 

 
 

Rozkaz  INIT  działa  ze  wszystkimi  źródłami  wyzwalania.  W  odróżnieniu  od  rozkazu 

READ? wynik jest umieszczany w pamięci wewnętrznej multimetru, skąd należy go pobrać do 
bufora  wyjściowego  rozkazem  FETCh?.  Rozkaz  READ?  daje  więc  ten  sam  efekt  co  rozkaz 
INIT  z  następującym  po  nim  bezpośrednio  rozkazem    FETCh?.  Zapamiętywanie  wyników w 
pamięci wewnętrznej jest szybsze niż przesyłanie ich do bufora wyjściowego. Multimetr może 
zapamiętać do 512 wyników w pamięci wewnętrznej. Przykłady: 
 
-wyzwolenie wewnętrzne: 
 
 

 

 

 

"CONF:VOLT:DC 10, 0.003" 

 

 

 

 

"INIT" 

background image

15 

 

 

 

 

"FETCh?" 

 
-wyzwolenie programowe z magistrali: 
 
 

 

 

 

"CONF:VOLT:DC 10, 0.003" 

 

 

 

 

"TRIG:SOUR BUS" 

 

 

 

 

"INIT" 

 

 

 

 

"*TRG" 

 

 

 

 

"FETCh?" 

 
 

Najprostszym sposobem zaprogramowania multimetru jest użycie rozkazu MEASure?. 

Wysłanie  rozkazu  MEASure?  jest  równoważne wysłaniu  rozkazu CONFigure  z bezpośrednio 
po  nim  następującym  rozkazem  READ?.  Wykonanie  rozkazu  MEASure?  powoduje,  że 
multimetr bezpośrednio wykonuje pomiar, co nie zawsze jest korzystne. Rozkaz CONFigure z 
następującym po nim rozkazem INITiate lub READ? jest bardziej elastyczny. Składnia rozkazu 
MEASure  różni  się  tylko  pierwszym  słowem  od  podanej  wcześniej  składni  rozkazu 
CONFigure,  tzn.  zamiast  słowa  CONFigure  należy  użyć  MEASure.  Rozkaz  MEASure 
podobnie jak CONFigure automatycznie ustawia źródło wyzwalania na IMM. 
 
Przyk³ad: 
 
 

 

 

"MEAS:VOLT:DC? 10, 0.003" 

 
 

Pojawienie się komunikatu w buforze wyjściowym jest sygnalizowane przez ustawienie 

bitu  dostępności  komunikatu  w  rejestrze  bajtu  statusu.  W  celu  sprawdzenia,  czy  bit  ten  jest 
ustawiony  można  cyklicznie  odczytywać  bajt  statusu.  Lepszym  rozwiązaniem  jest  takie 
skonfigurowanie systemu statusu, żeby ustawienie bitu dostępności komunikatu spowodowało 
żądanie  obsługi.  Wystarczy  wtedy  czekać  na  aktywny  stan  linii  SRQ  za  pomocą  instrukcji 
WaitSRQ(). Umieszczony w buforze wyjściowym wynik pomiaru można następnie odebrać za 
pomocą instrukcji Receive().  
 
VI. PROGRAMOWANIE GENERATORA HP-33120A. 
 
 

 

Wybrane funkcje i zakresy pomiarowe generatora HP-33120A: 

 
-Zakresy częstotliwości dla różnych kształtów przebiegów: 
 
sinus:   

 

 

 

100

###

Hz - 15 MHz; 

prostokąt: 

 

 

 

100

###

Hz - 15 MHz; 

trójkąt:  

 

 

 

100

###

Hz - 15 kHz; 

 
-Amplituda (przy obciążeniu 50

###

):  50mVpp - 10Vpp; 

 
-Amplituda (bez obciążenia):   

100mVpp - 20Vpp; 

 
-Składowa stała: 

 

 

###

 5 Vpk ac + dc; 

 
-Jednostki wyjściowe:  

 

Vpp, Vrms, dBm; 

 

background image

16 

-Impedancja wyjściowa: 

 

50

###

 

Generator  HP-33120A  może  także  generować  szum  oraz  przebiegi  zmodulowane. 

Istnieje  także  możliwość  zdefiniowania  dowolnego  przebiegu  o  liczbie  próbek  do  4000  i 
umieszczenie  go  w  pamięci  trwałej  generatora.  Generator  posiada  także  układ  wyzwalania, 
umożliwiający  np.  generowanie  pojedynczych  impulsów  z  określoną  fazą  początkową. 
Generator dysponuje obszerną listę rozkazów języka SCPI. 
 
 

Najprostszą  metodą  zaprogramowania  generatora  funkcyjnego  jest  zastosowanie 

rozkazu APPLy: 
 
-generacja fali sinusoidalnej: 
 

 

APPLy:SINusoid [<frequency> [,<amplitude> [,<offset>] ]]; 

 
-generacja fali prostokątnej: 
 

 

APPLy:SQUare [<frequency> [,<amplitude> [,<offset>] ]]; 

 
-generacja fali trójkątnej: 
 

 

APPLy:TRIangle [<frequency> [,<amplitude> [,<offset>] ]]; 

 
-generacja stałej amplitudy: 
 

 

APPLy:DC [<frequency|DEFault> [,<amplitude> [,<offset>] ]]; 

 
frequency - częstotliwość; 
amplitude - amplituda; 
offset - składowa stała. 
 
Uwaga:  W  przypadku  generacji  stałej  amplitudy  parametr  dotyczący  częstotliwości  zostanie 
zignorowany, musi jednak wystąpić w rozkazie jako konkretna wartość albo jako "DEFault". 
 

Zamiast  konkretnych  wartości  amplitudy,  częstotliwości  czy  składowej  stałej  można 

podać np. "MINimum" , "MAXimum" lub "DEFault". 
 
Przykłady: 
 

 

"APPL:SIN 5 KHZ, 3.0 VPP, -2.5 V" 

 
3.0 VPP 

-oznacza 3V wartości międzyszczytowej amplitudy; 

3.0 VRMS 

-oznacza 3V wartości skutecznej amplitudy; 

 
 

 

"APPL:SIN 5.0E+3, 3.0" 

 

 

"APPL:SIN MAX, 3.0, -2.5". 

 
 

Wartość  międzyszczytowa  amplitudy  na  wyjściu  generatora  zależy  od  wartości 

obciążenia  podłączonego  do  wyjścia  generatora.  Do  informowania  generatora  o  wartości 
obciążenia służy rozkaz: 
 

 

OUTPut:LOAD {50 | INFinity | MINimum | MAXimum}. 

 
Domyślną wartością obciążenia jest wartość 50

 (dopasowanie). W przypadku np. dołączenia 

do  generatora  odbiornika  o  wysokiej  impedancji  należy  poinformować  o  tym  generator  za 
pomocą rozkazu: 
 

 

"OUTP:LOAD INF". 

background image

17 

Zapewni to, że wartość amplitudy na wyjściu generatora będzie odpowiadała wartości podanej 
jako parametr rozkazu APPLy. 

background image

18 

VII. PRZYKŁADOWY PROGRAM APLIKACYJNY. 

/*  Uwaga:  Znaczną  część  programu  stanowią  komentarze  oraz  wywołania  funkcji  gpiberr()  poprzedzone 
testowaniem  bajtu  statusu  interfejsu  (ibsta).  Z  punktu  widzenia przygotowania do laboratorium najistotniejsze 
jest przeanalizowanie jakich (i w jakiej kolejności) użyto instrukcji kontrolera oraz rozkazów języka SCPI. */ 

 

/* 
======================================================================== 
 * 
 *  The following program determines if a HP-34401 multimeter is a listener 
 *  on the GPIB.  If the HP-34401 is a listener, ten measurements are read 
 *  and the average of the sum of the measurements is calculated. 
 * 
 *  The status variables IBSTA, IBERR, IBCNT, and IBCNTL are defined in 
 *  DECL.H.  Each bit of IBSTA and each value of IBERR are defined in DECL.H 
 *  as a mnemonic constant for easy recognition in application programs.  In 
 *  this example, these mnemonic definitions are logically ANDed with the 
 *  variable IBSTA to determine if a particular bit has been set. The 
 *  mnemonic definitions are equated with the variable IBERR to determine 
 *  the error code. 
 * 
 *  The function FOUND is called when the HP-34401 is identified as a 
 *  listener on the GPIB.  The ten measurements are read and the average 
 *  of the measurements is calculated. 
 * 
 *  The function GPIBERR is called when a NI-488.2 function fails. 
 *  The error message is printed along with the status variables IBSTA, 
 *  IBERR, and IBCNTL. 
 * 
 *  The NI-488 function IBONL is called from the main program or from the 
 *  function GPIBERR.  When the second parameter of the function IBONL is 
 *  zero, the software and hardware are disabled.  Program execution is 
 *  terminated after calling the function IBONL to disable the software and 
 *  hardware. 
 * 
 *  The function EXIT is used to terminate this program after a call to the 
 *  function GPIBERR.  The exit status is set to 1 to indicate an error has 
 *  occurred. 
 * ======================================================================== 
 */ 
 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
 
/*  DECL.H contains constants, declarations, and function prototypes.       */ 
 
#include "decl.h" 
 
/* 
 *  FOUND is a function called when the HP-34401 is identified as a listener 
 *  on the GPIB.  GPIBERR is an error function that is called when a 488.2 
 *  function fails. 
 */ 
 
void found (unsigned int hp); 
void gpiberr(char *msg); 
 

background image

19 

#define  MAVbit 0x10 

/* Position of the Message Available bit.  

*/ 

 
char 

buffer[101]; 

/* Data received from the HP-34401 

*/ 

int 

loop, 

 

/* FOR loop counter and array index 

*/ 

 

m, 

 

/* FOR loop counter 

 

 

*/ 

 

num_listeners, 

/* Number of listeners on GPIB 

 

*/ 

 

SRQasserted; 

/* Set to indicate if SRQ is asserted  

*/ 

double  sum; 

 

/* Accumulator of measurements   

*/ 

unsigned int  instruments[32], 

/* Array of primary addresses 

*/ 

 

result[31], 

/* Array of listen addresses 

 

*/ 

 

hp, 

 

/* Primary address of the HP-34401 

*/ 

 

pad, 

 

/* Primary address of listener on GPIB 

*/ 

 

statusByte; 

/* Serial Poll Response Byte 

 

*/ 

 
 
void main() { 
 
    system("cls"); 
 
/* 
 *  Your board needs to be the Controller-In-Charge in order to find all 
 *  listeners on the GPIB.  To accomplish this, the function SendIFC is 
 *  called.  If the error bit ERR is set in IBSTA, call GPIBERR with 
 *  an error message. 
 */ 
 
    SendIFC(0); 
    if (ibsta & ERR) { 
 

gpiberr ("SendIFC Error"); 

 

exit(1); 

    } 
 
 
/* 
 *  Create an array containing all valid GPIB primary addresses.  This 
 *  array (INSTRUMENTS) will be given to the function FindLstn to find all 
 *  listeners.  The constant NOADDR, defined in DECL.H, signifies the end 
 *  of the array. 
 */ 
 
    for (loop = 0; loop <= 30; loop++) { 
       instruments[loop] = loop; 
    } 
    instruments[31] = NOADDR; 
 
/* 
 *  Print message to tell user that the program is searching for all active 
 *  listeners.  Find all of the listeners on the bus.   Store the listen 
 *  addresses in the array RESULT.  If the error bit ERR is set in IBSTA, 
 *  call GPIBERR with an error message. 
 */ 
 
    printf("Finding all listeners on the bus...\n"); 
    printf("\n"); 
 
    FindLstn(0, instruments, result, 31); 
    if (ibsta & ERR) { 
 

gpiberr("FindLstn Error"); 

background image

20 

 

exit(1); 

    } 
 
/* 
 *  Assign the value of IBCNT to the variable NUM_LISTENERS.  The GPIB 
 *  interface board is detected as a listener on the bus; however, it is 
 *  not included in the final count of the number of listeners.   Print 
 *  the number of listeners found. 
 */ 
 
    num_listeners = ibcnt - 1; 
 
    printf("Number of instruments found = %d\n", num_listeners); 
 
/* 
 *  Send the *IDN? command to each device that was found.  Your GPIB interface 
 *  board is at address 0 by default.  The board does not respond to *IDN?, so 
 *  skip it. 
 * 
 *  Establish a FOR loop to determine if the HP-34401 is a listener on the 
 *  GPIB.  The variable LOOP will serve as a counter for the FOR loop and 
 *  as the index to the array RESULT. 
 */ 
 
    DevClearList (0, result); 
 
    for (loop = 1; loop <= num_listeners; loop++) { 
 
 

/* 

 

 *  Send the identification query to each listen address in the 

 

 *  array RESULT.  The constant NLend, defined in DECL.H, instructs 

 

 *  the function Send to append a linefeed character with EOI asserted 

 

 *  to the end of the message.  If the error bit ERR is set in IBSTA, 

 

 *  call GPIBERR with an error message. 

 

 */ 

 
 

   Send(0, result[loop], "*IDN?", 5L, NLend); 

 

   if (ibsta & ERR) { 

 

      gpiberr("Send Error"); 

 

      exit(1); 

 

   } 

 
 

/* 

 

 *  Read the name identification response returned from each device. 

 

 *  Store the response in the array BUFFER.  The constant STOPend, 

 

 *  defined in DECL.H, instructs the function Receive to terminate the 

 

 *  read when END is detected.  If the error bit ERR is set in IBSTA, 

 

 *  call GPIBERR with an error message. 

 

 */ 

 
 

   Receive(0, result[loop], buffer, 22L, STOPend); 

 

   if (ibsta & ERR) { 

 

      gpiberr("Receive Error"); 

 

      exit(1); 

 

   } 

 
 

/* 

 

 *  The low byte of the listen address is the primary address. 

background image

21 

 

 *  Assign the variable PAD the primary address of the device. 

 

 *  The macro GetPAD, defined in DECL.H, returns the low byte 

 

 *  of the listen address. 

 

 */ 

 
 

   pad = GetPAD(result[loop]); 

 
 

/* 

 

 *  Use the null character to mark the end of the data received 

 

 *  in the array BUFFER.  Print the primary address and the name 

 

 *  identification of the device. 

 

 */ 

 
 

   buffer[ibcnt] = '\0'; 

 

   printf("The instrument at address %d is a %s\n", pad, buffer); 

 
 

/* 

 

 *  Determine if the name identification is the HP-34401.  If it is 

 

 *  the HP-34401, assign PAD to HP,  print message that the 

 

 *  HP-34401 has been found, call the function FOUND, and terminate 

 

 *  FOR loop. 

 

 */ 

 
 

   if (strncmp(buffer, "HEWLETT-PACKARD,34401A", 22) == 0) { 

 

      hp = pad; 

 

      printf("**** We found the HP-34401A****\n"); 

 

      found(hp); 

 

      break; 

 

   } 

 
 

/* 

 

 *  W laboratorium dostępny jest także multimetr KEITHLEY 2000. 

 

 */ 

 
 

   if (strncmp(buffer, "KEITHLEY", 8) == 0) { 

 

      hp = pad; 

 

      printf("**** We found the KEITHLEY 2000 ****\n"); 

 

      found(hp); 

 

      break; 

 

   } 

 
    }      /*  End of FOR loop */ 
 
    if (loop > num_listeners) printf("Did not find the HP-34401A!\n"); 
 
/*  Call the ibonl function to disable the hardware and software.           */ 
 
    ibonl (0,0); 
 

 
/* ======================================================================== 
 *                      Function FOUND 
 *  This function is called if the HP-34401 has been identified as a listener 
 *  in the array RESULT.  The variable HP is the primary address of the 
 *  HP-34401.  Ten measurements are read from the hp and the average of 
 *  the sum is calculated. 
 * ======================================================================== 

background image

22 

 */ 
 
void found(unsigned int hp) { 
 
/*  Reset the HP-34401 using the functions DevClear.               */ 
 
/* 
 *  If the error bit ERR is set in IBSTA, call GPIBERR with an error message. 
 */ 
 
    DevClear(0, hp); 
    if (ibsta & ERR) { 
 

gpiberr("DevClear Error"); 

 

exit(1); 

    } 
 
/* 
 *  Use the function Send to send the IEEE-488.2 reset command (*RST) 
 *  to the HP-34401.  The constant NLend, defined in DECL.H, instructs 
 *  the function Send to append a linefeed character with EOI asserted 
 *  to the end of the message.  If the error bit ERR is set in IBSTA, 
 *  call GPIBERR with an error message. 
 */ 
 
    Send(0, hp, "*RST", 4L, NLend); 
    if (ibsta & ERR) { 
 

gpiberr("Send *RST Error"); 

 

exit(1); 

    } 
 
    Send(0, hp, "*CLS", 4L, NLend); 
    if (ibsta & ERR) { 
 

gpiberr("Send *CLS Error"); 

 

exit(1); 

    } 
 
/* 
 *  Use the function Send to send device configuration commands to the 
 *  HP-34401. Instruct the HP-34401 to measure volts DC (CONF:VOLTS:DC), 
 *  to wait for a trigger from the GPIB interface board (TRIG:SOURCE BUS), 
 *  and to assert the Service Request line SRQ, when the measurement has 
 *  been completed and the HP-34401 is ready to send the result (*SRE 16). 
 *  If the error bit ERR is set in IBSTA, call GPIBERR with an error message. 
 */ 
 
    Send(0, hp, "*SRE 16; *OPC?", 14L, NLend); 
    Receive(0, hp, buffer, 2L, STOPend); 
 
    Send(0, hp, "CONF:VOLT:DC", 12L, NLend); 
    Send(0, hp, "TRIG:SOURCE BUS", 15L, NLend); 
    Send(0, hp, "*OPC?", 5L, NLend); 
 
    if (ibsta & ERR) { 
 

gpiberr("Send Setup Error"); 

 

exit(1); 

    } 
 
    Receive(0, hp, buffer, 2L, STOPend); 

background image

23 

 
/*  Initialized the accumulator of the ten measurements to zero.            */ 
 
    sum = 0.0; 
 
/* 
 *  Establish FOR loop to read the ten measurements.  The variable m will 
 *  serve as the counter of the FOR loop. 
 */ 
 
 
    for (m=0; m < 10 ; m++) { 
 
      

/* 

 

 *  Trigger the HP-34401 by sending the trigger command (*TRG) and 

 

 *  request a measurement by sending the command "FETC?".  If the 

 

 *  error bit ERR is set in IBSTA, call GPIBERR with an error message. 

 

 */ 

 
 

   DevClear(0, hp); 

 

   Send(0, hp, "INIT", 4L, NLend); 

 

   Send(0, hp, "*TRG", 4L, NLend); 

 

   Send(0, hp, "FETC?", 5L, NLend); 

 
 

   if (ibsta & ERR) { 

 

       gpiberr("Send Trigger Error"); 

 

       exit(1); 

 

    } 

 
 

/* 

 

 *  Wait for the HP-34401 to assert SRQ, meaning it is ready to send 

 

 *  a measurement.  If SRQ is not asserted within the timeout period, 

 

 *  call GPIBERR with an error message.  The timeout period by default 

 

 *  is 10 seconds. 

 

 */ 

 
 

    WaitSRQ(0, &SRQasserted); 

 

    if (!SRQasserted) { 

 

 

printf("SRQ is not asserted.  The HP-34401 is not ready.\n"); 

 

 

exit(1); 

 

    } 

 
 

/* 

 

 *  Read the serial poll status byte of the HP-34401.  If the error 

 

 *  bit ERR is set in IBSTA, call GPIBERR with an error message. 

 

 */ 

 
 

   ReadStatusByte(0, hp, &statusByte); 

 

   if (ibsta & ERR) { 

 

       gpiberr("ReadStatusByte Error"); 

 

       exit(1); 

 

   } 

 
 

/* 

 

 *  Check if the Message Available Bit (bit 4) of the return status 

 

 *  byte is set.  If this bit is not set, print the status byte and 

 

 *  call GPIBERR with an error message. 

 

 */ 

background image

24 

 
 

   if (!(statusByte & MAVbit)) { 

 

       gpiberr("Improper Status Byte"); 

 

       printf(" Status Byte = 0x%x\n", statusByte); 

 

       exit(1); 

 

   } 

 
 

/* 

 

 *  Read the HP-34401 measurement.  Store the measurement in the 

 

 *  variable BUFFER.  The constant STOPend, defined in DECL.H, 

 

 *  instructs the function Receive to terminate the read when END 

 

 *  is detected.  If the error bit ERR is set in IBSTA, call 

 

 *  GPIBERR with an error message. 

 

 */ 

 
 

   Receive(0, hp, buffer, 15L, STOPend); 

 

   if (ibsta & ERR) { 

 

       gpiberr("Receive Error"); 

 

       exit(1); 

 

   } 

 
 

/* 

 

 *  Use the null character to mark the end of the data received 

 

 *  in the array BUFFER.  Print the measurement received from the 

 

 *  HP-34401. 

 

 */ 

 
 

   buffer[ibcnt] = '\0'; 

 

   printf("Reading :  %s\n", buffer); 

 
 

/*  Convert the variable BUFFER to its numeric value and add to the 

 

 *  accumulator. 

 

 */ 

 
 

   sum = sum + atof(buffer); 

 
    }  /*  Continue FOR loop until 10 measurements are read.   */ 
 
/*  Print the average of the ten readings.                                  */ 
 
    printf("   The average of the 10 readings is : %f\n", sum/10); 
 

 
/* ======================================================================== 
 *                      Function GPIBERR 
 *  This function will notify you that a NI-488.2 function failed by 
 *  printing an error message.  The status variable IBSTA will also be 
 *  printed in hexadecimal along with the mnemonic meaning of the bit position. 
 *  The status variable IBERR will be printed in decimal along with the 
 *  mnemonic meaning of the decimal value.  The status variable IBCNTL will 
 *  be printed in decimal. 
 * 
 *  The NI-488 function IBONL is called to disable the hardware and software. 
 * ======================================================================== 
 */ 
 
void gpiberr(char *msg) { 

background image

25 

 
    printf ("%s\n", msg); 
    printf ("ibsta = &H%x  <", ibsta); 
    if (ibsta & ERR )  printf (" ERR"); 
    if (ibsta & TIMO)  printf (" TIMO"); 
    if (ibsta & END )  printf (" END"); 
    if (ibsta & SRQI)  printf (" SRQI"); 
    if (ibsta & RQS )  printf (" RQS"); 
    if (ibsta & SPOLL) printf (" SPOLL"); 
    if (ibsta & EVENT) printf (" EVENT"); 
    if (ibsta & CMPL)  printf (" CMPL"); 
    if (ibsta & LOK )  printf (" LOK"); 
    if (ibsta & REM )  printf (" REM"); 
    if (ibsta & CIC )  printf (" CIC"); 
    if (ibsta & ATN )  printf (" ATN"); 
    if (ibsta & TACS)  printf (" TACS"); 
    if (ibsta & LACS)  printf (" LACS"); 
    if (ibsta & DTAS)  printf (" DTAS"); 
    if (ibsta & DCAS)  printf (" DCAS"); 
    printf (" >\n"); 
 
    printf ("iberr = %d", iberr); 
    if (iberr == EDVR) printf (" EDVR <DOS Error>\n"); 
    if (iberr == ECIC) printf (" ECIC <Not CIC>\n"); 
    if (iberr == ENOL) printf (" ENOL <No Listener>\n"); 
    if (iberr == EADR) printf (" EADR <Address error>\n"); 
    if (iberr == EARG) printf (" EARG <Invalid argument>\n"); 
    if (iberr == ESAC) printf (" ESAC <Not Sys Ctrlr>\n"); 
    if (iberr == EABO) printf (" EABO <Op. aborted>\n"); 
    if (iberr == ENEB) printf (" ENEB <No GPIB board>\n"); 
    if (iberr == EOIP) printf (" EOIP <Async I/O in prg>\n"); 
    if (iberr == ECAP) printf (" ECAP <No capability>\n"); 
    if (iberr == EFSO) printf (" EFSO <File sys. error>\n"); 
    if (iberr == EBUS) printf (" EBUS <Command error>\n"); 
    if (iberr == ESTB) printf (" ESTB <Status byte lost>\n"); 
    if (iberr == ESRQ) printf (" ESRQ <SRQ stuck on>\n"); 
    if (iberr == ETAB) printf (" ETAB <Table Overflow>\n"); 
    printf ("ibcnt = %d\n", ibcntl); 
    printf ("\n"); 
 
/*  Call the ibonl function to disable the hardware and software.           */ 
 
    ibonl (0,0); 

 
VIII. ZADANIA. 
 
1.  Jaka  sekwencja  rozkazów  jest  potrzebna  do  skonfigurowania  systemu  bajtu  statusu  tak, 
żeby  zgłoszenie  żądania  obsługi  następowało  tylko  w  wyniku  pojawienia  się  komunikatu  w 
buforze  wyjściowym  urządzenia.  Założyć,  że  nie  znamy  aktualnego  stanu  rejestrów  systemu 
statusu. 
 
2.  Przeanalizować  przykładowy  program  aplikacyjny  z  punktu  VII  i  zastanowić  się  jak 
należałoby go zmodyfikować, aby efektem jego działania było: 
-zaprogramowanie  generatora  funkcyjnego  (kształt  przebiegu:  sinus,  częstotliwość:  2kHz, 
amplituda: 2VRMS, składowa stała: -1V); 

background image

26 

-zaprogramowanie woltomierza (AC, zakres: 10V, rozdzielczość: 0.01V); 
-obliczenie średniej z wykonanych 20 pomiarów. 
 

Przyjąć,  że  generator  ma  adres  10  a  multimetr  adres  22  i  nie  przeprowadzać 

identyfikacji.  W  celu  sprawdzenia,  czy  generator  i multimetr  wykonały wysłane  im  sekwencje 
rozkazów  konfigurujących,  zastosować  rozkaz  *OPC?.  Multimetr  wyzwalać  ze  źródła 
wewnętrznego.  Obecność  wyniku  pomiaru w buforze multimetru  powinna być sygnalizowana 
jako żądanie obsługi. 
 
3.  Zastanowić  się  nad  użyciem  instrukcji  TestSRQ()  zamiast  WaitSRQ()  i  nad  tym  czy  w 
przypadku przykładowego programu ma sens taka zamiana. 
 
4.  Zastanowić  się  jaki  będzie  wynik  pomiaru,  jeżeli  generator,  do  którego  jest  podłączony 
tylko  woltomierz,  zaprogramowano  w  następujący  sposób:  obciążenie  50

,  amplituda 

międzyszczytowa  sinusoidalnego  sygnału  wyjściowego  1Vpp.  Woltomierz  zaprogramowano 
na pomiar napięcia międzyszczytowego AC.