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
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. 2
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, Znalezienie wszystkich odbiorników resultlist, limit) 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, Czytanie bajtów danych z bufora count, termination) 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, Odpytanie szeregowe pojedynczego result) 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. 3
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); 4
4. TestSRQ (board, result) - instrukcja służy do testowania stanu linii SRQ. Przyk³ad: 5. WaitSRQ (board, result) short result; TestSRQ (0, &result); if (result ==1) { SRQ jest aktywna else { SRQ jest nieaktywna - 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 5
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" :D :E :F :G :H :I :J :K :L = :C:L :M :N = :B:H:N Rys.1. Hierarchiczna struktura systemu rozkazów języka SCPI. 6
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. 7
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. Bufor wyjściowy Zdarzenie Standardowe Rejestr Zdarzeń Rejestr Maski Zakończ. Operację Nie Wykorzystany Błąd Zapytania Błąd Urządzenia Błąd Wykonania Błąd Rozkazu Nie Wykorzystany Zasilanie *ESR? 0 1 2 3 4 5 6 7 *ESE <wartość> *ESE? OR Bajt Statusu Rejestr Sumaryczny Rejestr Maski Nie Wykorzystany Nie Wykorzystany Nie Wykorzystany Nie Wykorzystany Dostępny Komunikat Zdarzenie Standard. Żądanie Obsługi Nie Wykorzystany Odpyt. Szereg. *STB? 0 1 2 3 4 5 6 7 *SRE <wartość> *SRE? OR 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 8
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. Niepewność Danych Rejestr Zdarzeń Rejestr Maski Voltage Overload 0 Current Overload 1 Not Used 2 Not Used 3 Not Used 4 Not Used 5 Not Used 6 OR Not Used 7 Not Used 8 Ohms Overload 9 Not Used 10 Limit Test Fail LO 11 Limit Test Fail HI 12 Not Used 13 Not Used 14 Not Used 15 STAT:QUES:EVEN? STAT:QUES:ENAB <wartość> STAT:QUES:ENAB? Bit nr 3 Rejestru Sumarycznego Rys.3. Dodatkowy rejestr zdarzeń zastosowany w multimetrze HP-34401. 9
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. 10
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 D E <nl> Przykład: -znak wyniku; -cyfry dziesiętne; -eksponent; -znak nowej linii. +4.00000000E+00. V. PROGRAMOWANIE MULTIMETRU HP-34401A. Funkcje i zakresy pomiarowe multimetru HP-34401A przedstawiono w tabeli 2. 11
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. Initiate Triggering: MEASure? READ? INITiate Idle State Trigger Source: TRIGger:SOURce IMMediate TRIGger:SOURce EXTernal TRIGger:SOURce BUS Wait-for Trigger State Trigger Delay: TRIGger:DELay Dealy Sample (*) Annunciator Measurement Sample Sample Trigger Count 1 Count 1 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; 12
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 13
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. Przyk³ad: Do wyboru źródła wyzwalania służą rozkazy z podsystemu TRIGger: TRIGger:SOURce {BUS IMMediate EXTernal "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" 14
"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: prostokąt: trójkąt: 100###Hz - 15 MHz; 100###Hz - 15 MHz; 100###Hz - 15 khz; -Amplituda (przy obciążeniu 50###): 50mVpp - 10Vpp; -Amplituda (bez obciążenia): -Składowa stała: -Jednostki wyjściowe: 100mVpp - 20Vpp; ### 5 Vpk ac + dc; Vpp, Vrms, dbm; 15
-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". 16
Zapewni to, że wartość amplitudy na wyjściu generatora będzie odpowiadała wartości podanej jako parametr rozkazu APPLy. 17
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); 18
#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"); 19
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. 20
* 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. * ======================================================================== 21
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); 22
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. 23
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) { 24
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); 25
-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. 26