Muszę przyznać, że rozmieszczenie zmiennych w pamięci i ich adresy było do niedawna dla mnie czarną magią. Wychowany na prostych językach programowania takich jak Basic czy JavaScript, gdy potrzebowałem zadeklarować jakąś zmienną, pisałem „VAR zmienna" i tyle. Nigdy nie zastanawiałem się, gdzie przechowywana w zmiennej wartość jest faktycznie w pamięci. Wiedza ta była dla mnie zbędna.
W przypadku PLC adresowanie stało się o tyle istotne, że po podaniu adresu, można jej wartość odczytać przez webserver stosując komendę READPI. Jeśli więc zadeklaruję: „JakasZmienna AT MB0 : BYTE" i wykonam komendę READPI ADR=MB0&FORMAT=%d (tworząc plik ze skryptem SSI umieszczony na sterowniku, lub wpisując w pasek przeglądarki http://AdresSterownika/READPI?ADR=MB0&FORMAT=%d) otrzymam aktualną wartość zmiennej „JakasZmienna". Jakie jednak są różnice między adresami różnych typów zmiennych? Czym różni się adres MX0.2 od MB2 i MW2? Poniżej przedstawiam moje wyjaśnienie.
Sterownik 750-841 posiada 8kB adresowalnej pamięci, którą można wykorzystywać do przechowywania zmiennych użytkownika i wymiany danych. Pamięć ta składa się z pojedynczych bitów (przyjmujących wartości 0 lub 1), które połączone w grupy po 8 dają bajty (przyjmujące wartości od 0 do 255), które połączone w pary mogę przechowywać wartości typu WORD (przyjmujące wartości od 0 do 65 536), które z kolei połączone w pary mogą pomieścić wartości typu DWORD. Można sobie wyobrazić, że pamięć ta jest jak tasiemka, którą możemy kroić na pojedyncze bity, większe bajty itd.
Gdy w CoDeSysie definiujemy zmienną podając jej adres, tak naprawdę mówimy kompilatorowi, gdzie ma sięgnąć, by odczytać/zapisać wartość danej zmiennej. Jeśli zaadresujemy zmienną typu BOOL pod MX0.0, wskażemy, że wartość tej zmiennej dostępna będzie w pierwszym bicie naszej pamięci. Gdy zaadresujemy zmienną typu BYTE pod MB0, stwierdzimy, iż jej wartość obejmuje pierwszy bajt, czyli pierwsze 8 bitów pamięci.
Oto pierwsza rzecz, która była dla mnie zaskoczeniem. Adresując różne zmienne, możne przypisać je do pokrywających się obszarów pamięci. Zmieniając wartość zmiennej pod adresem MX0.0, zmieniamy równocześnie wartość tej, przypisanej do MB0 (oraz MW0 i MD0, o czym później).
Aby unaocznić tę zależność przygotowałem prosty program definiujący różne zmienne:
V_BOOL AT %MX0.0 : BOOL; V_BYTE AT %MB0 : BYTE; V_WORD AT %MW0 : WORD; V_DWORD AT %MD0 : DWORD; V_STRING AT %MD0 : STRING(4);
W części programowej umieściłem jedynie:
V_STRING:='abcd'
Gdy skompilujemy taki program w trybie symulacji, naszym oczom ukażą się następujące wartości zmiennych:
Gdy go uruchomimy (F5), zobaczymy coś takiego:
Cóż się takiego stało i skąd te wszystkie liczby?
Zmiennej V_STRING przypisujemy wartość 'abcd', z czego 'a' przechowywane jest w 1 bajcie, 'b' w 2 itd. Litera 'a' w kodzie ASCII ma wartość 97, stąd też wartość zmiennej V_BYTE zaadresowanej do pierwszego bajtu pamięci (czyli również pierwszego znaku stringu V_STRING!) wynosi 97. 97 w postaci bitowej (w systemie dwójkowym to) 0110001, stąd też wartość zmiennej V_BOOL zaadresowanej do pierwszego bitu pamięci wynosi 1, czyli TRUE.
Litera 'b' w kodzie ASCI to 98, stąd też w drugim bajcie pamięci przechowywana jest wartość 98. W konsekwencji zmienna V_WORD ma wartość 97+256*98, czyli 25 185. Wartość zmiennej V_DWORD wyliczyć można jako 97+256*98+2562*99+2563b*100 (gdzie 99 to litera 'c' a 100 to litera 'd').
Oto link do prostego programu zawierającego powyższy kod, umożliwiającego wpisywanie wartości przez prostą wizualizację. Proponuję spróbować zmienić wartość którejkolwiek ze zmiennych, by zobaczyć, jak zmieniają się wartości pozostałych.
Teraz kilka słów o konwencji adresowania. Oto przykład zaczerpnięty z instrukcji sterownika:
Bit | %MX11.0 ... 15 | %MX12.0 ... 15 | ||
Byte | %MB22 | %MB23 | %MB24 | %MB25 |
Word | %MW11 | %MW12 |
Czyli:
Co więcej, ogólna konwencja dla adresu w postaci „ABCD” jest następująca:
Pozycja | Dozwolone wartości | Opis |
A | % | Oznaczenie adresu |
B |
I |
Wejścia |
C |
X |
Pojedynczy bit |
D | Adres |
No proszę! Możliwe jest więc adresowanie wejść i wyjść sterownika. Wcześniej korzystałem z tego odczytując np. stan wejścia poprzez READPI?ADR=IX8.2. Teraz wiem, że tak naprawdę odczytywałem 3 bit z 8 słowa w obszarze pamięci dedykowanej do przechowywania stanów wyjść.
Co wynika z powyższego?