32 Bit Timer Attiny (Zeitstempel)

Gast #3923747
Lesenswert?

Hallo,

ich bin gerade dabei mir ein 32 Bit Zeitstempel zu programmieren. Das 
funktioniert auch fast perfekt. Um den Code zu testen, habe ich mir eine 
Warteschleife programmiert, die eine LED alle X Mikrosekunden 
ausschaltet und dann sofort wieder einschaltet. Das funktioniert auch 
nur soweit, nur zwischen drin, kommt es immer wieder zu einem 
Doppelpuls(Siehe Oszibild). Woher kommt der Ausreißer?



Timer Klasse: CPP-Datei

static Systemtimer* pSystemtimer;
1
uint32_t Systemtimer::SystemtimerGetTime()
2
{
3
  uint32_t time;
4
  cli();
5
  
6
    time=(uint32_t)(TCNT1)+pSystemtimer->Time;
7
  sei();
8
  return time;
9
}
10

11
ISR(TIMER1_OVF_vect)
12
{
13
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
14
  {  
15
    pSystemtimer->Time+=0x10000;
16

17
  }
18
  
19
}

Header Datei:
1
class Systemtimer
2
{
3

4
  uint32_t SystemtimerGetTime();
5
  
6
  //Use Time in atomic Block
7
  volatile uint32_t Time;
8

9

10
}; //Systemtimer

Main:
1
uint32_t nexttime,current,waiting=100;
2

3
  
4
  current=timestamp.SystemtimerGetTime();  
5

6
    while(1)
7
    {
8
  LED.SetLed(false);
9

10
  do
11
  {
12
    LED.SetLed(true);
13
    nexttime=timestamp.SystemtimerGetTime();
14
  } while (nexttime-current<waiting);
15
  current=nexttime;
16

17
    }
Angehängte Dateien:
Gast #3923752
Lesenswert?

Pate schrieb:
> cli();
>
>     time=(uint32_t)(TCNT1)+pSystemtimer->Time;
Was wenn "zwischen" diesen beiden Zeilen der Timer überläuft, TCNT1 also 
0 zurückliefert, aber der Timerinterrupt noch nicht ausgeführt wurde, 
also pSystemtimer->Time noch nicht um 0x10000 erhöht wurde? Dann erfährt 
deine Zeit einen Sprung rückwärts...
Gast #3923754
Lesenswert?

ATOMIC_BLOCK mach in einer ISR wenig sinn.


> time=(uint32_t)(TCNT1)+pSystemtimer->Time;
wenn TCNT1 überläuft während die Berechnung läuft könnte das zu 
Problemen kommen. Da hilft auch das cli und sei nicht weil er Trotzdem 
weiterzählt.
Gast #3923768
Lesenswert?

Pate schrieb:
> Achso, dann muss ich das Überlaufbit mit betrachten.
Das bringt leider auch nix, was wenn der Überlauf kommt zwischen der 
Abfrage des Bits und Abfrage von TCNT1 - egal in welcher Reihenfolge 
dies stattfindet?
Eigentlich müsstest du den Timer temporär anhalten...
Gast #3923779
Lesenswert?

Ich habe es mal wie folgt angepasst:
1
uint32_t Systemtimer::SystemtimerGetTime()
2
{
3
  uint32_t time;
4
  cli();
5
  if (TIFR1 & (1<<TOV1))
6
  {
7
    pSystemtimer->Time+=0x10000;
8
    TIFR1=(1<<TOV1);
9
  }
10
  time=(uint32_t)(TCNT1)+pSystemtimer->Time;
11
  
12
  sei();
13
  return time;
14
}

Und laut Oszi, gibt es keine Ausreißer mehr, nur ein Jitter durch die 
If-Abfrage.
Gast #3923872
Lesenswert?

Wie wäre es mit einem zweiten Timer-Register, bei dem du per 
Flag-Register die Aktualisierung stoppst, wenn du Auslesen möchtest? 
Oder lass deinen Code zum Auslesen mit dem IRQ synchronisieren. Da gibt 
es viele Möglichkeiten, Timer anhalten würde ich nie machen, wenn's denn 
genau sein soll.

PS: Mit ARM wäre das nicht passiert (SCNRT) ;)
Gast #3923886
Lesenswert?

Das ein Timer soo viel ärger machen kann...

Das mit der Synchronisierung versuche ich derzeit. Ich habe mir in der 
Klasse eine Funktion gebaut, die Reibungslos funktioniert. Nur wenn ich 
mir eine Warte FUnktion in der Main baue, gehts daneben...
1
void Systemtimer::SystemtimerWait(uint32_t Wait_us)
2
{  
3
  uint32_t nexttime=this->SystemtimerGetTime()+Wait_us;
4
  
5
  while(this->SystemtimerGetTime()<nexttime)
6
  {
7
  }
8

9
}
Gast #3923894
Lesenswert?

greg schrieb:
> PS: Mit ARM wäre das nicht passiert (SCNRT) ;)

Wie, ARM baut auch Timer? Du meinst 'systick'?

Die Überlaufe eines Timers sind doch kein Problem, man muß sie nur 
beachten!

In der Routine "ISR(TIM1_CAPT_vect)" kann man sehen, wie es geht:
http://www.mino-elektronik.de/fmeter/fm_software.htm#bsp11

Georg G. schrieb:
> Wie wäre es mit "zweimal ablesen". Dann kannst du per Plausibilität
> entscheiden, ob und bei welcher Ablesung dein Timer übergelaufen ist.

Auch das kann man machen, wenn es nicht zeitkritisch ist.
Gast #3923923
Lesenswert?

Und wenn du den Timer stoppst und anschließend zu TCNT1 x addierst, 
wobei x die Anzahl der Takte ist, die es von Stoppen bis nach der 
Berechnung braucht (x könnte man aus dem Assemblerlisting berechnen)?
Gast #3923968
Lesenswert?

So jetzt gehts stabil, habe nochmal die Timer32.c übernommen und auf 
meine Sourcen angepasst.
1
uint32_t Systemtimer::SystemtimerGetTime()
2
{
3

4
    uint32_t val;
5
    uint8_t tifr;
6

7
    cli();
8
    val = pSystemtimer->Time + TCNT1;
9
    tifr = TIFR1;          // read interrupt flags
10
    sei();
11
    if( (tifr & 1<<TOV1) && !(val & 0x8000) )
12
    val += 32768;         
13

14
}
Gast #3923982
Lesenswert?

Die Timer32, produziert den gleichen Fehler... Schon wieder zu früh 
gefreut...


uint32_t Systemtimer::SystemtimerGetTime()
{

    uint32_t val;
    uint8_t tifr;

    cli();
    val = pSystemtimer->Time + TCNT1;
    tifr = TIFR1;          // read interrupt flags
    sei();
    if( (tifr & 1<<TOV1) && !(val & 0x8000) )
    val += 32768;
  return val;

}
Gast #3923995
Lesenswert?

Egal was ist da Eintrage, jedesmal kommt der Fehler...
1
uint32_t Systemtimer::SystemtimerGetTime()
2
{
3

4
    uint32_t val;
5
    uint8_t tifr;
6

7
    cli();
8
    val = pSystemtimer->Time + TCNT1;
9
    tifr = TIFR1;
10
    sei();
11

12
    if( (tifr & 1<<TOV1) && !(val & 0x80) )
13
    val += 256;    
14
          bzw.
15
          if( (tifr & 1<<TOV1) && !(val & 0x8000) )
16
    val += 32768;    
17
          bzw.
18
          if( (tifr & 1<<TOV1) && !(val & 0x80) )
19
    val += 32768;   
20
   
21
  return val;
22
}
Gast #3924003
Lesenswert?

Pate schrieb:
> Egal was ist da Eintrage, jedesmal kommt der Fehler...
> uint32_t Systemtimer::SystemtimerGetTime()
> {
>
>     uint32_t val;
>     uint8_t tifr;
>
>     cli();
>     val = pSystemtimer->Time + TCNT1;
>     tifr = TIFR1;
>     sei();
>
>     if( (tifr & 1<<TOV1) && !(val & 0x80) )
>     val += 256;
>           bzw.
>           if( (tifr & 1<<TOV1) && !(val & 0x8000) )
>     val += 32768;
>           bzw.
>           if( (tifr & 1<<TOV1) && !(val & 0x80) )
>     val += 32768;
>
>   return val;
> }

Habe es jetzt mit dem doppelten getestet:
1
uint32_t Systemtimer::SystemtimerGetTime()
2
{
3

4
    uint32_t val;
5
    uint8_t tifr;
6

7
    cli();
8
    val = pSystemtimer->Time + TCNT1;
9
    tifr = TIFR1;
10
    sei();
11
    if( (tifr & 1<<TOV1) && !(val & 0x8000) )
12
    val += 65536;       
13
  return val;
14
}

Jetzt taucht kein Fehler mehr auf. 15000 Passes keine Fails :)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren