Volatile for - teáskannák - pic24


Más emberek forrásainak feldolgozásával gyakran találkozom a programozó hibáival, amelyek az illékony minősítő céljának félreértéséből erednek. Ennek a félreértésnek az eredménye a kód, amely ritka, teljesen kiszámíthatatlan és gyakran nagyon destruktív és visszafordíthatatlan kudarcokat ad. Ez különösen igaz a mikrokontroller rendszerekre, ahol először is a megszakítási kezelők az alkalmazási kód részét képezik, másodsorban a perifériás vezérlõregiszterek általános célú RAM-ra vannak leképezve. Az illékony minősítők nem használatos (vagy helytelen használatával) kapcsolatos hibákat nehéz kiszámíthatatlanságuk és megismételhetetlenségük miatt nehéz megoldani. Előfordul, hogy a C-kód, amely nem biztosítja az illékony felhasználást, ahol használni kell, jól működik, egy fordító összeállít, és meghibásodik (vagy egyáltalán nem működik), amikor másra megy.

A legtöbb példa ebben a cikkben írt a GCC fordító (MPLAB C30), mert, mivel az építészet, a mag PIC24 fordító és funkciók a legegyszerűbb szintetizálni kis illusztrációk érte, ami lesz látható helytelen kezelése ingadozó. Sok ilyen példa tökéletesen összeáll más (egyszerűbb) fordítókkal, például PICC vagy MicroC. De ez nem jelenti azt, hogy amikor ezekkel a fordítókkal dolgozik, az illékony hibák egyáltalán nem jelennek meg. Csak ezeknek a fordítóknak a bemutató kódja sokkal nagyobbnak és bonyolultabbnak tűnne.

(az ingadozó angolul "instabil", "illékony")

Így illékony van C - ez egy változó selejtezőn, mondván, a fordító, hogy a változó értékét meg lehet változtatni bármikor, és része a kód, amelynél a változó át néhány fellépés (olvasási vagy írási) optimalizálni kell.

Az algoritmus szempontjából az a változó két alacsony rendű bitet állít be. Az optimalizáló az ilyen kódot egy szolgáltató helyettesítheti:

így nyert néhány ciklust és egy pár ROM-sejtet. De képzeljük el, hogy ugyanazokat a lépéseket csináljuk nem egy absztrakt változónál, hanem egy perifériális nyilvántartáson:

Ebben az esetben a "PORTB | = 3" helyettesítővel történő optimalizálás nem tud gondoskodni rólunk! A vezérlő terminálállapotainak közvetlen vezérlése gyakran fontos változásokkal jár. Például SPI jeleket generálunk, és egy tű (PORTB.0) az adatok, a másik (PORTB.1) a szinkronimpulzusok. Ebben az esetben nem tudjuk megváltoztatni a következtetések állapotát egy időben, mert Nem biztosítható, hogy az óra vezérelt chipje megkapja a helyes adatokat. Ráadásul nem szeretnénk optimalizálni a szinkronimpulzust generáló kódot egy ciklusidővel:

Ezt a kódot a fordító két kölcsönhatásként érzékelte, és az első sor nem tudott bejutni a kapott objektumkódba. A gyakorlatban azonban látjuk, hogy az ilyen optimalizálást nem végezzük. Ez azért van így, mert az a változó, amelyre a PORTB regiszter kerül leképezésre, az illékony minősítéssel kerül deklarálásra. például:

(ezt láthatja, ha megnézed a fordítóhoz mellékelt vezérlő fejlécfájlját). Az illékony minősítés megakadályozza a PORTB regiszterben végrehajtott műveletek végrehajtására szolgáló kód optimalizálását. Ezért még a kölcsönös cselekvések is ép optimizáló marad, és biztosak lehetünk benne, hogy a kimenet impulzus.

Tehát az optimalizálással a fordítók segítenek abban, hogy a kódot a lehető leggyorsabban és kompakt formában készítsük el. Ha azonban nem használsz illékony anyagot. egyes esetekben az optimalizálás rosszindulatú tréfát játszhat a programozóval. És azt kell mondanom, hogy az okosabb és erősebb a fordító, annál több problémát okozhat az analfabéta illékony felhasználásával.

Az illékony minősítéssel kapcsolatos három hiba létezik.

szükség esetén az illékony anyagok nem használhatók

Általában azok a programozók végzik el, akik nem ismerik az illékonyok létezését. vagy látni, de nem értik, mi ez;

szükség esetén használjon illékony anyagokat, de nem szükséges

olyan programozók számára rejlik, akik tudják, hogy mennyire fontos a változékony párhuzamos folyamatok programozása vagy a perifériális nyilvántartásokhoz való hozzáférés, de nem veszik figyelembe néhány árnyalatát;

használja az illékony, ha nem kell (és néha ez)

ilyenek azok, akik egyszer az első két hibára égettek. Ez nem hiba, és nem vezet a program rossz viselkedéséhez, de saját bajokat hoz létre.

Vegyünk egy példát a PIC24 számára:

Ha az optimalizálás le van tiltva, akkor ez a kód fog működni. De engedélyeznie kell az optimalizálást, mivel a program a váró () függvényben lógni kezd. Mi folyik itt? A fordító, amely a wait () függvényt sugározza, nem tudja, hogy a számláló változó bármikor megváltozhat, ha megszakítás történik. Csak azt látja, hogy visszaállítjuk, majd azonnal hasonlítjuk össze az Időparaméterrel. Más szavakkal, a fordító feltételezi, hogy az Idő változó mindig nullához hasonlít, és a wait () függvény és a felhasznált optimalizálás felsorolása így fog kinézni:

(Megjegyzés: A C fordító csak egy bájtos argumentumot ad a w0 regiszteren keresztül)

Amit látunk a következő kódot: Counter változó készül nulla, majd az Idő funkció átvezetéssel nyilvántartásban W0 képest nulla, nem figyel a valódi érték a változó Coutner, amely rendszeresen növeli minden egyes alkalommal az időzítő megszakítás. Más szavakkal véget értünk egy örök ciklusban. Amint már említettük, az a lényeg, hogy a fordító nem feltételezi, hogy a függvényt valamilyen kód megszakítja, amely a függvényekben szereplő változókon végrehajtja a műveleteket. Itt az illékony selejtező jön a segítségünkre.

Most, amikor összeáll, a fordító kódot generál, amely minden alkalommal hozzáfér a számláló változóhoz:

Ha a program RTOS alatt fut, pl. a funkció végrehajtása megszakadhat egy bizonyos ponton, majd újra átkerül a helyére, ahol megszakadt. Nem számít, hogy a kooperatív ütemező az operációs rendszer (azaz a programozó maga dönti el, ahol a funkciók megszakad), vagy helyettesítéssel (itt a programozó nem old meg semmit, és annak függvényében lehet szakítani teljesen bármikor több prioritás). Fontos megjegyezni, hogy a fordító nem tudja, hogy a függvény közepén végrehajtható néhány idegen kód. Itt van a kódtöredék egy valós programból:

A program jól működött, amikor a HT-PICC18 fordító összeállította, de amikor átkerült a PIC24-re (MCC30 fordító), akkor nem működött; nem reagált a gombokra. A probléma az volt, hogy az optimalizáló MCC30 eltérően optimalizáló HT-PICC18, vegye figyelembe, hogy abban az időben a végrehajtás kapcsoló változtatható gomb már tárolt egyik általános célú regiszterek (W0) (a PIC18 csak egy akkumulátort, így ha dolgozik vele ez a viselkedés kevésbé valószínű):

Gyakran ezek a funkciók kis késedelmek létrehozására használatosak:

Bizonyos fordítások azonban használhatatlanul kódolják ezeket a funkciókat, és egyáltalán nem tartalmazzák a kapott objektumkódot. Ha ez a késés csökkentésére használják az arány szoftver i2c (vagy SPI) a fordító, például HT-PICC, akkor a program már nem működnek, vagy inkább, hogy működni fog olyan gyorsan, hogy a vezérlő chip nem a transzfer a AVR mag fordító WinAVR képes feldolgozni hogy a késleltetés funkció minden hívása törlődik.

Ennek elkerülése érdekében használnia kell az illékony selejtezőt.

Egy másik gyakori hiba a nem illékony mutató használata az illékony változókhoz. Hogy ez kárt okozhat nekünk? Vegyünk egy példát (egy valós programból), amelyben az I / O portregiszterre mutató mutatót használtunk. Az SPI program két azonos chipet irányított a mikrokontroller különböző tüskéihez csatlakoztatva, a port, amelyhez az aktív mikrocirkulát csatlakoztatták, az egérmutató segítségével választották ki:

A helyzet ugyanaz, mint az előző példában: egy fordító (MCC18) alatt a kód működött, és a másik alatt (MCC30) megállt. Ennek oka egy rosszul kijelölt mutató volt. Az SPI_Send () függvény szétszerelője így néz ki:

Figyeljünk arra, hogy a fordító "kivetette" az 1. bit ("* Port | = 2") telepítését, mivel feleslegesnek tartja, mert majdnem utána, ez a bit újraindul, azaz elvégezte az optimalizálást anélkül, hogy figyelembe vette volna a Port változó által mutatott regiszter tulajdonságait, és azért tett, mert a programozó nem magyarázta meg a fordítónak, hogy a regiszter nem egyszerű. A hiba kijavításához be kellett állítania a Port változót, mint mutatót az illékony változónak:

Most a fordító tudja, hogy a Port által mutatott változó fölé nem lehet optimalizálni. És az új lista tükrözi ezt:

Ellenkező esetben ugyanazokat a hibákat kapjuk, mint az előző példában.

többnyelvű változóhoz való hozzáférés;

olvassa el / módosítsa / írja az akkumulátort.

Vegyünk egy példát olyan változó elérésére, amely egynél több memóriahelyet foglal el (16 int32, int64, float, double és int16 a 8 bites vezérlőkhöz). Gyakran adtam ezt a példát, újra megadom (a HT-PICC18 esetében):

Vegye figyelembe, hogy az ADCValue változó 2 byte méretű (az ADC 10-bites eredményének tárolásához). Mi a veszélye ennek a kódnak? Tekintsük az összehasonlítások egyikét (mondjuk az első):

Tegyük fel, ADC bemeneti feszültség értéke olyan, hogy az eredmény a konverzió 255 (0x0FF). Végül az értéke ADCValue, illetve szintén = 0x0FF. Ezzel az értékkel 100 (0x064) értékű összehasonlító kód indul. Először is, hasonlítsa össze a felső bájt variábilis és konstans (0x00 0x00), majd - junior (0x64 és 0xFF). Az eredmény nyilvánvaló. Itt azonban baj van. Bár az eredmény a AD átalakítás és egyenlő 0xFF, azt számos tényező befolyásolja: a tápfeszültség stabilitását (vagy referencia feszültség) bemeneti stabilitását a mért feszültség szintjét közelsége a küszöbérték pelenkázó LSB, áthallás, a zaj, stb Ezért az AD konverzió eredményének. van egyfajta jitter egy vagy két egység az alsó sorrendben. Ie az ADC eredmény ugrik az 0xFF és a 0x100 érték között. És ha az összehasonlítás végrehajtása megszakad, akkor a következők fordulhatnak elő:

érték ADCValue = 0x0FF;

A nagyrendű byteok összehasonlítása: 0x00 és 0x00;

Egy ADIF megszakítás történt, amelyben az ADCValue változó értéke 0x100-ra frissült;

az alsó bájtokat összehasonlítjuk: 0x64 és már 0x00!

mert a program úgy véli, hogy 0x000-es összehasonlítás volt <0x064, то она вызывает функцию Alarm.

És az illékony selejtező nem ment. Itt csak a megszakítások tilalma megtakaríthatja az összehasonlítást.

Tehát talán az illékonyoknak egyáltalán nincs szükségük? A megszakításokat egyébként tiltják? Igen, a megszakítások le vannak tiltva, de ingatagok. mindez ugyanúgy szükséges. Miért? A C30 fordítónak szinte ugyanazt a kódot vegye figyelembe:

Itt változékony és hasznos! Ügyeljen arra, hogy a vonal előtt az örök körforgás az - hozzárendel egy értéket egy változó ADCValue Temp. És ugyanakkor meglátjuk a listát:

Amint látja, a hurok belsejében nincs hozzáférés az ADCValue változóhoz, és az összehasonlítást a W1 regiszterrel végezzük, ahol az ADCValue változót a ciklus előtt lemásoltuk. Ezért, függetlenül attól, hogy az ADCV tér változik, programunk ezt nem veszi észre. Annyira ingatag, hogy ebben az esetben szükség van, csak ne felejtsük el, hogy ez a minősítő nem garantálja az atomicitás műveleteket a deklarált változón.

Mindkét rekord ugyanaz, de nem teszi meg a p változó változót. Mindkét bejegyzés azt jelenti, hogy "a változó p a mutató az illékony karakterekhez". Ennek a következményei meghatározás nyilvánvaló: ha az érték a mutató megszakítani vagy párhuzamos feladat, hogy a program nem veszi észre, mert fog működni a mutatóval a RON-on keresztül.

A helyes meghatározás így néz ki:

Ha az illékony mutatónak illékony változónak kell lennie, akkor ez így nyilvánul meg:

Itt nem tudok világos ajánlásokat adni. A legtöbb esetben a C programban történő programíráskor megpróbálom a következő szabályokat követni:

mind a megszakításokban, mind a programban (vagy különböző prioritások megszakításaiban) használt globális változókat illékonynak kell nyilvánítani;

A globális változók által feldolgozott két vagy több feladatot a munkahelyi alatt a multitasking operációs rendszer, meg kell állapítsa, mint illékony;

a perifériás nyilvántartásokra mutató mutatókat, valamint az illékonyaként deklarált változókat. kell mutatni, hogy a mutatók illékony;

minden, ami nem tartozik az első három kifejezést, és nem kapcsolódik a periféria, ajánlott, hogy írjon, ha eltekintünk a vas. Ekkor világossá válik, hogy a (i = 0; i<100; i++) <>; "nem hordoz semmilyen algoritmikus terhelést, és törölhető, és ha el kell hagyni őket, akkor a változókat illékonynak kell nyilvánítani.

minden más esetben feleslegessé válik az illékony.

Még néhány megjegyzés:

Kapcsolódó cikkek