Używamy plików cookies (tzw. ciasteczka) by spersonalizować treść i ogłoszenia oraz by analizować ruch na stronie.  W sposób automatyczny dzielimy się informacjami o Twoim użyciu tego portalu z dostawcami ogłoszeń, którzy mogą połączyć te informacje z informacjami, które im udzieliłaś/łeś lub, które sami zebrali. Korzystanie z witryny bez zmiany ustawień dotyczących cookies oznacza, że będą one zamieszczane w Państwa urządzeniu końcowym.  Możecie Państwo dokonać w każdym czasie zmiany ustawień dotyczących cookies zmieniając opcje przeglądarki.

Adresowanie zmiennych w pamięci PLC

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:

addressing1

Gdy go uruchomimy (F5), zobaczymy coś takiego:

addressing2

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.

addressing3

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:

  • Zmienne typu BOOL adresuje się podając numer „słowa” i po kropce numer bitu od 0 do 15. W powyższym przykładzie mamy od %MX11.0 do %MX11.15.
  • Zmienne typu BYTE adresuje się podając kolejne numery bajtów, a więc %MB22, %MB23 itd.
  • Zmienne typu WORD adresuje się podając kolejne numery ‘słów’, czyli %MW11, %MW12…

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
Q
M

Wejścia
Wyjścia
Flagi/Zmienne

C

X
B
W
D

Pojedynczy bit
Byte
Word
Doubleword

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?

  1. Zmienne typu BOOL należy adresować jako MX…, a nie, jak pisałem dotychczas MB. Szkoda pamięci. W jednym bajcie mieści się 8 bitów.
  2. Odczytując zmienne wyższego rzędu, można jednym ruchem pobrać dane o wielu zmiennych bitowych. Zamiast więc wykonać 16 zapytań o zmienne od MX0.0 do MX0.15, można pobrać wartość zmiennej przechowywanej pod MW0, rozbić ją na wartość binarną i odczytać poszczególne bity. Zamiast sprawdzać stany poszczególnych wyjść odpytując IX3.1, IX3.3 itd., można pobrać słowo IW3, zamienić na system dwójkowy i w ten sposób poznać stany pojedynczych wyjść.
  3. Zrozumienie tego wszystkiego otwiera drzwi do komunikacji MODBUS. Samo w sobie jest to mocno ekscytujące, gdyż wcześniej całe to rozróżnianie Input Coils i Input Registers tylko mnie denerwowało… ale o tym w kolejnym artykule ;)