Forum: Mikrocontroller und Digitale Elektronik Timer1 ISR - kein festes zeitverhalten


von Adam P. (adamap)


Lesenswert?

hallo zusammen,

ich habe seit gestern ein problem mit meinem timer1 (atmega 1284p).
zuerst meine konfig:
1
#define CLK_PRELOAD_10MS     2499L
2
3
void sys_clk_init(void)
4
{
5
    /* Compare Match */
6
    TCCR1A = 0;
7
    TCCR1B |= (1<<WGM12) | (1<<CS11) | (1<<CS10);
8
    OCR1A = CLK_PRELOAD_10MS;
9
    TIMSK1 |= (1<<OCIE1A);
10
}

die ISR vom timer löst jede 10ms aus, soweit so gut. in der ISR erhöhe 
ich eine variable ticks vom typ uint32_t.
diese lasse ich mir in der main(), jede sekunde ausgeben - die ausgabe 
ist dann:

100
200
300
400
...usw.

nun bekomme ich ein fehlverhalten bei festen zeiten, was mir nicht so 
ganz einleuchtet.
z.b. von dem wert 9300 auf 9316. egal wie oft ich die steuerung neu 
starte, der fehler tritt immer an den gleichen stellen auf...auch im 
späteren verlauf.

meine main():
1
int main(void)
2
{
3
    init();
4
  
5
    char num[7];
6
  
7
    DDRA = 0xff;
8
    DDRD = 0xff;
9
  
10
    uint32_t tick = get_uptime(TICKS) + 100;
11
  
12
    while(1)
13
    {
14
        if(tick <= get_uptime(TICKS))
15
        {
16
            tick = get_uptime(TICKS) + 100;
17
            PORTA ^= (1<<PINA0);
18
            PORTD ^= (1<<PIND5);
19
      
20
            ultoa( (tick), num, 10);
21
            uart_puts(num);
22
            uart_putc(10);
23
            uart_putc(13);
24
        }
25
    }
26
    return 0;
27
}

ist im moment nur eine testausgabe, hoffe nicht das der fehler in der 
abfrage der ticks liegt, dass er dort zu früh/spät abfragt und einen 
falschen wert erkennt. die andere möglichkeit was mir noch einfällt 
könnte der vorladewert des timers sein?!

hoffe ihr habt eine idee.

gruß adam

von Karl H. (kbuchegg)


Lesenswert?

Adam P. schrieb:

> die ISR vom timer löst jede 10ms aus, soweit so gut. in der ISR erhöhe
> ich eine variable ticks vom typ uint32_t.

Und die ist hoffentlich volatile und und die Ausleseanweisung ist mit 
einem cli()/sei() gekapselt, zwecks atomarem Zugriff.

von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> hoffe nicht das der fehler in der
> abfrage der ticks liegt

Hoffen und Harren macht manchen zum Narren.

Immer diese Zumutung, nur durch Schlüsselloch gucken zu dürfen.
Mach die Tür auf und zeig alles (als Anhang)!

Peter

von Adam P. (adamap)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Und die ist hoffentlich volatile und und die Ausleseanweisung ist mit
> einem cli()/sei() gekapselt, zwecks atomarem Zugriff.

du meinst die tick variable? wie du siehst ist die in main nicht 
volatile.
den wert den ich erhöhe wird in einem array abgelegt das diverse daten 
enthält, die fkt. get_uptime liest dann von dem index x aus dem array 
den wert, den die ISR erhöht.

also wenn ich nun in der main z.b. cli()/sei() einbaue, dann sollte es 
gut sein? aber damit halte ich doch die abarbeitung der ISR auf und habe 
somit doch im laufe der zeit eine zeitverschiebung oder?

von Karl H. (kbuchegg)


Lesenswert?

Adam P. schrieb:
> Karl Heinz Buchegger schrieb:
>> Und die ist hoffentlich volatile und und die Ausleseanweisung ist mit
>> einem cli()/sei() gekapselt, zwecks atomarem Zugriff.
>
> du meinst die tick variable?

Nö.
Wie kommt get_uptime(TICKS) zu seinen Erkentnissen?

Dort wird ja wohl die Variable in irgendeiner Form ausgewertet, die von 
der ISR hochgezählt wird.

> wie du siehst ist die in main nicht
> volatile.
Ich sehe nur, dass du die relevanten Dinge wieder mal nicht zeigst, zu 
gunsten von Dingen die eh keinen interessieren. Dass du ein ultoa 
fehlerfrei hinkriegst, davon sollte man eigentlich ausgehen können.

> den wert den ich erhöhe wird in einem array abgelegt das diverse daten
> enthält, die fkt. get_uptime liest dann von dem index x aus dem array
> den wert, den die ISR erhöht.

Ach.
Da gibt es also noch zusätzlich jede Menge anderes Zeug, dass du 
verbockt haben könntest?

Nur gut, dass du uns das alles gezeigt hast!
Oops. Hast du ja gar nicht.

von Adam P. (adamap)


Angehängte Dateien:

Lesenswert?

...ein moment, bitte :)

p.s. ich habe kein problem euch das zu zeigen, es hätte nur sein können, 
dass jemand eine spontane idee hätte.

von Karl H. (kbuchegg)


Lesenswert?

1
uint32_t get_uptime(uint8_t format)
2
{
3
  cli();   // *********  <--------------------
4
5
6
  switch(format)
7
  {
8
  case TICKS:
9
      return reg_read32(SYS_TICKS);
10
    break;
11
12
  case SECOND:
13
      return reg_read32(UPTIME_SS);
14
    break;
15
16
  case MINUTE:
17
      return reg_read32(UPTIME_MM);
18
    break;
19
20
  case HOUR:
21
      return (uint32_t)(reg_read(UPTIME_HH));
22
    break;
23
24
  case DAY:
25
      return (uint32_t)(reg_read(UPTIME_DD));
26
    break;
27
    
28
  default:
29
      return 0;
30
    break;
31
  }
32
33
  sei();   // ***********   <---------------------
34
}


mal als Erstabhilfe. Ob sonst nach was im argen liegt hab ich jetzt 
nicht kontrolliert.

von Adam P. (adamap)


Lesenswert?

ok, vielen dank! werde ich direkt mal ausprobieren.

bekomme ich denn dadurch keine zeitverschiebung in meinem 10ms takt?
falls die ISR genau in diesem moment den wert erhöhen möchte.

falls doch, welche möglichkeiten gäbe es dieses problem zu umgehen?
bin für jede anregung dankbar!

von Karl H. (kbuchegg)


Lesenswert?

Adam P. schrieb:
> ok, vielen dank! werde ich direkt mal ausprobieren.
>
> bekomme ich denn dadurch keine zeitverschiebung in meinem 10ms takt?
> falls die ISR genau in diesem moment den wert erhöhen möchte.

Dann findet das Erhöhen halt um ein paar µs später statt und beim 
nächsten ISR Aufruf stimmts dann wieder.

Nur weil Interrupts mittels cli() gesperrt sind, bedeutet das ja nicht, 
dass Interrupts automatisch unterschlagen werden. Die ISR wird dann eben 
erst zum Zeitpunkt aufgerufen, an dem mittels sei() die Interrupts 
wieder freigegeben werden. Und da der Timer in der Zwischenzeit 
unbeeindruckt weitergezählt hat, kommt der nächste Interrupt dann auch 
wieder pünktlich.

> falls doch, welche möglichkeiten gäbe es dieses problem zu umgehen?

Bissi lernen, wie die Dinge wirklich funktionieren.

von Adam P. (adamap)


Lesenswert?

vielen danke karl heinz!

ist ja nicht so das ich erst seit gestern µc programmieren, aber 
manchmal steh ich da und seh den wald vor lauter bäumen nicht mehr.

aber das mit dem sperren ist ja irgendwie klar beim lesen, da ich 
mehrere 16bit array-plätze nutze und beim schreiben auch eine 
bit-verschiebung stattfindet etc.

somit bedanke ich mich bei euch! :-)

von Peter D. (peda)


Lesenswert?

Dein Programm ist mir viel zu kompliziert.
Wenn Du mehrere Variablen zusammenfassen willst, mach ne Struct daraus. 
Aber nicht sowas komisches zu Fuß.

Ne 32Bit Division im Interrupt wäre mir etwas grenzwertig.
Die Umrechnung der Ticks in was Du willst kann doch in aller Ruhe das 
Main machen. Das muß dann auch nicht tausende Register sichern und 
restoren für nen Funktionsaufruf.


Peter

von Adam P. (adamap)


Lesenswert?

die 32bit division gehört dort nicht hin...ist nur testweise.
das mit den registern hat damit zu tun, dass sie später über uart 
zugänglich sein sollen. z.b. übers terminal mit "getreg xy".
bei variablen stelle ich mir das recht schwer vor.

von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> bei variablen stelle ich mir das recht schwer vor.

Nö.
Du kannst die Elemente einer Struct auch Byte- oder Wortweise 
ansprechen, indem Du ne Union draus machst.


Peter

von Adam P. (adamap)


Lesenswert?

eine union mit mehreren variablen? dann ist die doch nur so groß wie die 
größte variable die sie enthält.
kann mir das gerad nicht so ganz vorstellen wie du das meinst, hast du 
ein kleines code-bsp. vllt. ist es ja wirklich einfacher, dann werde ich 
mein ansatz mit den registern verwerfen :-D

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Adam P. schrieb:
>> bei variablen stelle ich mir das recht schwer vor.
>
> Nö.
> Du kannst die Elemente einer Struct auch Byte- oder Wortweise
> ansprechen, indem Du ne Union draus machst.

Und selbst wenn nicht, das Zauberwort heisst UART. Wenn du über UART auf 
die 'Register' zugreifst, hast du alle Zeit der Welt (ok, übertrieben), 
die Dinge auseinanderzupfriemeln - die UART Übertragung ist langsamer 
als das.

Du verlangsamst jetzt den häufigsten Fall nur um im Ausnahmefall 
schneller zu sein. So was nennt man Pessimierung und nicht Optimierung.

Was ist der absolut häufigste Fall?
Der, dass du einfach nur Ticks brauchst. Wenn du wissen willst, wieviel 
Zeiteinheiten das entspricht, dann kannst du das dort, wo du die Zeiten 
brauchst (normalerweise nur im Benutzerinterface) immer noch umrechnen. 
Und gibt dir der Benutzer eine Zeit vor, dann kannst du diese Zeit 
einfach in Ticks zurückrechnen und bist wieder auf der Ebene, in der du 
einfach nur mit Ticks hantierst.

von Adam P. (adamap)


Lesenswert?

du hast recht keine frage, die berechnung sei mal vernachlässigt...
ist jetzt auch nicht die endlösung.

die ganze geschichte so wie sie im moment ist, habe ich nur zu 
testzwecken aufgebaut um die konfig des timers zu prüfen.

der häufigste fall ist sicherlich der, dass ich die ticks brauche -
da ich einen zeitlich definierten ablauf von funktionsaufrufen benötige.

von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> eine union mit mehreren variablen? dann ist die doch nur so groß wie die
> größte variable die sie enthält.

Hier mal der Klassiker einer struct-union:
1
union b_w {
2
  struct{
3
    uint8_t l;
4
    uint8_t h;
5
  }b;
6
  uint16_t w;
7
};


Peter

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.