Forum: Compiler & IDEs Timer ISR beenden


von Stephan (Gast)


Lesenswert?

Guten Morgen zusammen,

hab mal ne Frage zu ner ISR:
1
ISR(TIMER0_OVF0_vect)
2
  {
3
    TCNT0 = 0x3D;
4
    
5
    timer_tick++;
6
7
    if  (timer_tick == 40)
8
      { 
9
        timer_tick = 0;
10
        timer_sec++;
11
      }
12
13
    if (timer_sec == 60)
14
      {
15
        timer_sec = 0;
16
        timer_min++;
17
      }
18
19
    if (timer_min == 60)
20
      {
21
        timer_min = 0;
22
        timer_hour++;
23
      }
24
  }

so sieht die Timer_ISR (AVR GCC mit Tiny 26) bei mir aus.
Wie man erkennen kann, soll hiermit ein Stunden-, Minten- und 
Sekunden-Timer realisiert werden.
Mein Frage nun:
Muss ich die ISR noch irgendwie beenden, so wie bei normalen UP mit 
return?
Weil wenn ich nun in meinem Simulator da rein springe, kommt er nicht 
mehr raus. Auch in meinem Gerät tut sich nichts, wenn ich später das 
Minutenregister abfrage.

Hoffe ihr könnt mir helfen.

Gruß
Stephan

von OliverSo (Gast)


Lesenswert?

>Muss ich die ISR noch irgendwie beenden, so wie bei normalen UP mit
>return?

Nö.
Auch ein "normales UP" braucht nur dann ein return, wenn ein Wert 
zurückgegeben wird. (In Basic mag das anders sein).

>Weil wenn ich nun in meinem Simulator da rein springe, kommt er nicht
>mehr raus. Auch in meinem Gerät tut sich nichts, wenn ich später das
>Minutenregister abfrage.

Welcher Simulator, wie reinspringen, ...?

Zeig doch mal das ganze Programm.

Oliver

von Thomas W. (thomasw)


Lesenswert?

Du musst das Flag löschen, dass den Interrupt ausgelöst hat.

MfG

von Stephan (Gast)


Angehängte Dateien:

Lesenswert?

Wenn ich den Simulator von AVR Studio verwende kann man ja Schritt für 
Schritt das Programm durchgehen. Nur wenn ich nun in die ISR 
reinspringe, komm ich da nicht mehr raus.

Also müsste ich dann timer_min mit reutrn zurückgeben?
kann ich auch mehrere Werte zurückgeben, bräuchte auch den Stundenwert.
Wenn ja, wie?

von Rolf Magnus (Gast)


Lesenswert?

> Also müsste ich dann timer_min mit reutrn zurückgeben?

Nein. Wohin sollte es auch zurückgegeben werden? Du rufst die ISR ja 
nirgends explizit auf.

> kann ich auch mehrere Werte zurückgeben, bräuchte auch den Stundenwert.
> Wenn ja, wie?

So wie du es schon tust: Indem du sie als globale Variablen anlegst. 
Allerdings mußt du sie als volatile deklarieren.

> Nur wenn ich nun in die ISR reinspringe, komm ich da nicht mehr raus.

Wie "springst" du denn da rein?

Warum gibst du eigentlich aus allen Funktionen 0 zurück (außer bei 
main(), wo es eigentlich vorgeschrieben ist)? Außerdem solltest du bei 
der Parameterliste explizit void reinschreiben, also z.B. so:

[/C]
void timer_init(void)
{
  TCCR0 = 0x05;
  TIMSK = 0x02;
  TCNT0 = 0x3D;

}
[/C]

Übrigens empfiehlt es sich, statt irgendwelchen magischen Zahlen die 
Bits beim Namen zu nennen (bei TCNT0 natürlich nicht). Das ist deutlich 
lesbarer.

Übrigens: Ist es gewollt, daß dein Programm einmal pro Stunde für eine 
Minute wild den PB4 hin- und hertoggelt?

von Stephan (Gast)


Lesenswert?

ok danke für die Tipps.

Bin grad erst noch am Anfang mit der C-Programmierung.
Habe mich die ganze Zeit mit Assembler rumgetrieben und wollte
jetzt halt mal was neues ausprobieren.

Ja des mit dem toggeln war halt, dass ich seh, ob der Timer irgendwas 
macht.

Eine volatile Deklaration geht dann so?:
1
volatile uint8_t timer_tick, timer_sec, timer_min, timer_hour;

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus wrote:

> Außerdem solltest du bei
> der Parameterliste explizit void reinschreiben, also z.B. so:
>
> [/C]
> void timer_init(void)
> {
>   TCCR0 = 0x05;
>   TIMSK = 0x02;
>   TCNT0 = 0x3D;
>
> }
> [/C]
>

Und wozu soll das gut sein?

(Das void in der Argumentliste macht nur bei Protoypen Sinn,
da ist es dann aber tatsächlich wirklich wichtig. Bei
Funktionsdefinitionen wie hier im Beispiel ist es IMHO sinnlos.
Was soll der Compiler denn in diesem Fall annehmen, ausser dass
die Argumentliste leer ist?)

von Karl H. (kbuchegg)


Lesenswert?

Thomas W. wrote:
> Du musst das Flag löschen, dass den Interrupt ausgelöst hat.
>

Quatsch.
Dafür sorgt schon der reine Aufruf der ISR.
AFAIK ist einzig und alleine der UART Receive Interrupt eine
Ausnahme: Das Interrupt Flag wird erst durch Lesen von UDR
gelöscht. Aber ansonsten wird das Flag immer automatisch
durch den Aufruf der zugehörigen ISR gelöscht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ja, volatile funktioniert so.  Ist aber eine ,,Pessimierung'', ggf.
willst du die Werte dann in der ISR am Anfang cachen:
1
ISR(TIMER0_OVF0_vect)
2
  {
3
    uint8_t t, s, m, h;
4
5
    TCNT0 = 0x3D;
6
7
    t = timer_tick;
8
    s = timer_sec;
9
    m = timer_min;
10
    h = timer_hour;
11
12
    if  (++t == 40)
13
      {
14
        t = 0;
15
        s++;
16
      }
17
18
    if (s == 60)
19
      {
20
        s = 0;
21
        m++;
22
      }
23
24
    if (m == 60)
25
      {
26
        m = 0;
27
        h++;
28
      }
29
30
    timer_hour = h;
31
    timer_min = m;
32
    timer_sec = s;
33
    timer_tick = t;
34
  }

Übrigens: das Rücksetzen von TCNT0 innerhalb der ISR ist nicht gerade
optimal.  Vom Overflow selbst bist zum Rücksetzen vergeht Zeit, u. U.
hat der Zähler dann bereits weiter gezählt.  Wenn dein AVR für den
Timer 0 den CTC-Modus unterstützt (clear timer on compare match), dann
nimm lieber diesen.  Dann heißt aber die ISR auch anders (z. B.
TIMER0_COMPA_vect).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> (Das void in der Argumentliste macht nur bei Protoypen Sinn,
> da ist es dann aber tatsächlich wirklich wichtig.

Wenn die Definition der Funktion zugleich die Deklaration ist
(d. h. die erste Erwähnung dieses Namens im laufenden Programmtext),
dann hat es sehr wohl Sinn.  Ansonsten schadet es auch nichts, wenn
man es sich gleich so angewöhnt, dann vergisst man es wenigstens
nicht, wenn es wirklich drauf ankommt.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch wrote:
> Karl heinz Buchegger wrote:
>
>> (Das void in der Argumentliste macht nur bei Protoypen Sinn,
>> da ist es dann aber tatsächlich wirklich wichtig.
>
> Wenn die Definition der Funktion zugleich die Deklaration ist
> (d. h. die erste Erwähnung dieses Namens im laufenden Programmtext),
> dann hat es sehr wohl Sinn.

Glaubst du im Ernst, dass ein Compiler so dämlich ist
aus

void foo()
{
  irgendwas;
}

sich ein Deklaration von
void foo();
zurechtzulegen.

> Ansonsten schadet es auch nichts,

Das ist wohl wahr.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Glaubst du im Ernst, dass ein Compiler so dämlich ist
> aus
>
> void foo()
> {
>   irgendwas;
> }
>
> sich ein Deklaration von
> void foo();
> zurechtzulegen.

Na klar macht er das.
1
#include <stdio.h>
2
void
3
foo()
4
{
5
        printf("foo!\n");
6
}
7
8
int
9
main(void)
10
{
11
        foo(42);
12
        bar(42);
13
        return 0;
14
}
1
% gcc -Wall -Wextra -ansi -pedantic -c foo.c
2
foo.c: In function `main':
3
foo.c:12: warning: implicit declaration of function `bar'

Wie zu sehen ist, wird foo() ordentlich als Funktion mit "old style
K&R"-Deklaration registriert, es gibt keine Warnung, wenn die Funktion
anschließend mit einem Parameter aufgerufen wird, obwohl sie
eigentlich gar keinen Parameter verarbeiten kann.  Eine im Vergleich
dazu undeklarierte Funktion bar() erzeugt hingegen sehr wohl eine
Warnung.

Der C-Standard sieht es ausdrücklich vor, dass Deklaration und
Definition einer Funktion zusammen fallen können.  Zwar sagt der
Standard, dass eine leere Parameterliste in einem Funktionsdeklarator,
der Teil einer Funktionsdefinition ist (also nicht eigentständig nur
als Deklarator benutzt wird) eine parameterlose Funktion deklariert.
Da aber bei der nicht-prototypischen Funktionsdeklaration der Compiler
keinerlei Argumentüberprüfungen vornimmt, vergisst zumindest der GCC
diese Parameterlosigkeit sofort wieder, obwohl er den Namen der
Funktion sehr wohl ab diesem Moment als deklariert ansieht.  Nachdem
ich Punkt 6.7.5.3 Anmerkung 14 Satz 2 gelesen habe, denke ich, dass
der Compiler zumindest eine Warnung generieren dürfte, wenn die
Funktion dann dennoch mit Parametern aufgerufen wird (so wie hier).

von Rolf Magnus (Gast)


Lesenswert?

> Glaubst du im Ernst, dass ein Compiler so dämlich ist aus
>
> void foo()
> {
>   irgendwas;
> }
>
> sich ein Deklaration von
> void foo();
> zurechtzulegen.

Also gcc ist so "dämlich". Aber hat er denn eine andere Wahl? Ich kann 
grad nicht in der C-Norm nachschlagen, aber ich kann mir eigentlich 
nicht vorstellen, daß der Compiler hier an der Deklaration noch 
eigenmächtig was verändern darf.

von OliverSo (Gast)


Lesenswert?

>Ist aber eine ,,Pessimierung'', ggf.
>willst du die Werte dann in der ISR am Anfang cachen:

Generell ist der Hinweis sicher richtig, aber ggf. ist auch das richtige 
Stichwort. In dem speziellen Fall müsste man tatsächlich einmal genau 
nachzählen, ob die zusätzlichen 8 Zyklen fürs cachen tasächlich durch 
die bessere Registeroptimierung wieder rausgeholt werden, und bei mal 
gerade 40Hz ISRfrequenz und 8MHz CPU-Takt ist das alles sowieso völlig 
Wurscht :-)


Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

OliverSo wrote:

> Generell ist der Hinweis sicher richtig, aber ggf. ist auch das richtige
> Stichwort. In dem speziellen Fall müsste man tatsächlich einmal genau
> nachzählen, ob die zusätzlichen 8 Zyklen fürs cachen tasächlich durch
> die bessere Registeroptimierung wieder rausgeholt werden,

Der Compiler muss die Werte aus dem SRAM ohnehin in Register
laden, bevor er sie benutzt.  Damit ensteht durch das Cachen in
keinem Falle ein Nachteil.  Der generierte Code wird kleiner und
schneller:

/* function __vector_6 size 52 (40) */

gegenüber:

/* function __vector_6 size 49 (31) */

> und bei mal
> gerade 40Hz ISRfrequenz und 8MHz CPU-Takt ist das alles sowieso völlig
> Wurscht :-)

Klar, "never start optimizing before you have profiled it".
Andererseits sollte man sich solche Dinge einfach schon frühzeitig
angewöhnen.  Wenn irgendwo ein "volatile" auftaucht, sollten die
Alarmglocken klingeln, dass hier der Compiler am Optimieren gehindert
wird, und dass man ihm ggf. helfen kann, die Dinge trotzdem noch zu
optimieren, die optimierbar sind.  So ein Caching nachträglich
reinzubauen ist viel mehr Aufwand, als wenn man's gleich von Beginn an
macht.

von OliverSo (Gast)


Lesenswert?

>Der Compiler muss die Werte aus dem SRAM ohnehin in Register
>laden, bevor er sie benutzt.

Das ist richtig. Allerdings pusht er in der cache-Variante bei jedem 
Aufruf 4 Register mehr, als in der Nicht-Cache-Variante, während ein 
Teil des durch die Optimierung verkürzten Codes nur bei erfüllter 
Vergleichbedingung, d.h. äusserst selten, durchlaufen wird.

Da kommt es halt darauf an, was jetzt das Optimierungsziel ist: mittlere 
kürzeste Durchlaufzeit, oder minimale Maximaldurchlaufzeit.
Echt "profilert" hab ich das natürlich nicht.

>Wenn irgendwo ein "volatile" auftaucht, sollten die
>Alarmglocken klingeln, dass hier der Compiler am Optimieren gehindert
>wird

Grundsätzlich volle Zustimmung.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

OliverSo wrote:

> Das ist richtig. Allerdings pusht er in der cache-Variante bei jedem
> Aufruf 4 Register mehr, als in der Nicht-Cache-Variante, während ein
> Teil des durch die Optimierung verkürzten Codes nur bei erfüllter
> Vergleichbedingung, d.h. äusserst selten, durchlaufen wird.

OK, hier noch die mikro-optimierte Variante ohne zuätzliche Pushes. ;-)
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint8_t timer_tick, timer_sec, timer_min, timer_hour;
5
6
ISR(TIMER0_OVF0_vect)
7
{
8
    uint8_t t, s, m;
9
10
    TCNT0 = 0x3D;
11
12
    t = timer_tick;
13
14
    if  (++t == 40)
15
    {
16
        timer_tick = 0;
17
18
        s = timer_sec;
19
20
        if (++s == 60)
21
        {
22
            timer_sec = 0;
23
24
            m = timer_min;
25
26
            if (++m == 60)
27
            {
28
                timer_min = 0;
29
30
                timer_hour++;
31
            }
32
            else
33
            {
34
                timer_min = m;
35
            }
36
        }
37
        else
38
        {
39
            timer_sec = s;
40
        }
41
    }
42
    else
43
    {
44
        timer_tick = t;
45
    }
46
}

von Matthias (Gast)


Lesenswert?

Blöde Frage: Die Angabe der Controllers in der makefile bzw. im 
AVR-Studi stimmt, oder?

Das Programm sieht auf den ersten Blick lauffähig aus. Nur das sei();
würde ich gleich hinter den Initialisierungen und nicht im Mainloop
platzieren!

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch wrote:
> Wie zu sehen ist, wird foo() ordentlich als Funktion mit "old style
> K&R"-Deklaration registriert, es gibt keine Warnung, wenn die Funktion
> anschließend mit einem Parameter aufgerufen wird, obwohl sie
> eigentlich gar keinen Parameter verarbeiten kann.

Ich schmeiss mich weg.
Bin wohl schon zusehr C++ verwöhnt.

> Der C-Standard sieht es ausdrücklich vor, dass Deklaration und
> Definition einer Funktion zusammen fallen können.

Logisch.

>  Zwar sagt der
> Standard, dass eine leere Parameterliste in einem Funktionsdeklarator,
> der Teil einer Funktionsdefinition ist (also nicht eigentständig nur
> als Deklarator benutzt wird) eine parameterlose Funktion deklariert.

Auch das ist sofort einsichtig.

> Da aber bei der nicht-prototypischen Funktionsdeklaration der Compiler
> keinerlei Argumentüberprüfungen vornimmt, vergisst zumindest der GCC
> diese Parameterlosigkeit sofort wieder, obwohl er den Namen der
> Funktion sehr wohl ab diesem Moment als deklariert ansieht.

Den Satz versteh ich jetzt nicht. Ich seh irgendwie nicht wie
der erste Satzteil mit dem zweiten zusammenhängt.
Und ehrlich: so richtig logisch erscheint mir das nicht. Wenn die
Funktionsdefinition vorliegt, kann doch die Deklaration vollständig
davon abgeleitet werden. Eine leere Argumentliste in einer
Definition ist doch einer void Argumentliste in der Dekleration
absolut gleichzusetzen.

Aber gut das wir mal darüber gesprochen haben. Wieder was
gelernt.

von OliverSo (Gast)


Lesenswert?

Traumhaft :-)

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus wrote:
>> Glaubst du im Ernst, dass ein Compiler so dämlich ist aus
>>
>> void foo()
>> {
>>   irgendwas;
>> }
>>
>> sich ein Deklaration von
>> void foo();
>> zurechtzulegen.
>
> Also gcc ist so "dämlich". Aber hat er denn eine andere Wahl? Ich kann
> grad nicht in der C-Norm nachschlagen, aber ich kann mir eigentlich
> nicht vorstellen, daß der Compiler hier an der Deklaration noch
> eigenmächtig was verändern darf.

Ich seh das eigentlich so, dass diese Funktionsdefinition
eine Deklaration (*) von

void foo( void );

nach sich ziehen sollte. Denn genau das steht doch in der
Definition: Eine Funktion die keine Argumente nimmt. Das
Pendant dazu in der Dekleration ist die void Argumentliste.


(*) heisst das eigentlich Dekleration oder Deklaration
                              #                #

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dekl_a_ration. Kommt von Dekl_a_rieren.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Ich schmeiss mich weg.
> Bin wohl schon zusehr C++ verwöhnt.

C++ hat ja auch kein 1970er K&R-Erbe zu tolerieren...  Da ist
1
void foo(void);

und
1
void foo();

wirklich das Gleiche.

>> Da aber bei der nicht-prototypischen Funktionsdeklaration der
>> Compiler keinerlei Argumentüberprüfungen vornimmt, vergisst
>> zumindest der GCC diese Parameterlosigkeit sofort wieder, obwohl er
>> den Namen der Funktion sehr wohl ab diesem Moment als deklariert
>> ansieht.

> Den Satz versteh ich jetzt nicht. Ich seh irgendwie nicht wie der
> erste Satzteil mit dem zweiten zusammenhängt.

Beide Funktionen sind in der Tat als Funktionen ohne Parameter
deklariert.  Aber: dann schlägt das K&R-Erbe zu, in Form der
constraints in 6.5.2.2, Absatz 2:

  If the expression that denotes the called function has a type that
  includes a prototype, the number of arguments shall agree with the
  number of parameters. Each argument shall have a type such that its
  value may be assigned to an object with the unqualified version of
  the type of its corresponding parameter.

Da nun eine Deklaration + Definition der Form
1
void foo()
2
{
3
  ...
4
}

keinen Prototypen darstellt, ist der Compiler nicht gehalten, die
Anzahl der Argumente zu überprüfen.  Hyster^H^H^H^Historischer Code
könnte sich auf diesen Quatsch leider verlassen (auch wenn das
Verhalten bei falschen Argumenten explizit "undefined" ist), sodass
GCC daran wahrscheinlich auch nicht drehen mag.  Nicht-prototypische
Funktionsdeklarationen sind laut Standard ein "obsolescent feature",
d. h. sollten in neuem Code einfach mal nicht auftauchen.

> Und ehrlich: so richtig logisch erscheint mir das nicht. Wenn die
> Funktionsdefinition vorliegt, kann doch die Deklaration vollständig
> davon abgeleitet werden.

Nein, eine prototypische Deklaration ist etwas anderes als eine
K&R-mäßige.  Die folgenden beiden Funktionen sind nämlich in ihrem
Aufrufverhalten nicht gleich:
1
void
2
foo(char c, float f)
3
{
4
   ...
5
}
6
7
void
8
bar(c, f)
9
  char c;
10
  float f;
11
{
12
   ...
13
}

Beim Aufruf von foo() werden die Argumente so übergeben, als wären sie
Zuweisungen an die beiden Parameter.  Beim Aufruf von bar() dagegen
werden default argument promotions durchgeführt, also der erste
Parameter (der dann hoffentlich ein ganzzahliger Typ ist :) wird nach
int promotet, der zweite nach double!  (Diese default argument
promotion passiert bei protypischen Deklarationen nur noch für die
variable Argumentliste, die aus diesem Grunde auch keine Datentypen
kleiner als int oder double übernehmen kann.)

Alles zusammen jedenfalls gute Gründe, bei C immer
1
void
2
foo(void)
3
{
4
  ...
5
}

...zu schreiben.  Da's bei C++ nicht stört, bevorzuge ich diese
Schreibweise auch dort.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch wrote:

> ...  Nicht-prototypische
> Funktionsdeklarationen sind laut Standard ein "obsolescent feature",
> d. h. sollten in neuem Code einfach mal nicht auftauchen.

Wenigstens dafür aber gibt's eine Warnung, wenn man will:

-Wstrict-prototypes

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jörg Wunsch wrote:

> Wenigstens dafür aber gibt's eine Warnung, wenn man will:
>
> -Wstrict-prototypes

Sehe ich gerade noch:

  -Wold-style-definition (C only)
      Warn if an old-style function definition is used.  A warning is
      given even if there is a previous prototype.

von Karl H. (kbuchegg)


Lesenswert?

Merci, Jörg

Jetzt hab ichs auch geschnallt.

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.