Spørgsmål:
forsinkelse); vs if (millis () - forrige> tid); og drift
ATE-ENGE
2017-12-04 22:38:17 UTC
view on stackexchange narkive permalink

Når jeg gennemgik et gammelt projekt, havde jeg kode på to Arduino Due der så sådan ud

  void loop () {foo (); forsinkelse (tid);}  

tager hjertet hovedparten af litteratur om brug af delay (); Jeg kodede dette som

  void loop () {statisk usigneret lang PrevTime; hvis (millis () - PrevTime>time) {foo (); PrevTime = millis (); }}  

Dette ser imidlertid ud til at have skabt en situation, hvor de to enheder driver over en periode, hvor de ikke tidligere

Mit spørgsmål er dobbelt:

  1. Hvorfor ville hvis (millis () - PrevTime>time) forårsage mere drift end forsinkelse(tid)?
  2. Er der en måde at forhindre denne drift uden at gå tilbage til forsink(tid)?
Hvad er størrelsesordenen for "tidsperiode", hvor du bemærker afdriften? Er de to enheder på samme sted og dermed ved samme temperatur? Køres de på en krystaloscillator eller en keramisk resonator?
Personligt foretrækker jeg Majenkos løsning og bruger den altid (jeg sætter stigningen foran de andre instruktioner, men dette er bare en præference). Bemærk dog, at denne timing er PRÆCIS 100 ms, mens den anden kode (`foo; forsinkelse;`) har en periode, der er længere end 100 ms (det er 100 ms + tid for `foo`). Så du vil opleve drift (men det er den 'forsinkelse' -implementerede SW, der driver). Under alle omstændigheder skal du huske på, at selv perfekt lige implementeringer "driver", fordi ure ikke er lige; hvis du har brug for komplet synkronisering, skal du bruge et signal til at synkronisere de to programmer.
De to enheder er ved siden af ​​hinanden, efter at have kørt fra fredag ​​kl. 17:00 til mandag kl. 9:00 var der en drift på 4 minutter. Jeg beslutter, at jeg vil bruge en digital pin til at synkronisere indgangene pr. dit forslag
"De to enheder er ved siden af ​​hinanden, ..." det betyder ikke, at timingmekanismen ikke er nøjagtig, du taler om en fejlrate på ~ 800 ppm, høj for to krystaloscillatorer, men rimelig for endda en keramisk resonator. du er nødt til at sammenligne det med en rimelig nøjagtig timingstandard for at være sikker: krystaller er typisk inden for 20 ppm, og tcxo's kan gøre sub 1 ppm. det ville være min måde at gøre det på.
Fire svar:
Majenko
2017-12-04 23:01:05 UTC
view on stackexchange narkive permalink

Der er en vigtig ting, du skal huske, når du arbejder med tiden på en Arudino af enhver form:

  • Hver operation tager tid.

Din foo () -funktionen tager lang tid. Hvad den tid er, kan vi ikke sige.

Den mest pålidelige måde at håndtere tid på er at kun stole på tiden til udløsning, ikke til at træne, når den næste udløsende skal være.

Tag f.eks. følgende:

  hvis (millis () - sidste > interval) {doSomething (); last = millis ();}  

Variablen sidste er det tidspunkt, som rutinen udløste * plus tiden doSomething tog at køre . Så sig interval er 100, og doSomething tager 10 ms at køre, du får udløsninger på 101ms, 212ms, 323ms osv. Ikke de 100ms, du havde forventet.

Så en ting du kan gøre er at altid bruge den samme tid uanset ved at huske den på et bestemt tidspunkt (som Juraj foreslår):

  uint32_t time = millis (); hvis ( tid - sidste > interval) {doSomething (); sidste = tid;}  

Den tid, som doSomething () tager, har ingen indflydelse på noget. Så du får udløsninger ved 101ms, 202ms, 303ms osv. Stadig ikke helt de 100ms, du ønskede - fordi du leder efter mere som 100ms er gået - og det betyder 101ms eller mere. I stedet skal du bruge >=:

  uint32_t time = millis (); hvis (tid - sidste > = interval) {doSomething (); sidste = tid;}  

Nu, forudsat at der ikke sker noget andet i din løkke, får du udløsninger på 100ms, 200ms, 300ms osv. Men bemærk den bit: "så længe intet andet sker i din sløjfe " ...

Hvad sker der, hvis en handling, der tager 5 ms, sker ved 99ms ...? Din næste udløsning bliver forsinket indtil 104 ms. Det er en drift. Men det er let at bekæmpe. I stedet for at sige "Den indspillede tid er nu" siger du "Den indspillede tid er 100 ms senere, end den var". Det betyder, at uanset hvilke forsinkelser du får i din kode, vil din udløsende altid være med 100 ms intervaller eller glide inden for et 100ms kryds.

  if (millis () - sidste > = interval) { gør noget(); sidste + = interval;}  

Nu får du udløsninger ved 100ms, 200ms, 300ms osv. Eller hvis der er forsinkelser i andre kodebit, kan du få 100ms, 204ms, 300ms, 408ms, 503ms, 600ms osv. Det forsøger altid at køre det så tæt på intervallet som muligt uanset forsinkelser. Og hvis du har forsinkelser, der er større end intervallet, kører det automatisk din rutine nok gange til at indhente det aktuelle tidspunkt.

Før du havde drift . Nu har du jitter .

Juraj
2017-12-04 22:44:44 UTC
view on stackexchange narkive permalink

Fordi du nulstiller timeren efter operationen.

  statisk usigneret lang PrevTime = millis (); usigneret lang t = millis (); hvis (t - PrevTime > tid) {foo ( ); PrevTime = t;}  
ingen. bemærk, at PrevTime er statisk.
AilijiaeydCMT ja, og?
hvis du vidste, hvad "statisk" betyder, ville du vide, hvorfor dit svar ikke er korrekt.
Jeg ved, hvad statisk gør med lokal variabel .. Jeg forstår ikke, hvorfor du tror, ​​at mit svar har noget at gøre med det statiske. Jeg har lige flyttet den aktuelle millisaflæsning, før foo () påberåbes.
ThatAintWorking
2017-12-07 23:40:17 UTC
view on stackexchange narkive permalink

For hvad du prøver at gøre, er forsinkelse () den rigtige måde at implementere koden på. Årsagen til, at du vil bruge if (millis ()), er, hvis du vil tillade, at hovedsløjfen fortsætter med at løkke, så din kode eller en anden kode uden for den sløjfe kan udføre anden behandling.

For eksempel:

  lang next_trigger_time = 0L; langt interval = DESIRED_INTERVAL; ugyldig loop () {do_something (); // kontrollere en sensor eller værdi, der er indstillet med en interrupt lang m = millis (); hvis (m > = næste_trigger_tid) {næste_trigger_tid = m + interval; foo (); }}  

Dette kører foo () i det angivne interval, mens sløjfen fortsætter med at løbe imellem. Jeg lagde beregningen af ​​næste_udløser_tid før opkaldet til foo () for at hjælpe med at minimere drift, men det er uundgåeligt. Hvis drift er en væsentlig bekymring, skal du bruge en afbrydertimer eller en slags ur / timersynkronisering. Husk også, at millis () vil vikle sig rundt efter et stykke tid, og jeg har ikke taget højde for det for at holde kodeeksemplet enkelt.

Hader at nævne dette: rollover-problem om 52 dage.
Jeg nævnte allerede rollover-spørgsmålet i slutningen af ​​mit svar.
Løs det.
Mit standard konsulentgebyr $ 100 / time, hvis du vil have mig til at skrive kode til dig. Jeg synes, hvad jeg har skrevet er tilstrækkeligt relevant.
Er du opmærksom på, at Majenko sendte et mere komplet og bedre svar på dit? Er du klar over, at din kode ikke kompileres? At `lang m - millis ()` ikke gør, hvad du har til hensigt at gøre? Det er på huset.
Stem derefter på hans svar, hvis du kan lide det bedre.
Jeg kan ikke se meningen med at sende en mangelfuld løsning, når en god allerede er blevet accepteret. Majenkos løsning lider ikke under rollover-problemet, og det viser, at drift ikke er "uundgåelig", men snarere meget let at undgå. Dette svar giver ingen merværdi.
@EdgarBonet, Majenkos løsning diskuterer ikke, hvorfor du måske vælger ikke at bruge forsinkelse (), selvom det forårsager drift. Mit kodeeksempel var beregnet til at fremhæve det og ikke give en "komplet" løsning. Så mit svar tilføjer værdi til diskussionen, men jeg antager, at du heller ikke faktisk læste mit svar.
dannyf
2017-12-04 22:47:53 UTC
view on stackexchange narkive permalink

din kode er korrekt.

Det problem, du løber ind i, er med millis (): det tæller lidt under (det maksimale undertælling er bare genert på 1 ms pr. påkaldelse).

løsningen er med finere flåter, som micros () - men det tæller også lidt.

Giv venligst noget bevis eller en eller anden reference til “_Det vil under-tælle lidt_”.


Denne spørgsmål og svar blev automatisk oversat fra det engelske sprog.Det originale indhold er tilgængeligt på stackexchange, som vi takker for den cc by-sa 3.0-licens, den distribueres under.
Loading...