Nerräkningstimer kontra system-timer, tankar

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
Användarvisningsbild
Icecap
Inlägg: 26139
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Nerräkningstimer kontra system-timer, tankar

Inlägg av Icecap »

Jag hamnar ofta i situationer där en funktion ska utföras - men att utförandet tar en viss tid.

T.ex. ska strömmen slås på en extern enhet och den ska hinna starta upp. Tänk el-motor som sedan ska hinna varva upp.

Jag använder en 32 bitars µC på 50MHz och jag kan helt enkelt inte leva med att den står still under tiden den väntar på att den externa enheten "kommer igång". Jag förordar ju också kraftigt att alla Delay() i programmering är en styggelse som inte får hända.

Sedan satt jag och funderade lite på om inte det kunde löna sig med en system-timer i ett givet projekt, á la Arduino.

Fördelen är att man enkelt kan kolla tiden just nu och spara i en variabel med ett offset (Ska_Hända_När = Systemtid() + 20 sekunder).
I mainloop kan man sedan köra den vanliga:
if(Systemtid() >= Ska_Hända_När)
{
// Do whatever...
}

Men det finns ett problem med den lösningen: variabeln som utgör Systemtimer ska vara ett visst minimalt antal bitar för att få ett tidsförlopp som inte upprepar sig för ofta. Jag ämnar att använda en upplösning på 1ms, oavsett om min systemtimer kör med den hastighet eller inte varför en 32-bitars variabel "håller" lite drygt 49 dygn.

Under de 49 dygn fungerar det riktigt bra på detta sätt men sedan?

Ja, om man lägger till offset'en och kommer över vad variabeln klarar rullar den runt till noll + lite - men detta kommer då att utlösa en för tidig time-out. Ok, en gång per 49 dygn kan vara till att leva med, herregud, den rapade ju bara till...

Men det kan också vara en katastrof som kan hända pga. denna kända bugg/"feature" - och då har man planerat en katastrof.

Samtidig är en trög 8-bitar inte snabb på 32-bitars matte så det är ett problem också.

Det som är spiken i kistan för användandet av en systemtimer är att man inte! kan vara 100% säker på att main-loop kommer till avkänningen varenda timer-klick, hade man det kunde man ha använd:
if(Systemtid() == Ska_Hända_När)
{
}

Detta hade löst alla problem med systemtimern - men det fungerar ju inte!
================================================

Sedan finns den lösningen jag använder mig av: en nerräkningstimer.

Jag använder en timer-interrupt på "valfri" men fast frekvens. ISR'n som hör till räknar ner ett antal variabler om de är högre än noll, t.ex:
if(Delay_General) Delay_General--;
if(Delay_Key) Delay_Key--;

Såklart det antal jag behöver och är det två (eller fler) delay som inte kan köra samtidig kan jag använda samma variabel.

De är såklart deklarerat som volatile och har en storlek som medger att de kan hålla de tider jag kan behöva som mest.
Ett WORD (16 bit) kan hålla upp till 65535 ms - alltså 65,535 sekunder - vilket ofta räcker mycket långt.

För en 8-bit µC är en 16-bit variabel inte fullt så arbetstung som en 32-bit men viktigast av allt:
Det finns ingen buggar i detta sätt! Det fungerar likadan varje gång.

Sedan har jag en ovana att använda lite olika timer-tider, ibland 1ms, ibland 10ms. Under utvecklingen sker det att jag byter hastigheten.

Därför har jag en definition av "SYSTEM_CLOCK_SPEED" som kan vara 100 eller 1000 eller vad jag vill.

Jag gör en macro:
#define MILISEC(X) (((X) * SYSTEM_CLOCK_SPEED) / 1000)

När jag sedan anger tider är det enkelt:
Delay_General = MILISEC(250); // För 250 ms delay.

På detta sätt kan timerns hastighet ändras och en omkompilering av koden ger samma delay hela tiden utan att man ska pilla och ändra.
================================================

Det finns såklart möjlighet att använda hårdvaru-timer om man har hårdvara nog till detta, det är dock oftast på mer avancerade system detta kan finnas och det är inte där Arduino befinner sig.

Sedan kan hårdvara-timers vara svåra att få långsamma nog om man behöver längre tider.
thebolt
Inlägg: 248
Blev medlem: 10 februari 2008, 17:41:40
Ort: Taipei Taiwan

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av thebolt »

Rätt sätt att hantera det är att låta den wrappa, och använda modulär aritmetik för att göra jämförelsen.

Det innebär (om man kodar i C) att
- Använd "unsigned" typ så att overflow/underflow är definierat
- Beräkna och jämför intervall-längder, inte tidsstämplar

Dvs, för att det ska bli rätt, om du vill ha något som ska hända om 20 sekunder så gör

Kod: Markera allt

unsigned int start_tid = systemtid()
....
if (systemtid() - start_tid >= 20sekunder) {
...
}
Det hanterar korrekt att systemtid/starttid wrappar runt.
Användarvisningsbild
Jan Almqvist
Inlägg: 1580
Blev medlem: 1 oktober 2013, 20:48:26
Ort: Orust

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av Jan Almqvist »

Blir inte detta villkor falskt under 20 sekunder vart 49:e dygn?
Användarvisningsbild
Andax
Inlägg: 4373
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av Andax »

Nej. Säg att tiden är i s och start_tid är 0xffffffff, om t.ex. systemtid() wrappar och blir 0x00000000. då är differensen fortfarande 1. Det är det som är vitsen att köra med unsigned int att skillnaden mellan två tal blir den samma oavsett om du lägger på en offset på båda talen (om offseten är samma givetvis för båda).

edit: läste ditt inlägg igen och ser att man kan tolka det på fler sätt. Ja du har rätt att om vi sparar undan start_tid och kontrollerar med vår if-sats så kommer den att bli uppfylld felaktigt om klockan hunnit wrappa runt flera varv. Där måste man ju ha med en kontroll att vi redan har utfört funktionen en gång under första systemklockecykeln och inte skall utföra den fler gånger utan omstart av vår funktion.
Användarvisningsbild
Jan Almqvist
Inlägg: 1580
Blev medlem: 1 oktober 2013, 20:48:26
Ort: Orust

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av Jan Almqvist »

Visst har det varit en diskussion om detta tidigare?

Jag får inte ihop det, vi utgår från denna kod:

Kod: Markera allt

unsigned int start_tid = systemtid();
För enkelhets skull är både start_tid och systemtid() 0 och inte förrän efter 20s blir skillnaden >= 20.

Kod: Markera allt

if (systemtid() - start_tid >= 20sekunder) {
Men, efter 49 dygn så wrappar systemtid() och börjar om på 0 eller hur?

Då blir väl

Kod: Markera allt

systemtid() - start_tid 
mindre än 20 igen?
Användarvisningsbild
adent
Inlägg: 4100
Blev medlem: 27 november 2008, 22:56:23
Ort: Utanför Jönköping
Kontakt:

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av adent »

Det stämmer nog ja, men man får väl komplettera med en flagga om det är en engångshändelse, då klarar man sig. Är det ett intervall så puttar man ju fram det 20s hela tiden och då funkar det också.
thebolt
Inlägg: 248
Blev medlem: 10 februari 2008, 17:41:40
Ort: Taipei Taiwan

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av thebolt »

Ja, det är en begränsning.. du måste uppfylla två saker för att det ska funka:

1. Ingen väntetid/intervall kan vara längre än en wrap-around (om det är 32-bitars uint för millisekunder så är det 49 dygn)
2. Du måste kolla minst en gång per wrap-around tid, helst oftare.

I nästan alla tillämpningar är det inget problem.

För att förtydliga, start_tid är inte tänkt att vara _processorns_ starttid, utan tiden du börjar vänta på ditt event. Säg att du har en knapp som tänder en lampa och du 20 sekunder senare vill släcka den, då sätter du start_tid när du trycker knappen och kollar sen för att släcka den.

-M
Användarvisningsbild
lillahuset
Gått bort
Inlägg: 13969
Blev medlem: 3 juli 2008, 08:13:14
Ort: Norrköping

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av lillahuset »

Då kommer jag dragande med min gamla trotjänare igen. :D

Kod: Markera allt

/**
  * @brief  check if *timer has timed out
  * @brief  initialise *timer by a call with ticks = 0
  * @param  timer: pointer to timer variable
  * @param  ticks: number of ticks to wait
  * @retval return 0 if not timeout, !0 if timeout
  * @date   2012-12-14
  */
int timeout(uint32_t *timer, uint32_t ticks)
{
  uint32_t t, diff;
  int tout;

  tout = 0;
  t = getTicks();
  diff = t - *timer;

  if (0 == ticks || diff >= ticks) {
    *timer = t;
    tout = 1;
  }

  return tout;
} /* timeout */
Användarvisningsbild
Jan Almqvist
Inlägg: 1580
Blev medlem: 1 oktober 2013, 20:48:26
Ort: Orust

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av Jan Almqvist »

Jo, men vad är getTicks() för en funktion?
hummel
Inlägg: 2267
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av hummel »

Mycket bra lösning. Du skriver kod som "De gamle" på samma sätt gör jag efter att ha lärt mig att man får inte slarva när det gäller säkerhetskritiska tillämningar.

Tummen upp!

PS. Du ditt sätt att skriva kod tyder på att du även använder Misra?
Användarvisningsbild
lillahuset
Gått bort
Inlägg: 13969
Blev medlem: 3 juli 2008, 08:13:14
Ort: Norrköping

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av lillahuset »

Jan Almqvist: Vad som helst som hämtar något som räknas upp i lagom takt.
Jag brukar ha någon "systemtimer" som räknas upp mellan typ 100kHz och 10Hz beroende på behov.

hummel: Jag har tittat lite på Misra och kanske tagit lite intryck. Min filosofi har alltid varit att eftersom jag är måttligt smart gäller det att skriva kod som är lätt att förstå. :)
hummel
Inlägg: 2267
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av hummel »

Du har helt rätt vad gäller att skriva kod. tänk om fler gjorde på det sättet!
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4694
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av Swech »

Jag kör med nedräkningsvariabler men
låter ISRen även dela ned så man får
0.01 sek 100hz
0.1 sek 10hz
1.0 sek 1hz
så körs nedräkningen av variablerna i respektive kategori.
Med 8 bitar så brukar man få tillräcklig upplösning på det man vill ha.
t.ex. 0.3 sek knappfördröjning -> räkna ned från 30 i 0.01hz
5 sek tänd diod. -> räkna ned från 50 i 0.1hz
1 minut fördröjning -> räkna ned från 60 i 1.0hz

Swech
persika
EF Sponsor
Inlägg: 1347
Blev medlem: 31 juli 2006, 22:14:37
Ort: Österlen, Skåne

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av persika »

Icekapp> Jag förordar ju också kraftigt att alla Delay() i programmering är en styggelse som inte får hända.

Det är ett felaktigt påstående.
I en enkel tillämpning kan det vara fullt tillräckligt att använda enkla fördröjningar med delay().
kodar-holger
EF Sponsor
Inlägg: 920
Blev medlem: 26 maj 2014, 12:54:35
Ort: Karlskoga

Re: Nerräkningstimer kontra system-timer, tankar

Inlägg av kodar-holger »

Det finns dessutom inget som säger att delay() innebär en busy-loop och att allt annat stannar.

Ingår det i det gamla sättet att programmera att man inte skriver en enda kommentarsrad förutom några kryptiska taggar till doxygen så är jag mer emot det sättet än att använda delay()
Skriv svar