Forum: Mikrocontroller und Digitale Elektronik 32 Bit Timer Attiny (Zeitstempel)


von Pate (Gast)


Angehängte Dateien:

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
    }

von Dr. Sommer (Gast)


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...

von Peter II (Gast)


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.

von Pate (Gast)


Lesenswert?

Achso, dann muss ich das Überlaufbit mit betrachten.

von Pate (Gast)


Lesenswert?

Das Atomic war eher eine Verzweiflungstat...

von Dr. Sommer (Gast)


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...

von Pate (Gast)


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.

von Pate (Gast)


Lesenswert?

Zu früh gefreut, ich muss wohl doch den Timer anhalten.

von Pate (Gast)


Lesenswert?

Wie könnte ich denn das Problem lösen?

von Georg G. (df2au)


Lesenswert?

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

von greg (Gast)


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) ;)

von Klaus 2. (klaus2m5)


Lesenswert?

Man könnte nach dem Einlesen des tcnt1 während disabled interrupts und 
wenn der tcnt <max/2 ist, das tifr überprüfen, ob ein Überlauf 
stattgefunden hat und noch nicht abgearbeitet wurde. In diesem Falle 
muss der zu speichernde timerwert entsprechend erhöht werden.

von Pate (Gast)


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
}

von m.n. (Gast)


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.

von Nils Friess (Gast)


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)?

von Peter D. (peda)


Lesenswert?


von Pate (Gast)


Lesenswert?

WIe soll ich denn das Capture Event auslösen?

von Pate (Gast)


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
}

von Pate (Gast)


Lesenswert?

Thread kann geschlossen werden :)

von Pate (Gast)


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;

}

von Peter D. (peda)


Lesenswert?

Pate schrieb:
> val += 32768;

???

von Ulrich H. (lurchi)


Lesenswert?

Im Prinzip ist die Idee mit dem Interruptflag und der Größe schon 
richtig. Nur eine Timerüberlauf bringt nicht 32768 Schritte, sondern das 
doppelte.

von Pate (Gast)


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
}

von Pate (Gast)


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 :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.