BCD számábrázolás, CPU stop

Nehéz volt eldönteni hogy ezt a témát S7-hez, a ProTool vagy a WinCC Flexible tippekhez tegyem. Egy kicsit mindegyikhez tartozik. Mivel egy megoldás mellékhatásaként jelentkező problémát taglal aminek az eredménye a CPU leállása, azt hiszem ide tartozik a leginkább.

Elég gyakori igény, hogy HMI-vel felszerelt rendszerekben a HMI-ről (a továbbiakban OP) be lehessen állítani egy időtag időzítésének hosszát vagy egy számláló számlálási értékét.
Erre több megoldás is elképzelhető. Én most ezek közül leírok rosszat és jót is.
Általában mindegyik megoldás abból indul ki, hogy az OP-n elhelyezünk egy input mezőt, amibe a kezelő beírhaja a kívánt időzítés. Ezt az OP eltárolja valahol. Célszerűen egy DB-ben, amiben mindenféle beállításokat tárolunk. Tehát létrehozunk egy TAG-et, amiz említett DB egy word-jére mutat, majd ezt átadjuk az input mezőnek, a DB word-öt meg valamilyen módon továbbítjuk a Timer TV paraméteréhez.
A kulcs kérdés itt az adattípus és az a mód, ahogy a kezelő által beírt értéket az időtagnak átadjuk.

1. Az időtag beállítását tartalmazó változót egész számként deklaráljuk (INT)
Ekkor egy INT típusú TAG-et kell létrehozni az OP-ban. Mivel az aidőtagok számára (és a számlálóknak is) a TV értéket BCD formátumban kell megadni, szükség van egy adatkonverzióra. Az OP által DB-be továbbított értéket BCD formátumúra alakítjuk és egy lokális változóba tesszük, amit megadunk az időtagnak.
Amennyiben ezt létrában vagy FBD-ben karjuk kivitelezni, nehézségekbe ütközünk, mert a létra és FBD meglehetősen finnyás az adattípusokra. Az egészből BCD-re konvertáló utasítás  (I_BCD) WORD típusba teszi a BCD-ben ábrázolt számot. A timernek a TV bemenete viszont S5TIME típust vár, a WORD-öt nem fogadja el. Nincs más választásunk, mint két lépésre bontani a dolgot:
Először átkonvertáljuk az OP-ról érkező integert WORD típusúra az I_BCD utasítás segítségével, az eredményt egy WORD típusú lokális változóba helyezzük ideiglenesen. Majd egy MOVE utasítással a WORD típusú változóból a BCD értéket egy másik lokális változóba másoljuk, ami már S5TIME típusú, majd ez utóbbi változót megadjuk a timer TV paraméterében:



Mivel a MOVE utasítás nem finnyás a típusokra, megengedi hogy WORD-öt S5TIME-ba másoljunk vele.
Elkerülhetjük a fölösleges lépést ha STL-ben konvertáljuk át az OP-n beírt értéket S5TIME-ba. STL-ben a fordító nem végez típusellenőrzést, nekünk kell gondoskodni arról, hogy az ITB utasítás tényleg integer típusú adatot kapjon és a mi felelősségünk mit kezdünk az eredménnyel.
STL-ben a fenti megoldás tehát a következő:



Ez már így működik, de két probléma van vele.
Az egyik az, hogy az S5TIME típus ábrázolása azért teljesen nem felel meg a BCD számnak, mert míg a BCD egy 3 digites előjeles szám, melynek bal oldali digitje tartalmazza az előjelet (0000-:pozitív 1111: negatív), addig az S5TIME egy 3 digites BCD szám, aminek bal oldali (magas helyiértékű) digitje az időzítő időalapját határozza meg az alábbiak szerint:


Az időalap Az időalap kódja
10 ms 00
100 ms 01
1 s 10
10 s 11

A magas helyiértékű digit jelentése tehát eltér a két típusnál. Ez további két problémát okoz.
Egyrészt az OP-n beírt érték (egész szám), amennyiben az pozitív, mindig 10ms-os időalappal számolt időt fog meghatározni. Pl. ha 526-ot írunk be, akkor az 5.260 másodper időzítést fog jelenteni (526*10 ms).
Másrészt ha a beírt szám negatív, az 10 másodperces időalapot fog használni, így pl. ha -526 a beadott érték, akkor 5260 másodperces időzítés jön létre (87 perc, 40 másodperc) Ez azért történik így, mert a pozitív számnál a legnagyobb helyiértékű digit tartalma bináris 0000, negatív értéknél pedig 1111. A fenti időalap táblázatból kitűnik, hogy a 0000 10ms időalapot fog adni, az 1111 pedig 10sec időalapot, mivel ez a digit kerül az S5TIME típus legfelső digitjébe, ami az időalapot határozza meg.
Így tehát nem tudjuk kihasználni a többi időalapot.

A másik probléma sokkal nagyobb. Integer-ből konvertálunk BCD-re. Az integer ábrázolási tartománya -32768-tól 32767-ig, a BCD ábrázolási tartománya pedig -999-tól +999-ig. Azt hiszem szembetűnő milyen konfliktus adódhat ebből.
Ha a kezelő 999-nél nagyobb értéket ír be, akkor két eset lehetséges:

1.
Amennyiben a fenti példa szerint létrában írtuk meg a BCD konverziót, túl nagy baj nem történik, a hibás értéket az I_BCD nem konvertálja, a kimenetén nem jelenik meg új érték, így az időzítés a legutolsó még helyes értékkel történik meg. Minden megy tehát tovább, a hibás érték figyelmen kívül hagyásával.

2.
Ha az STL változatot használtuk, akkor baj van. Az ITB ugyan itt sem hajtódik végre ha a forrás adat nem felel meg a BCD szám ábrázolási tartományának, de az ITB-t követő T utasítás (Transfer) feltétel nélkül, mindig végrehajtásra kerül. Mint azt tudjuk az ITB az ACCU1 regiszterből veszi a konvertálandó értéket (az előtte lévő Load ide tölti be azt a DB-ből) és az eredményt is az ACCU1-be írja be. Amikor túlcsordulás miatt nem kerül végrehajtásra a konverzió, az ACCU1-be nem ír semmit az ITB. Ott marad a korábbi érték, tehát az, amit konvertálni akartunk. Ezt azonban a transfer szépen továbbítja a timernek. Ha mondjuk a kezelő 1000-et adott meg, akkor a timer ezt binárisan fogja megkapni (hexában ez 3E8) ami semmiképpen nem felel meg a BCD ábrázolás szabályainak és keletkezik egy BCD conversion error, az oprendszer megpróbálja meghívni az OB121-et (programming error). Amennyiben ez nem létezik, a CPU STOP állapotba kerül és a program futása leáll:



Ez igen váratlan tud lenni, gondoljuk csak el: A berendezés zavartalanul üzemel, a kezelő próbál finomítani néhány beállításon és a fentebb vázolt módon megvalósított időzítő paraméterezésnél kicsit növelni akarja a 850-es értéket, mert kevés a 8.5 másodperces késleltetés, beír hát 1000-et. Abban a pillanatban minden leáll és csend lesz. A kiérkező karbantartó előbb-utóbb rájön hogy a CPU STOP-ban van és megpróbálja újraindítani, ami nem sikerül, mert a hibás konverzió ismér leállítja ahányszor csak próbálkozik. Csak úgy tudja újra elindítani a rendszert, ha az értéket 999-re vagy kisebbre módosítja. Egy értéket átírni sokáig sem tart, és nem is nehéz. De megtalálni hogy mit kell átírni, az lehet nehéz (különösképpen ha a kezelő tagadja hogy bármit módosított volna a megállás előtti pillanatban :)!

OB121 CPU stop ellen
Ha váratlanul minden megáll CPU stop miatt, az elég egyértelmű. A fenti hibát alattomosabbá lehet tenni azzal, hogy egy üres OB121-et teszünk a PLC-be. BCD conversion error esetén megpróbálja meghívni az oprendszer, és sikerül is, mivel létezik. Igaz üres lévén nem csinál semmit, de ez már elég ahhoz, hogy a CPU ne álljon le. Minden megy tehát tovább. De ezzel nem kerültük el a BCD konverzió problémáját, a BCD konverzió továbbra is sikertelen. Emiatt az időtag nem fog megfelelően működni. Ez pedig kevésbé szembetűnő mint egy teljes leállás (bár technológi függő, de bizonyos rendszereknél ez okoz kisebb problémát). A híbát így esetleg nehezebb megtalálni.
Az üres OB121 még egy következménnyel is jár BCD konverziós hiba bekövetkezésekor: teleírja a diagnosztikai puffert kisöpörve minden más üzenetet belőle. A konverziós hiba minden PLC ciklusban bekövetkezik (bár ez nyilván az időtagot tartalmazó blokk hívásától, függ, de az a jellemző hogy minden ciklusban lefut).



A BCD hiba elnyom minden más üzenetet, nem tudjuk megnézni milyen más hibaüzenetek voltak korábban.


Mi a megoldás?
Ha ismerjük ezt a problémát, az OP-ban korlátozhatjuk az érték megadásának maximumát 999ben, a minimumát 0-ban. Vagy összehasonlítással a PLC programban is meggátolhatjuk, hogy a hiba bekövetkezzen, de jobb megoldásnak tartom, ha az OP-ban eleve Timer típusú TAG-et állítunk be és közvetlenül átadjuk azt az időzítőnek.
Az OP ismeri az S5TIME típust ezért ezzel a megoldással nem lehet olyan értéket beírni ami hibát okoz és egycsapásra megoldódik az időalap problémája is.

ProTool példaWinCC Flexible példa

Az input mezőben mindig ezred másodpercet kell megadni a beírt időzítéstől és az ahhoz szükséges időalaptól függetlenül. Az OP fogja meghatározni hogy a beírt időzítést melyik időalappal lehet megoldani. Az input mező tizedes pontját eltoljuk 3 helyiértékkel és készen is van a késleltetés beírására való input mező.
Az érték beírásakor amennyiben nem akarunk ezred másodpercekkel is bajlódni egyszerűen csak beírunk egy egész számot. Ha nem nyomjuk le a tizedes vesszőt, akkor azt egész másodpercnek fogja venni.
Ha korlátozni szeretnénk, hogy a kezelő ne tudjon extrém nagy vagy adott esetben extrém kicsi időzítést beállítani, akkor az input mezőhöz tartozó TAG Limit paramétereivel korlátozható a beírható érték:

ProTool példa WinCC Flexible példa

A korlátozást ugyan úgy ezred másodpercben kell meghatározni, mint magát az időzítést is. A fenti példa szerinti 6500 tehát 6.5 másodpercet, az 1000 pedig 1 másodpercet jelent. Így 1-6.5 másodpercig lehet időt állítani.

Egy másik rossz megoldás

Még rosszabb az, ha az OP-ban egy integer típusú TAG-et állítunk be időparaméternek, ahogy fentebb is, de nem használunk BCD konverziót, hanem közvetlenül megadjuk ezt az integert az időtagnak:

Miért rosszabb? Mert itt mégsúlyosabb típus összeférhetetlenség van. Az integer bináris, a timernek BCD kell. A kétféle számábrázolás között elég nagy különbség van, és ez nem csak az ábrázolási tományban és az előjel digit eltérő értelmezésében nyilvánul meg.
Egy ilyen megoldásnál ha a kezelő által beírt decimális szám (hiszen ő decimálisan adja meg) bináris formája (mivel az integer binárisan kerül tárolásra) nem pont olyan, ami megfele egy BCD számnak, akkor máris konverziós hiba lesz az eredmény. Vagyis nem csak akkor ha 999-nél nagyobb értéket írnak be!
Már akkor is, ha pl. 12 a megadott érték. Ez ugyanis hexadecimálisan 0C, ami nem felel meg a BCD ábrázolásnak, ugyanis egyik digit 4 bitje sem lehet 9-nél nagyobb (márpedig itt a C nagyobb).
El lehet ugyan találni akár véletlenül is egy olyan számot ami nem okoz BCD hibát, de ha ez a szám 9-nél nagyobb, akkor már nem annyit fog jelenteni mint amennyit a kezelő lát a kijelzőn.

Néhány példa:
OP-n beírt számA szám BCD-benIdőzítés
16356636.63 mp
314--------
24379859.85 mp
20140.14 mp


Kapcsolódó témák:
WinCC Flexible tippek: Időtag idejének megváltoztatása HMI-ről
Üzemállapot visszajelzés, paraméterezés megvalósítása
Példák OP7-re: Időtag értékének kijelzése és módosítása



Szirty