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:
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
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.
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ítja3683 / .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.