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
>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
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?
> 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?
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?:
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?)
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.
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_tt,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).
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.
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.
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
return0;
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).
> 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.
>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
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.
>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
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. ;-)
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!
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.
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
# #
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
voidfoo(void);
und
1
voidfoo();
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
voidfoo()
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(charc,floatf)
3
{
4
...
5
}
6
7
void
8
bar(c,f)
9
charc;
10
floatf;
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.
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
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.