Hogyan használhatjuk megfelelően a volatilis változókat az Arduino-ban?

Egy Arduino Uno-val készítettem egy kis projektet. Ez megszakításokat tartalmazott, mivel kódolókkal mérem, hogy a differenciálkerék-rendszer mennyit mozog előre. A robotom csak előre halad. Tehát minden kódolóból csak egyetlen csatornát használok. Itt van a két megszakítási rutinom:

ISR (INT0_vect){ encoderRPos = encoderRPos + 1; } ISR (INT1_vect){ encoderLPos = encoderLPos + 1; } 

A encoderRPos és encoderLPos volatile int típusúak. Megértem, hogy azoknak a változóknak, amelyek minden megszakítási rutinban változnak, típusúnak kell lenniük. Ez arra figyelmeztet, hogy a kód más részei, amelyek ezeket a változókat használják, bármikor megváltozhatnak.

De ami a kódomban történt, kissé furcsa volt, és nem tudtam elmagyarázni. Így számolom a bal kerék által mozgatott távolságot:

 #define distancePerCount 0.056196868 float SR = distancePerCount * (encoderRPos - encoderRPosPrev); float SL = distancePerCount * (encoderLPos - encoderLPosPrev); encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos; 

De amikor a következőket nyomtatom soros monitoromra, rendellenességet észlelek:

írja ide a kép leírását

Ha megnézi a harmadik oszlop, (SL) értéke egy ideig túl magas. Ez felborítja az összes számításomat.

Az egyetlen nyom, amelyet megszerezhetek, ha a kapott SL értékét veszem ( 3682), amely mindig konstans, és számoljon vissza (encodeLPos - encoderLPosPrev), 65519,66-ot kapok, ami közel van a unsigned int maximális értékéhez Ami azt jelenti, hogy (encoderLPos - encoderLPosPrev) túlcsordulást okoz, miközben mindkettő értéke, amelynek különbségét vesszük, csak valahol 5000 körül van!

És sikerült megoldanom. szerencse. Így módosítottam a kódot:

 static int encoderRPosPrev = 0; static int encoderLPosPrev = 0; int diffL = (encoderLPos - encoderLPosPrev); int diffR = (encoderRPos - encoderRPosPrev); float SR = distancePerCount * diffR; float SL = distancePerCount * diffL; encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos; 

Nem tudom felfogni a történteket. Van valami az illékony változókról, amiről tudnom kellett volna?

Frissítés: Itt a teljes kód található, ha valaha is meg akarja nézni. És nagyon jól működik, miután megváltoztatta az elfogadott válaszban javasoltakat.

Megjegyzések

  • A kérdésed azt mondja, amit a harmadik oszlop a kimenet … mi a többi oszlop? Kérjük, szerkessze a kérdést, és adjon hozzá oszlopfejléceket
  • @ jwpat7 Szándékosan távolítottam el őket, mert ez csak megzavarja az olvasót. De a kérdésre Majenko már jól válaszolt.
  • Nehéz részletes válaszokat adni a kivonatokból. could you explain why it is not happening randomly but at a specific time every time I run the code? Also why does it give the particular value? – Valószínűleg megtehetném, ha látnám a teljes kódot. Időközben olvassa el ezt: gammon.com.au/megszakítja
  • @NickGammon Tessék: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 tehát úgy tűnik, hogy rossz pillanatban növekedett, igen? Olyan változóhoz fér hozzá, amelyet egy megszakítás során megváltoztathat többször is ebben a kódban, így sokkal biztonságosabb lenne a helyi másolat megszerzése, miközben a megszakítások ki vannak kapcsolva.

Válasz

Meg kell ismernie a kritikus szakaszokat .

valószínűleg az történik, hogy a változókat a megszakítási rutinok a számítások közepén megváltoztatják. A “javítás” csökkenti a volatilis változókkal végzett számítással töltött időt, így kevésbé valószínű az ütközés.

Amit meg kell tennie, az az illékony változók átmásolása helyi változókba, amelyeknél a megszakítás megszakadt. rövid periódus.

cli(); int l = encoderLpos; int r = encoderRpos; sei(); 

Mivel az Arduino egy 8 bites CPU, ezért több szerelési utasításra van szükség matematikai műveletek végrehajtásához 16 bites értékeken. A lebegőpont még ennél is rosszabb, ha sok kiegészítést egyszerű utasításként használ. Az osztás és a szorzás lényegesen többet használ. A megszakításnak rengeteg lehetősége van arra, hogy az utasításlista során lőjön. Ilyen hozzárendelés elvégzésével, majd az új helyi változók felhasználásával a számításokban az illékony változók kezeléséhez szükséges utasításokat a lehető legkisebbre kell csökkenteni. A megszakítások kikapcsolásával a hozzárendelés során garantálja, hogy a változókat soha nem lehet megváltoztatni, miközben használja őket. Ezt a kódrészletet kritikus szakasz nak hívják.

Megjegyzések

  • Ez lehet, hogy csak ez a helyzet. Csak kíváncsi lennék, elmagyaráznád, miért nem véletlenszerűen, hanem egy adott időpontban történik minden alkalommal, amikor futtatom a kódot? Miért is ad az adott érték?
  • Itt található egy nagyszerű hivatkozás a cli / sei-re. nongnu.org/avr-libc/user-manual/… . A memóriakeretnél a fenti kódban valójában nincs szükség volatilis deklarációra. Íme néhány szórakoztató olvasnivaló erről a témáról. kernel .org / doc / Documentation / volatile-tekinthető-káros.txt
  • @MikaelPatel Nice, de nem az MCU-k szempontjából releváns.Ebben a helyzetben szükség van volatilisra, hogy megakadályozzuk a fordítót abban, hogy optimalizálja azokat az eseteket, amikor úgy gondolja, hogy ‘ nem használják (az érték soha nem változik). A cli / sei azért van, hogy az atom WRT műveletet az egyetlen másik szálként (megszakításként) végrehajtsa.
  • Megpróbálta fordítani a kódot volatilissal és anélkül? De a kritikus szekcióval (cli / sei). Amit megpróbálok megvitatni, az a memóriakorlát fogalma és az, hogy ez miként biztosítja a változók hozzáférhetőségét (és helyes sorrendjét) a fordítótól azzal, hogy változókat volatilisnak kell nyilvánítanunk. A legtöbb programozónak azt tanítják, hogy az ISR-ben elérhető minden változót ingatagnak kell nyilvánítani, de ennél sokkal többről van szó.
  • Nem hiszem, hogy a fordító sok fogalma van arról, hogy mit csinál a cli () és a sei (), és hogy ez hogyan befolyásolhatja például az olyan változók optimalizálását, amelyeket nem szabad ‘ optimalizálni. A sei () és a cli () műveletek csak a globális megszakítást engedélyező jelzőt kezelik a regiszterében. Semmit sem tesznek a kódfolyamatért.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük