Hoe vluchtige variabelen correct te gebruiken in Arduino?

Ik deed een klein project met een Arduino Uno. Het ging om onderbrekingen omdat ik encoders gebruik om te meten hoeveel het differentieelwielsysteem naar voren beweegt. Mijn robot beweegt alleen vooruit. Dus ik gebruik slechts één kanaal van elke encoder. Dit zijn mijn twee interruptroutines:

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

De variabelen encoderRPos en encoderLPos zijn van het type volatile int. Ik begrijp dat de variabelen die in een interruptroutine veranderen, van het type vluchtig moeten zijn. Dit is om andere delen van de code die deze variabelen gebruiken te waarschuwen dat het op elk moment kan veranderen.

Maar wat er in mijn code gebeurde was een beetje vreemd en ik kon het niet uitleggen. Hier is hoe ik de afstand bereken die door het linkerwiel wordt bewogen:

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

Maar als ik het volgende afdruk op mijn seriële monitor, merk ik een anomalie op:

voer hier de beschrijving van de afbeelding in

Als je naar de derde kijkt kolom, (SL) de waarde is te hoog voor een tijdje. Dit verstoort al mijn berekeningen.

De enige aanwijzing die ik kan krijgen, als ik de waarde van SL neem die ik heb ( 3682), wat altijd een constante is, en (encodeLPos - encoderLPosPrev) terugberekent, krijg ik 65519.66, wat dicht bij de maximale waarde van unsigned int ligt Wat betekent dat (encoderLPos - encoderLPosPrev) een overflow veroorzaakt, terwijl beide waarden waarvan het verschil wordt genomen ergens rond de 5000 liggen!

En ik heb het opgelost. Het was door geluk. Dit is hoe ik de code heb aangepast:

 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; 

Ik kan “niet begrijpen wat er is gebeurd. Is er iets met vluchtige variabelen dat ik had moeten weten?

Update: Hier is de volledige code als u ooit een kijkje wilt nemen. En het werkt erg goed nadat het is gewijzigd in wat werd voorgesteld in het geaccepteerde antwoord.

Opmerkingen

  • Uw vraag zegt wat de derde kolom van output is … wat zijn de andere kolommen? Bewerk de vraag en voeg kolomkoppen toe
  • @ jwpat7 Ik heb ze met opzet verwijderd omdat dat de lezer alleen maar in verwarring zal brengen.Maar de vraag is al goed beantwoord door Majenko.
  • Het is moeilijk om gedetailleerde antwoorden te geven vanuit je fragmenten. 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? – Ik zou dat waarschijnlijk kunnen doen als ik de hele code zou zien. Lees ondertussen dit: gammon.com.au/interrupts
  • @NickGammon Alsjeblieft: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 dus het lijkt alsof het op het verkeerde moment is opgehoogd, ja? U gebruikt een variabele die tijdens een onderbreking kan worden gewijzigd meerdere keren in die code, dus het zou veel veiliger zijn om een lokale kopie te krijgen terwijl de interrupts zijn uitgeschakeld.

Antwoord

Je moet leren over kritische secties .

Wat waarschijnlijk gebeurt, is dat de variabelen halverwege de berekeningen worden gewijzigd door de interruptroutines. Uw “oplossing” vermindert de tijd die wordt besteed aan het uitvoeren van de berekening met de vluchtige variabelen, waardoor het minder waarschijnlijk wordt dat er een botsing plaatsvindt.

Wat u moet doen is de vluchtige variabelen kopiëren naar lokale variabelen met onderbrekingen daarvoor uitgeschakeld korte periode.

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

Omdat de Arduino een 8 bit CPU is, zijn er meerdere montage-instructies nodig om wiskundige bewerkingen uit te voeren op 16 bit waarden. Drijvende komma is nog erger als je veel instructies gebruikt voor een eenvoudige toevoeging. Delen en vermenigvuldigen gebruiken aanzienlijk meer. Een onderbreker heeft tijdens die lijst met instructies voldoende gelegenheid om te vuren. Door zon opdracht uit te voeren en vervolgens de nieuwe lokale variabelen in je berekeningen te gebruiken, worden de instructies die nodig zijn om met de vluchtige variabelen om te gaan tot een absoluut minimum beperkt. Door interrupts uit te schakelen tijdens de toewijzing, garandeer je dat de variabelen nooit kunnen worden gewijzigd terwijl je ze gebruikt. Dit codefragment wordt een kritieke sectie genoemd.

Opmerkingen

  • Dit zou zomaar het geval kunnen zijn. Je vraagt je af, kun je uitleggen waarom het niet willekeurig gebeurt, maar op een bepaald tijdstip elke keer dat ik de code uitvoer? En waarom geeft het de specifieke waarde?
  • Hier is een goede verwijzing naar de cli / sei. nongnu.org/avr-libc/user-manual/… . Met de geheugenbarrière is vluchtige declaratie niet echt nodig in de bovenstaande code. Hier is wat leuke lectuur over dit onderwerp. kernel .org / doc / Documentation / vluchtig-beschouwd-schadelijk.txt
  • @MikaelPatel Mooi, maar niet zo relevant voor MCUs.Vluchtig is in deze situatie vereist om te voorkomen dat de compiler instanties optimaliseert waarvan hij denkt dat deze ‘ niet wordt gebruikt (waarde verandert nooit). De cli / sei is er om de operatie atomic WRT de enige andere thread (interrupts) te maken die wordt uitgevoerd.
  • Heb je geprobeerd de code te compileren met en zonder vluchtig? Maar met de kritische sectie (cli / sei). Wat ik probeer te bespreken is het concept van de geheugenbarrière en hoe dat vluchtige toegang (en correcte volgorde) van de compiler biedt, waarbij variabelen als vluchtig moeten worden verklaard. De meeste programmeurs wordt geleerd dat elke variabele waartoe toegang wordt verkregen in een ISR vluchtig moet worden verklaard, maar er is zoveel meer in dit verhaal.
  • Ik denk niet dat ‘ de compiler heeft veel begrip van wat cli () en sei () doen en hoe dat zaken zou beïnvloeden als het optimaliseren van variabelen die niet ‘ zouden moeten worden geoptimaliseerd. Het enige dat sei () en cli () doen, is het manipuleren van de vlag met globale onderbreking in zijn register. Ze doen niets voor de codestroom.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *