W tej części opowiem jak wykorzystywać proste bloki funkcyjne, które usprawniają programowanie upraszczają kod i umożliwiają realizację złożonych zadań.
Niejszy artykuł bazuje na pojęciach i plikach omawianych w poprzednich częściach - zachęcam więc, by czytać całość od samego początku
Zmodyfikujmy nieco nasz program z poprzedniego artykułu. Niech wyjście OUT1 zależy od wejścia IN1 w następujący sposób:
OUT1 := IN1;
Co się wtedy stanie?
Po sprawdzeniu projektu (Project -> Build), wgraniu go (Online -> Login) i uruchomieniu (Online -> Run) przetestujmy działanie programu. Podając napięcie 24V+ na wejście IN1, która jest pierwszą dziurką pierwszego modułu wejść, powinniśmy włączać wyjście OUT1, które jest pierwszą dziurką pierwszego modułu wyjść, czyli trzeciego modułu w naszej konfiguracji. Gdyby wyjście OUT1 podłączone było do przekaźnika, a przekaźnik zwierał obwód, w który wpięta jest żarówka podłączona do 230V, zobaczylibyśmy, jak światło zapala się i gaśnie. Warto to przetestować :)
No dobrze, ale w automatyce stosujemy łączniki ścienne zwierne, które zwierają wejścia na czas przyciśnięcia, a nie przełączają się na stałe. Jak sterować w takiej sytuacji oświetleniem?
Osoby przyzwyczajone do innych języków programowania mogą chcieć zaproponować następujący kod:
IF IN1 = TRUE THEN (* lub w skrócie IF IN1 THEN *) OUT1 := NOT OUT1; END_IF;
Co na ludzki język chciało by się przetłumaczyć następująco: jeśli IN1 jest prawdziwe (tzn, pojawiło się napięcie w pierwszej dziurce), wtedy odwróć stan OUT1.
To niestety nie zadziała. Program w PLC wykonywany jest kilkadziesiąt razy na sekundę i nie wie, czy napięcie się właśnie ‘pojawiło’, czy już jakiś czas jest. Tak długo jak długo przyciśnięty jest przycisk, każdy cykl programu zmieniać będzie stan OUT1!
To ważne. Zatrzymajmy się i przeczytajmy powyższe kilka razy. Program:
IF IN1 THEN OUT1 := NOT OUT1; END_IF;
wykonywany jest raz za razem. W trakcie pół sekundowego przyciśnięcia przycisku (podania prądu do IN1), stan OUT1 zostanie odwrócony kilkadziesiąt razy. Na czym stanie? Nie wiadomo. Sprawa przypadku.
W trakcie całego programowania PLC należy o tym pamiętać i myśleć nie o stanach prawda/fałsz ale o wykrywaniu zbocza wznoszącego i opadającego danej zmiennej, czyli momenu, w którym zmienna lub wejście zmienia stan. Program nasz powinien brzmieć: jeśli IN1 zmieni stan z FALSE na TRUE, zmień raz stan OUT1.
CoDeSys oferuje bloki funkcyjne, które zajmują się takim ‘wykrywaniem zbocza”. Przetestujmy jeden z nich. W definicjach:
PROGRAM PLC_PRG VAR IN1_TRIG: R_TRIG; END_VAR
W programie:
IN1_TRIG(CLK := IN1); IF IN1_TRIG.Q THEN OUT1 := NOT OUT1; END_IF;
R_TRIG jest blokiem funkcyjnym wykrywającym wznoszące zbocze impulsu (stąd „Rising Trigger” => R_TRIG).
Blok funkcyjny to taka czarna skrzynka, która przyjmuje coś na wejściu i oddaje coś na wyjściu. Linia IN1_TRIG (CLK := IN1) informuje, że do wejścia CLK bloku IN1_TRIG należy podłączyć stan fizycznego wejście IN1.
IN1_TRIG będzie się odtąd ‘kręcić’ w programie, ilekroć ten będzie wykonany (czyli kilkadziesiąt razy na sekundę). Gdy tylko IN1 zmieni się z FALSE na TRUE (podamy napięcie na IN1), blok IN1_TRIG na wyjściu Q poda wartość TRUE, ale zrobi to tylko 1 raz! W konsekwencji warunek IF IN1_TRIG.Q THEN…. zostanie spełniony tylko raz i tylko raz odwrócona zostanie wartość OUT1.
Przetestujmy!
Jeszcze jedna rzecz – utraciliśmy możliwość sterowania przez wizualizację. Trzeba to naprawić! W definicjach dodajemy deklarację zmiennej VIS_light i bloku funkcyjnego wykrywającego ‘wznoszące zbocze’ tej zmiennej.
VAR IN1_TRIG: R_TRIG; VIS_light : BOOL; VIS_light_TRIG : R_TIRG; END_VAR
A w programie mamy teraz:
IN1_TRIG(CLK := IN1); VIS_light_TRIG(CLK := VIS_light); IF IN1_TRIG.Q OR VIS_light_TRIG.Q THEN OUT1 := NOT OUT1; END_IF;
Zmieńmy też konfigurację przycisku na wizualizacji by jego przyciśnięcie zmieniało stan zmiennej VIS_light na czas przyciśnięcia: Klikamy 2x na przycisk “Click!”, wybieramy Input, czyścimy checkbox przy „Toggle variable”, zaznaczamy pole przy „Tap variable” i wpisujemy „PLC_PRG.VIS_light”:
Powinno działać :)