Hvordan bruker jeg flyktige variabler riktig i Arduino?

Jeg gjorde et lite prosjekt med en Arduino Uno. Det involverte avbrudd mens jeg bruker kodere for å måle hvor mye differensialhjulsystemet beveger seg fremover. Roboten min beveger seg bare fremover. Så jeg bruker bare en enkelt kanal fra hver koder. Her er mine to avbruddsrutiner:

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

Variablene encoderRPos og encoderLPos er av typen volatile int. Jeg forstår at variablene som gjennomgår endring i enhver avbruddsrutine må være av typen flyktige. Dette for å advare andre deler av koden som bruker disse variablene om at den kan endres når som helst.

Men det som skjedde i koden min var litt rart, og jeg kunne ikke forklare det. Slik beregner jeg avstanden som er flyttet av venstre hjul:

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

Men når jeg skriver ut følgende på min serielle skjerm, merker jeg en avvik:

skriv inn bildebeskrivelse her

Hvis du ser på den tredje kolonne, (SL), er verdien for høy bare for en stund. Dette forstyrrer alle beregningene mine.

Den eneste aningen jeg kan få, hvis jeg tar verdien av SL som jeg fikk ( 3682), som alltid er konstant, og beregner tilbake (encodeLPos - encoderLPosPrev), får jeg 65519,66, som er nær maksverdien på unsigned int . Hvilket betyr at (encoderLPos - encoderLPosPrev) forårsaker et overløp, mens begge verdiene hvis forskjell er tatt er bare et sted rundt 5000!

Og jeg klarte å løse det. Det var ved lykke. Slik endret jeg 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; 

Jeg kan ikke forstå hva som har skjedd. Er det noe med flyktige variabler som jeg burde ha kjent til?

Oppdatering: Her er hele koden hvis du noen gang vil ta en titt. Og det fungerer veldig bra etter å ha endret det til det som ble foreslått i det aksepterte svaret.

Kommentarer

  • Spørsmålet ditt sier hva den tredje kolonnen i output er … hva er de andre kolonnene? Rediger spørsmålet og legg til kolonneoverskrifter
  • @ jwpat7 Jeg har forsettlig fjernet dem fordi det bare vil forvirre leseren. Men spørsmålet har allerede blitt besvart bra av Majenko.
  • Det er vanskelig å gi detaljerte svar fra kodebitene dine. 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? – Jeg kunne nok gjort det hvis jeg så hele koden. Les i mellomtiden dette: gammon.com.au/interrupts
  • @NickGammon Her kommer du: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 så det ser ut til at den steg opp i feil øyeblikk, ja? Du får tilgang til en variabel som kan endres i et avbrudd flere ganger i den koden, så å få en lokal kopi, mens avbrudd er av, ville være mye tryggere.

Svar

Du må lære om kritiske seksjoner .

Hva som sannsynligvis skjer, er at variablene endres av avbruddsrutinene midt gjennom beregningene. «Fix» reduserer tiden du bruker på å beregne med de flyktige variablene, og gjør det mindre sannsynlig at det er en kollisjon.

Det du bør gjøre er å kopiere de flyktige variablene til lokale variabler med avbrudd deaktivert for det kort periode.

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

Fordi Arduino er en 8-biters CPU, krever det flere monteringsinstruksjoner for å utføre matematiske operasjoner på 16-biters verdier. Flytende punkt er enda verre ved å bruke mange mange instruksjoner for et enkelt tillegg. Deling og multiplikasjon bruker betydelig mer. En avbrytelse har mange muligheter til å skyte under den listen over instruksjoner. Ved å utføre en slik oppgave og deretter bruke de nye lokale variablene i beregningene, blir instruksjonene som trengs for å håndtere de flyktige variablene, holdt på et absolutt minimum. Ved å slå av avbrudd under oppgaven garanterer du at variablene aldri kan endres mens du bruker dem. Denne kodebiten kalles en kritisk seksjon .

Kommentarer

  • Dette kan bare være tilfelle. Bare lurer på, kan du forklare hvorfor det ikke skjer tilfeldig, men på et bestemt tidspunkt hver gang jeg kjører koden? Også hvorfor gir det den spesielle verdien?
  • Her er en flott referanse til cli / sei. nongnu.org/avr-libc/user-manual/… . Med hukommelsesbarrieren er det egentlig ikke behov for ustabil erklæring i koden ovenfor. Her er litt morsom lesing om dette emnet. .org / doc / Documentation / flyktig-ansett-skadelig.txt
  • @MikaelPatel Hyggelig, men ikke så relevant for MCU-er.Flyktig er nødvendig i denne situasjonen for å forhindre at kompilatoren optimaliserer forekomster der den mener at den ‘ ikke blir brukt (verdien endres aldri). Cli / sei er der for å gjøre operasjonen atomic WRT til den eneste andre tråden (avbryter) som utfører.
  • Prøvte du å kompilere koden med og uten flyktig? Men med den kritiske delen (cli / sei). Det jeg prøver å diskutere er konseptet med minnebarrieren og hvordan det gir flyktig tilgang (og riktig rekkefølge) fra kompilatoren med å måtte erklære variabler som flyktige. De fleste programmerere lærer at enhver variabel som er tilgjengelig i en ISR, må erklæres ustabil, men det er så mye mer i denne historien.
  • Jeg tror ikke ‘ Jeg tror ikke kompilatoren har mye begrep om hva cli () og sei () gjør, og hvordan det vil påvirke ting som å optimalisere ut av variabler som ikke ‘ ikke skal optimaliseres. Alt sei () og cli () gjør er å manipulere det globale avbruddsaktiverte flagget i registeret. De gjør ingenting for strømmen av kode.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *