Hur man använder flyktiga variabler på rätt sätt i Arduino?

Jag gjorde ett litet projekt med en Arduino Uno. Det innebar avbrott när jag använder kodare för att mäta hur mycket differentialhjulsystemet rör sig framåt. Min robot rör sig bara framåt. Så jag använder bara en enda kanal från varje kodare. Här är mina två avbrottsrutiner:

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

Variablerna encoderRPos och encoderLPos är av typen volatile int. Jag förstår att variablerna som genomgår förändringar i alla avbrottsrutiner måste vara av typen flyktiga. Detta för att varna andra delar av koden som använder dessa variabler om att den kan komma att ändras när som helst.

Men det som hände i min kod var lite konstigt och jag kunde inte förklara det. Så här beräknar jag det avstånd som flyttas av det vänstra hjulet:

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

Men när jag skriver ut följande på min seriella bildskärm, märker jag en avvikelse:

ange bildbeskrivning här

Om du tittar på den tredje kolumn, (SL) är värdet för högt bara under en längre tid. Detta stör alla mina beräkningar.

Den enda ledtråd jag kan få om jag tar värdet av SL som jag fick ( 3682), som alltid är konstant, och räknar tillbaka (encodeLPos - encoderLPosPrev), jag får 65519,66, vilket är nära maxvärdet för unsigned int Vilket betyder att (encoderLPos - encoderLPosPrev) orsakar ett överflöde medan båda värden vars skillnad tas är bara någonstans runt 5000!

Och jag lyckades lösa det. Det var genom tur. Så här ändrade jag koden:

 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; 

Jag kan inte förstå vad som har hänt. Finns det något med flyktiga variabler som jag borde ha känt till?

Uppdatering: Här är hela koden om du någonsin vill ta en titt. Och det fungerar mycket bra efter att ha ändrat det till vad som föreslogs i det accepterade svaret.

Kommentarer

  • Din fråga säger vad den tredje kolumnen i output är … vilka är de andra kolumnerna? Redigera frågan och lägg till kolumnrubriker
  • @ jwpat7 Jag har medvetet tagit bort dem eftersom det bara kommer att förvirra läsaren. Men frågan har redan besvarats bra av Majenko.
  • Det är svårt att ge detaljerade svar från dina utdrag. 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? – Jag skulle nog kunna göra det om jag såg hela koden. Under tiden läs detta: gammon.com.au/interrupts
  • @NickGammon Så här: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 så det ser ut som att det ökas i fel ögonblick, ja? Du använder en variabel som kan ändras i ett avbrott flera gånger i den koden, så att få en lokal kopia, medan avbrott är avstängd, skulle vara mycket säkrare.

Svar

Du måste lära dig mer om kritiska avsnitt .

Vad troligen händer är att variablerna ändras av avbrottsrutinerna halvvägs genom beräkningarna. Din ”fix” minskar tiden som används för att göra beräkningen med de flyktiga variablerna, vilket gör det mindre troligt att det är en kollision.

Vad du bör göra är att kopiera de flyktiga variablerna till lokala variabler med avbrott avaktiverade för det kort period.

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

Eftersom Arduino är en 8-bitars CPU krävs flera monteringsinstruktioner för att utföra matematiska operationer på 16 bitars värden. Flytpunkten är ännu värre med många instruktioner för ett enkelt tillägg. Uppdelning och multiplikation använder betydligt mer. En avbrott har gott om möjligheter att skjuta under listan med instruktioner. Genom att göra en sådan uppgift och sedan använda de nya lokala variablerna i dina beräkningar hålls instruktionerna som behövs för att hantera de flyktiga variablerna till ett absolut minimum. Genom att stänga av avbrott under uppdraget garanterar du att variablerna aldrig kan ändras medan du använder dem. Detta kodavsnitt kallas ett kritiskt avsnitt .

Kommentarer

  • Detta kan bara vara fallet. Undrar du bara, kan du förklara varför det inte sker slumpmässigt utan vid en viss tidpunkt varje gång jag kör koden? Varför ger det också det specifika värdet?
  • Här är en stor hänvisning till cli / sei. nongnu.org/avr-libc/user-manual/… . Med minnesbarriären behövs inte riktigt flyktig deklaration i koden ovan. Här är lite rolig läsning om detta ämne. kärna .org / doc / Documentation / volatile-anses-skadlig.txt
  • @MikaelPatel Trevligt, men inte så relevant för MCU.Flyktiga krävs i denna situation för att förhindra att kompilatorn optimerar fall där den tror att den ’ inte används (värdet ändras aldrig). Cli / sei är där för att göra operationen atomisk WRT till den enda andra tråd (avbrott) som körs.
  • Försökte du kompilera koden med och utan flyktig? Men med det kritiska avsnittet (cli / sei). Vad jag försöker diskutera är begreppet minnesbarriär och hur det ger flyktig åtkomst (och korrekt ordning) från kompilatorn med att deklarera variabler som flyktiga. De flesta programmerare lär sig att alla variabler som nås i en ISR måste förklaras flyktiga men det finns så mycket mer i den här berättelsen.
  • Jag tror inte ’ tänker inte kompilatorn har mycket koncept på vad cli () och sei () gör och hur det skulle påverka saker som att optimera ut av variabler som inte ’ inte ska optimeras ut. Allt sei () och cli () gör är att manipulera den globala avbrottsaktiverade flaggan i sitt register. De gör ingenting för flödet av kod.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *