Hvordan man korrekt bruger flygtige variabler i Arduino?

Jeg lavede et lille projekt med en Arduino Uno. Det involverede afbrydelser, da jeg bruger kodere til at måle, hvor meget differentialhjulsystemet bevæger sig fremad. Min robot bevæger sig kun fremad. Så jeg bruger kun en enkelt kanal fra hver indkoder. Her er mine to afbrydelsesrutiner:

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

Variablerne encoderRPos og encoderLPos er af typen volatile int. Jeg forstår, at de variabler, der gennemgår ændringer i enhver afbrydelsesrutine, skal være af typen flygtige. Dette er for at advare andre dele af koden, der bruger disse variabler, om at den kan ændre sig når som helst.

Men hvad der skete i min kode var lidt underligt, og jeg kunne ikke forklare det. Sådan beregner jeg afstanden, der flyttes med venstre hjul:

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

Men når jeg udskriver følgende på min serielle skærm, bemærker jeg en anomali:

indtast billedebeskrivelse her

Hvis du ser på den tredje (SL) er værdien for høj bare i nogen tid. Dette forstyrrer alle mine beregninger.

Den eneste anelse, jeg kan få, hvis jeg tager værdien af SL, som jeg fik ( 3682), som altid er konstant, og beregner tilbage (encodeLPos - encoderLPosPrev), får jeg 65519,66, hvilket er tæt på den maksimale værdi på unsigned int Hvilket betyder, at (encoderLPos - encoderLPosPrev) forårsager et overløb, mens begge værdier, hvis forskel er taget, kun er et sted omkring 5000!

Og det lykkedes mig at løse det. Det var ved held. Sådan ændrede 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å, hvad der er sket. Er der noget ved flygtige variabler, som jeg burde have kendt til?

Opdatering: Her er hele koden, hvis du nogensinde vil tage et kig. Og det fungerer meget godt efter at have ændret det til det, der blev foreslået i det accepterede svar.

Kommentarer

  • Dit spørgsmål siger, hvad den tredje kolonne i output er … hvad er de andre kolonner? Rediger spørgsmål og tilføj kolonneoverskrifter
  • @ jwpat7 Jeg har med vilje fjernet dem, fordi det kun vil forvirre læseren. Men spørgsmålet er allerede blevet besvaret godt af Majenko.
  • Det er svært at give detaljerede svar fra dine uddrag. 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? – Det kunne jeg sandsynligvis gøre, hvis jeg så hele koden. I mellemtiden skal du læse dette: gammon.com.au/interrupts
  • @NickGammon Here you go: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 så det ser ud til at det steges i det forkerte øjeblik, ja? Du får adgang til en variabel, der muligvis ændres i en afbrydelse flere gange i denne kode, så det ville være meget sikrere at få en lokal kopi, mens afbrydelser er slået fra.

Svar

Du skal lære om kritiske sektioner .

Hvad der sandsynligvis sker, er at variablerne ændres af afbrydelsesrutinerne midt i beregningerne. Din “rettelse” reducerer den tid, der bruges på beregningen med de flygtige variabler, hvilket gør det mindre sandsynligt, at der er en kollision.

Hvad du skal gøre er at kopiere de flygtige variabler til lokale variabler med afbrydelser deaktiveret for det kort periode.

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

Da Arduino er en 8-bit CPU, kræver det flere monteringsinstruktioner for at udføre matematiske operationer på 16 bit-værdier. Flydepunktet er endnu værre ved at bruge mange mange instruktioner til en simpel tilføjelse. Opdeling og multiplikation bruger betydeligt mere. En afbrydelse har masser af muligheder for at affyre under denne liste over instruktioner. Ved at udføre en sådan opgave og derefter bruge de nye lokale variabler i dine beregninger holdes de instruktioner, der er nødvendige for at håndtere de flygtige variabler, til et absolut minimum. Ved at deaktivere afbrydelser under opgaven garanterer du, at variablerne aldrig kan ændres, mens du bruger dem. Dette kodestykke kaldes et kritisk afsnit .

Kommentarer

  • Dette kan bare være tilfældet. Kun undrende, kan du forklare, hvorfor det ikke sker tilfældigt, men på et bestemt tidspunkt hver gang jeg kører koden? Også hvorfor giver det den særlige værdi?
  • Her er en god henvisning til cli / sei. nongnu.org/avr-libc/user-manual/… . Med hukommelsesbarrieren er der ikke rigtig behov for ustabil erklæring i koden ovenfor. Her er lidt sjov læsning om dette emne. kerne .org / doc / Dokumentation / flygtig-betragtes-skadelig.txt
  • @MikaelPatel Dejligt, men ikke så relevant for MCUer.Flygtig er påkrævet i denne situation for at forhindre, at kompilatoren optimerer tilfælde, hvor den mener, at den ‘ ikke bruges (værdien ændres aldrig). Cli / sei er der for at gøre operationen atomisk WRT til den eneste anden tråd (interrupts), der udføres.
  • Forsøgte du at kompilere koden med og uden flygtige? Men med det kritiske afsnit (cli / sei). Hvad jeg forsøger at diskutere er begrebet hukommelsesbarrieren, og hvordan det giver flygtig adgang (og korrekt rækkefølge) fra compileren med at skulle erklære variabler som flygtige. De fleste programmører lærer, at enhver variabel, der er adgang til i en ISR, skal erklæres ustabil, men der er så meget mere til denne historie.
  • Jeg tror ikke ‘ Jeg tror ikke kompilatoren har meget koncept for, hvad cli () og sei () gør, og hvordan det vil påvirke ting som optimering ud af variabler, der ikke ‘ ikke skal optimeres ud. Alt sei () og cli () gør er at manipulere det globale interrupt-aktiverede flag i sit register. De gør intet for strømmen af kode.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *