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
2
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.
3
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
z
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.
4
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);
5
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
6
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 ibcnt, iberr 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.
7
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.
8
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
9
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.
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.
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)
S
-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.
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;
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}
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"
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;
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".
17
Zapewni to, że wartość amplitudy na wyjściu generatora będzie odpowiadała wartości podanej
jako parametr rozkazu APPLy.
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);
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");
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.
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.
* ========================================================================
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);
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.
*/
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) {
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);
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.