Hi,
Mit einem ATTiny85(GCC) steuere ich mittels Timer1 einen Triac an.
Ich habe eine Überlasterkennung. Falls eine Überlast erkennt wird, soll
das
Programm sicher beendet werden und kann nur noch durch einen Reset
wiedergestartet werden.
Hier mein Beispiel-Code:
1
C-Code
int main (void) {
initPortB();
initTimer1();
sei(); // Global Interrupts aktivieren
while(1) {
if(ueberlast()){ // Überlasterkennung
cli(); // disable all interrupts->Notaus
PORTB &= ~(1 << PORTB3); // Triac ausschalten
PORTB |= (1 << PORTB1); // ÜberlastLED einschalten
while(1); // Programm in Endlos-Loop?
//return 0; oder lieber so aussteigen?
}
}
return 0;
}
Nun meine Frage was ist schöner|besser?
Mit der while(1)-Schlaufe weiss ich, dass nichts mehr passiert, aber es
scheint unsauber, da das Programm nie wircklich beendet ist.
Mit return ist das Programm beendet, aber ich weiss eigentlich nicht,
wie der Kompiler das umsetzt. Ich nehme an, dass er ebenfalls in eine
Endlosschleife geht?
Was bevorzugt ihr?
ist alles prinzipiell gut so...
Dres schrieb:> Ich nehme an, dass er ebenfalls in eine> Endlosschleife geht?
ja... deshalb ist es egal ob du mit return rausgehst oder selber in
endlosschleife gehst.
ich finds nur so viel schoener
1
intmain(void){
2
//init
3
initPortB();
4
initTimer1();
5
sei();// Global Interrupts aktivieren
6
7
//body
8
while(!ueberlast()){};
9
10
//aufraeumen
11
cli();// disable all interrupts->Notaus
12
PORTB&=~(1<<PORTB3);// Triac ausschalten
13
PORTB|=(1<<PORTB1);// ÜberlastLED einschalten
14
15
return0;//hier ein kommentar dass in endlosschleife
16
//geht... dann verstehts auch der
17
//programmierer von nebenan, der das
18
//maintainen soll ;)
19
}
dann ist das programm eher so aufgebaut: init, body, aufräumen, ende
lg
noch eine sache:
wenn es wirklich ein notaus ist, ist es wichtig, dass du garantieren
kannst, dass der notaus sofort triggert. aber zwischen überlasterkennung
und cli koennen theoretisch ganz viele andere interrupts triggern und
den notaus verzögern. deshalb besser die überlasterkennung an einen
interrupt zu hängen mit höchster prio und dort die notaus
konfigurationen vorzunehmen. dann kann niemand mehr dazwischenfunken.
1
intmain(void){
2
3
initPortB();
4
initTimer1();
5
sei();// Global Interrupts aktivieren
6
7
8
while(1){};
9
10
11
return0;
12
}
13
14
voidueberlast_ISR(void){//event handler von überlast interrupt mit höchster prio
Sina A. schrieb:> deshalb besser die überlasterkennung an einen> interrupt zu hängen mit höchster prio
CLI ist schneller, als erstmal einen Interrupt zu triggern.
Prioritätslevel gibt es beim AVR nicht.
Peter D. schrieb:> Prioritätslevel gibt es beim AVR nicht.
Da hab ich ne Frage zu.
Ich habe gehört, dass das Prio Level abhängig ist von der Vector No.
Stimmt das?
Beispiel Atmega8.
Vector No.1 RESET
Vector No.2 INT0
Vector No.3 INT1
Vector No.4 TIMER2 COMP
usw.
Demnach das INT0 höher priorisiert wird als INT1.
Ich meine das habe ich irgendwo gehört, sicher bin ich mir aber nicht
mehr. (war vllt auch bei PIC(?!) so...)
Dres schrieb:> Nun meine Frage was ist schöner|besser?
Die eigene while-Schleife finde ich besser, da sie explizit ausdrückt,
was du machen willst und nicht darauf vertraut, dass implizit die
Laufzeitumgebung das selbe macht, wenn du aus main() "rausfällst".
> Mit der while(1)-Schlaufe weiss ich, dass nichts mehr passiert, aber es> scheint unsauber, da das Programm nie wircklich beendet ist.
Das sehe ich eher anders rum, da bei einem µC das Programm sowieso nie
beendet werden sollte. Sieh deine Schleife nicht als "Ende des
Programms", sondern als "in einem sicheren Zustand halten".
Tim W. schrieb:> Peter D. schrieb:>> Prioritätslevel gibt es beim AVR nicht.>> Da hab ich ne Frage zu.> Ich habe gehört, dass das Prio Level abhängig ist von der Vector No.
Kommt drauf an, was man unter Interrupt-Prio versteht. Manche verstehen
darunter, dass Interrupts höherer Priorität die mit geringerer Priorität
unterbrechen können, aber nicht umgekehrt. Sowas gibt's beim AVR nicht.
Was die "Prioritäten" beim AVR angeht, wirken sie sich nur darauf aus,
welche Interrupt zuerst abgearbeitet wird, wenn zwei
Interrupt-Anforderungen gleichzeitig anstehen.
Normalerweise reicht es, den Port nur einmal auszuschalten. Aber da es
nichts kostet und man nie weiß, was EMV so für spielchen treibt, schadet
es auch nicht, die Abschaltung ständig zu wiederholen.
Tim W. schrieb:> Demnach das INT0 höher priorisiert wird als INT1.
Eine IRQ-Priorisierung im eigentlichen Sinne gibt's wie gesagt auf AVR
nicht. Falls mehrere IRQs anliegen, wird diejenige mit kleiner Nummer
zuerst abgearbeitet. Dieses Szenario ist nicht so selten, weil viele
AVR-Programme ISRs blockierent implementieren, d.h. es gibt recht große
IRQ-Latenzen, während denen sich mehrere IRQs "ansammeln" können.
Zu deiner Ursprünglichen Frage: Ich würd hier das explizite "while(1);"
vorziehen, da explizit. Und wenn du willst kannst es auch in eine
eigene Funktion wie "freeze" oder was auch immer stecken.
ZUdem ist je nach AVR-Modell ein "cli + while(1);" nicht genug, weil der
Watchdog bedient werden will; ein freeze könnte also in etwa so
aussehen:
Vielen Dank allen für die Antworten.
Michael schrieb:> Normalerweise reicht es, den Port nur einmal auszuschalten. Aber da es> nichts kostet und man nie weiß, was EMV so für spielchen treibt, schadet> es auch nicht, die Abschaltung ständig zu wiederholen.
Ein guter Gedanke, werde das beherzigen.
Sina A. schrieb:> wenn es wirklich ein notaus ist, ist es wichtig, dass du garantieren> kannst, dass der notaus sofort triggert.
Die Überlastbedingung wird im Originalcode bereits in einer ISR
berechnet, aber nicht ausgewertet. Werde dies ändern. Somit ist
garantiert, dass der Notaus schnellstmöglich bei Überlast ausgelöst
wird. Manchmal sehe ich das Naheliegende nicht.
Johann L. schrieb:> ZUdem ist je nach AVR-Modell ein "cli + while(1);" nicht genug, weil der> Watchdog bedient werden will; ein freeze könnte also in etwa so> aussehen:
Muss mich mal in die ganze Watchdog-Thematik einlesen, habe ich bis
jetzt ignoriert.
Zum Thema Interrupt: Mit euren Anregungen und Fragen ist mir in den Sinn
gekommen, wie wir das während dem Studium (lang ist's her) auf dem
Motorola 68000 gelernt haben:
Dort kann man den Interrupts Priority Levels zuordnen und in der ISR
dann die Levels mit niedriger Priority disablen. Und vor dem Return
natürlich wieder enablen. Hat zu interresanten Debugging Marathons
geführt, wenn jemand vergessen hat die Interrupts wieder zu enablen. :-)
Die Idee das Programm zu verlassen ist unguenstigt. Was soll der
Programmcounter dann machen ?
Man kann auch in einen sicheren Zustand fahren und dort bleiben, ohne
das Programm zu beenden. Das Programm muss so gebaut seein, dass man es
nie verlaesst.
Dampf T. schrieb:> Die Idee das Programm zu verlassen ist unguenstigt. Was soll der> Programmcounter dann machen ?
man verlässt ja nur die main, nicht das Programm. Hinter der Main gibt
es auch eine endlosschleife. (zumindest bei der AVR-Toolchain)
Dres schrieb:> Somit ist> garantiert, dass der Notaus schnellstmöglich bei Überlast ausgelöst> wird.
Ich habe oft das Problem, daß die Überlasterkennung viel zu empfindlich
reagiert. Ich muß dann einen Timerinterrupt aufsetzen, der zyklisch
prüft, ob die Überlast eine minimale Zeit (z.B. 10ms) lang andauert.
Dres schrieb:> Dort kann man den Interrupts Priority Levels zuordnen und in der ISR> dann die Levels mit niedriger Priority disablen. Und vor dem Return> natürlich wieder enablen.
Warum das denn?
Wenn man Interrupt-Level zuordnen kann, dann macht die Hardware doch
alles von alleine.
Z.B. beim AT89C51 weise ich T1 Priorität 0 (höchste) zu und UART0
Priorität 3. Dann kann der T1 den UART-Interrupt unterbrechen, das
Enable-Bit braucht dazu nirgends angefaßt zu werden.
> Hinter der Main gibt es auch eine endlosschleife.
Ja, aber nur, wenn die main() Funktion nicht selbst eine Endlosschleife
ohne Abbruch-Bedingung oder break ist.
Der Compiler spart die 2 Bytes noch, wenn er kann.
Das fällt in die Kategorie "was man nicht wissen muss" :-)
Dres schrieb:> Mit der while(1)-Schlaufe weiss ich, dass nichts mehr passiert, aber es> scheint unsauber, da das Programm nie wircklich beendet ist.
Du willst dein Programm auch nicht beenden, sondern das System in einem
sicheren Zustand halten. Unter UNIX heißt die entsprechende Funktion
"panic()". :-)
> Mit return ist das Programm beendet, aber ich weiss eigentlich nicht,> wie der Kompiler das umsetzt. Ich nehme an, dass er ebenfalls in eine> Endlosschleife geht?
Das hängt vom Startup-Code ab. Niemand garantiert dir, dass deine
eingestellte Konfiguration so erhalten bleibt, der Controller darf auch
anfangen, auf einem Pin einen Hilferuf zu morsen.
Meine eigenen Routinen reagieren unterschiedlich:
- die CPU springt undefiniert mit aktiven Interrupts irgendwo hin
- Endlosschleife
- Reset
- blinkende LED
- Fehlermeldung auf UART oder Display (inklusive Mini-Treiber).
Alle Varianten haben ihre Berechtigung (ersteres war zwar
unbeabsichtigt, ist aber als Falle für Studenten trotzdem lehrreich und
bleibt daher so).
Stefan U. schrieb:>> Hinter der Main gibt es auch eine endlosschleife.> Ja, aber nur, wenn die main() Funktion nicht selbst eine Endlosschleife> ohne Abbruch-Bedingung oder break ist.
Du darfst (bei gcc/clang) beliebige Funktionen mit
__attribute__((noreturn)) dekorieren, dann spart sich der Compiler das
Aufräumen (und sichert die Register nicht).