Bin nun aus Langeweile dabei eine alte Taschenlampe umzubauen. Ehemals
Bleiakku ..., nun Hardware (NE555) alles fertig. Will die Steuerung aber
mit einem ATtiny85 vornehmen. Habe vor langer Zeit so etwas programmiert
(Z80 noch mit HEX-Code) und mit dem Versuchssteuerungen für
physiologische Experimente aufgebaut.
Sehe nun offensichtlich durch die IR-Funktionsweise des uC nicht durch.
Programmablauf ist ja relativ simpel. Power on schaltet Modus 1 ein.
Tasterdruck setzt Variable auf true und schaltet Modus weiter (4
Leuchtmodi).
Diese werden in der while Schleife ausgewertet (wenn true, nächster
Modus, sonst alter). Vor dem Verlassen der IR-Routine wird IR disabled
und nach dem neuen Modus wieder enabled. Die IR wird auch ausgeführt.
Mit der meiner While() komme ich nicht weiter.
Simulieren geht (Variable event & modus von Hand gesetzt). AT85 sitzt
auf Breadboard mit entsprechender Hardware zum testen.
Kann mir da jemand helfen. Kenne das Datenblatt mitterlerweile fast
auswendig und auch ein paar Bücher neben mir.
Den Skript habe gekürzt (Bibliotheken, Definition und Deklarationen)
denn der Compiler zeigt keine Fehler an. (Skript nochmal als Anhang).
Hoffe jemand gibt mir etwas Nachhilfe.
1
//Interrupt Service Routine for INT0
2
ISR(INT0_vect){
3
Button_event=true;
4
_delay_ms(10);// button debounce
5
GIMSK&=~(1<<INT0);// disable IR
6
//Test_LED_toggle(); // visual control toggle PORTB
7
//_delay_ms(20);
8
}
9
10
intmain(void)
11
{
12
// setting i/o ports
13
DDRB&=~(1<<PB2);// PB2 input - clear bit
14
PORTB|=(1<<PB2);// PB2 pull up
15
DDRB|=(1<<PB1);
16
DDRB|=(1<<PB4);// PB1 and PB4 output
17
PORTB&=~((1<<PB1)|(1<<PB4));// LED off
18
//PORTB |= (1<<PB1) | (1<<PB4); // LED on
19
20
Full_Light();
21
22
// setting interrupt
23
GIMSK|=(1<<INT0);// ext. interrupt enable
24
MCUCR|=(1<<ISC00);
25
MCUCR|=(1<<ISC01);// Trigger INT0 on rising edge
26
sei();
27
28
Light_mode=0;
29
Button_event=true;
30
31
while(1)
32
{
33
if(Button_event==true){
34
Button_event=false;
35
Light_mode++;
36
}
37
if(Light_mode>=4){
38
Light_mode=0;
39
}
40
while(Light_mode==0){
41
Full_Light();
42
GIMSK|=(1<<INT0);
43
sei();
44
}
45
while(Light_mode==1){
46
Half_Light();
47
GIMSK|=(1<<INT0);
48
sei();
49
}
50
while(Light_mode==2){
51
SOS();
52
GIMSK|=(1<<INT0);
53
sei();
54
}
55
while(Light_mode==3){
56
Flash_Ligt_1();
57
GIMSK|=(1<<INT0);
58
sei();
59
//Light_mode=0;
60
}
61
}
62
}
63
64
voidTest_LED_toggle(){
65
PORTB=~PORTB;
66
//_delay_ms(1000);
67
}
68
69
voidFull_Light(){
70
71
PORTB|=(1<<LED_Vcc_Warning);// red on blue 0ff
72
}
73
74
voidHalf_Light(){
75
PORTB|=(1<<LED_Light);// blue on red off
76
PORTB&=~(1<<LED_Vcc_Warning);
77
}
78
79
voidSOS(){
80
PORTB|=(1<<LED_Vcc_Warning);// rot blau an
81
}
82
83
voidFlash_Ligt_1(){
84
_delay_ms(2000);// blinc
85
PORTB|=(1<<LED_Light);
86
_delay_ms(500);
87
PORTB&=~(1<<LED_Light);
88
_delay_ms(500);
89
PORTB&=~(1<<LED_Vcc_Warning);
90
}
[Mod: c-Tags das nächste Mal bitte selber einfügen. Und längeren
Sourcecode als Anhnag posten]
Michael schrieb:> Sehe nun offensichtlich durch die IR-Funktionsweise des uC nicht durch.
IR? Infrarot? Oder eher Interrupt?
> Programmablauf ist ja relativ simpel. Power on schaltet Modus 1 ein.> Tasterdruck setzt Variable auf true und schaltet Modus weiter (4> Leuchtmodi).> Diese werden in der while Schleife ausgewertet (wenn true, nächster> Modus, sonst alter). Vor dem Verlassen der IR-Routine wird IR disabled> und nach dem neuen Modus wieder enabled. Die IR wird auch ausgeführt.> Mit der meiner While() komme ich nicht weiter.
Dein Programm ist nicht sonderlich sinnvoll.
> Den Skript habe gekürzt (Bibliotheken, Definition und Deklarationen)> denn der Compiler zeigt keine Fehler an. (Skript nochmal als Anhang).
Warum nicht den originalen Quelltext unverändert anhängen? Zu einfach?
Zu wenig fehleranfällig? OMG! Und wozu dann nochmal als Text im Beitrag?
> Hoffe jemand gibt mir etwas Nachhilfe.
Ja, lies was über Netiquette. Und Entprellung.
Tasten wertet man im Normalfall NICHT per externem Interrupt aus. Dann
braucht man dort auch keine komischen Delays.
Das macht man bestenfalls, wenn man sehr stromsparend mit dem [[Sleep
Mode]] arbeitet, was du hier aber nicht tust.
Deine Tastenauswertung und zentrale Programmlogik ist auch nicht
sonderlich brauchbar.
Falk B. schrieb:> Dein Programm ist nicht sonderlich sinnvoll.> Tasten wertet man im Normalfall NICHT per externem Interrupt aus.> Dann braucht man dort auch keine komischen Delays.
Für mich sieht das Programm durchaus sinnvoll und Prinzipiell
funktionsfähig aus.
Konzentriere dich auf das Problem, nicht auf den Programmierstil. Lass
die Kurve mal gerade sein. Jeder hat mal angefangen.
Michael schrieb:> Vermute, dass die IR da nicht reinkommt.
Schreibe vollständige Sätze. "Ich" ist kein Tabu-Wort.
Ich vermute das ebenfalls. Damit sind wir schon zu zweit, Grund genug,
genau das zu überprüfen. Was kam denn bei deinem Versuch mit
Test_LED_toggle() heraus?
Du könntest den Wert der Variable Button_event in main() kontrollieren,
indem du ihren Wert durch ein Blinkmuster anzeigst.
Ich nehme an, dass das Schlüsseldorf "volatile" vor "Button_event=true;"
helfen wird. Probiere das aus.
Steve van de Grens schrieb:> Michael schrieb:>> Mit der meiner While() komme ich nicht weiter.>> Was ist denn dein Problem mit der while Schleife?
Es sind Endlos-Schleifen:
while (Light_mode==0) {
Full_Light();
GIMSK |= (1<<INT0);
sei();
}
statt 'while' muss es 'if' heißen. :-))
Davon abgesehen ist das Programm verwirrend und viel zu kompliziert. In
diesem einfachen Fall braucht man gar keinen Interrupt. Man kann die
Taste in der Hauptschleife abfragen.
>> Es sind Endlos-Schleifen:> while (Light_mode==0) {> Full_Light();> GIMSK |= (1<<INT0);> sei();>
ja das ist eine Endlosschleife aber nur solange bis ein IR erfolgt
dachte ich. Oder liege ich da falsch?
Rolf schrieb:> Es sind Endlos-Schleifen:> while (Light_mode==0) {> statt 'while' muss es 'if' heißen. :-))
Stimmt, Adlerauge sei wachsam!
Änderungsvorschlag (zusätzlich zum Volatile):
1
while(1){
2
3
if(Button_event==true){
4
Button_event=false;
5
Light_mode++;
6
}
7
8
switch(Light_mode){
9
case4:{
10
Light_mode=0;
11
}
12
13
case0:
14
Full_Light();
15
break;
16
17
case1:
18
Half_Light();
19
break;
20
21
case2:
22
SOS();
23
break;
24
25
case3:
26
Flash_Ligt_1();
27
break;
28
29
default:
30
Light_mode=0;
31
}
32
}
Bitte entschuldige, falls da Syntaxfehler drin sind. Ich arbeite seit
mehr als einem Jahr mit anderen Programmiersprachen.
Den Teil "Light_mode++" könnte man in die ISR verschieben, dann entfällt
die Variable Button_event. Light_mode sollte müsste dann volatile sein.
Michael schrieb:> ja das ist eine Endlosschleife aber nur solange bis ein IR erfolgt> dachte ich. Oder liege ich da falsch?
Dein Fehler ist: Light-mode wird bei Tastendruck nicht geändert, weil
Michael schrieb:> Danke, das probiere ich gleich.
Sorry, lass den "case 4" weg. Ich habe vergessen, diese Zeilen zu
löschen. Was da passiert ist durch den default case abgedeckt. Ich
hoffe, das ist offensichtlich.
ja das war es, nun muss ich nur den Unterschied verstehen.
Danke Dir sehr. Mit dem Break kommt er wieder in die Endlosschleife, das
ist bei mir nicht so. Da hatte ich gedacht, dass er diese mit dem
IR-Request verlässt und mit der Whileschleife wieder anfängt.
Gruß Michael
p.s. benutze Atmel7 mit einem STK500-Clone
Steve van de Grens schrieb:> Michael schrieb:>> ja das ist eine Endlosschleife aber nur solange bis ein IR erfolgt>> dachte ich. Oder liege ich da falsch?>> Dein Fehler ist: Light-mode wird bei Tastendruck nicht geändert, weil>
1
>if(Button_event==true){
2
>Button_event=false;
3
>Light_mode++;
4
>}
5
>
>> außerhalb der "endlosen" while Schleife liegt.
das verstehe ich nicht. Der Code lautet:
while (1)
{
if (Button_event==true) {
Button_event=false;
Light_mode++;
}
Michael schrieb:> PORTB=~PORTB; /* Toggle PORTC */
1. der Tiny kennt keinen PortC
2. damit stirbt der Pullup ab, und danach ist nix mehr mit
kontrolliertem "rising"
Michael schrieb:> // PB4 - output> DDRB = (0<< PB4); // PB4 output
So wird kein Pin zum Output gemacht.
> Ich nehme an, dass das Schlüsseldorf "volatile" vor "Button_event=true;"> helfen wird. Probiere das aus.
Danke, das habe ich versucht, aber ergebnislos.
@Michael
Du scheinst die Vorstellung zu haben, mit einem Interrupt könne man
Schleifen verlassen. Nein, kann man nicht. Eine Schleife kann man nur
durch eine 'break'-Anweisung verlassen, oder dadurch, dass die
Schleifenbedingung nicht (mehr) erfüllt ist.
Arduino F. schrieb:> Michael schrieb:>> PORTB=~PORTB; /* Toggle PORTC */>> 1. der Tiny kennt keinen PortC>
ist Dir wohl entgangen, dass das ein Kommentar ist und in in der
Anweisung PORTB steht. Mit dem Rest kann ich folgen. Also C ist
bedeeutungsloser Schreibfehler. | ist Unterlassung aber unauffällig da
Port trotzdem getoggelt wird ein Pin als Ausgang geschaltet ist. What
now???
Rolf schrieb:> @Michael> Du scheinst die Vorstellung zu haben, mit einem Interrupt könne man> Schleifen verlassen. Nein, kann man nicht. Eine Schleife kann man nur> durch eine 'break'-Anweisung verlassen, oder dadurch, dass die> Schleifenbedingung nicht (mehr) erfüllt ist.
Ja, die hatte ich, dass war zumindest beim Z80 so. Kannst Du mir auch
erklären, warum das so ist???
Michael schrieb:> Kannst Du mir auch erklären, warum das so ist???
Eine Schleife kann von einem Interrupt lediglich unterbrochen werden,
aber sie kann von diesem Interrupt nicht abgebrochen werden.
> Ja, die hatte ich, dass war zumindest beim Z80 so.
Eigentlich auch nicht, aber: in welcher Sprache hast du den
programmiert?
Michael schrieb:> ist Dir wohl entgangen, dass das ein Kommentar ist und in in der> Anweisung PORTB steht.
Nein, mir ist da gar nichts entgangen!
Ich formuliere das mal so um, dass es jeder versteht:
> Kein Kommentar, ist besser, als ein falscher Kommentar!
Falsche Kommentare haben nur einen einzigen Zweck, Sinn oder Aufgabe,
sie sollen den Leser und vielleicht sogar den Autor selber ins Bockhorn
jagen.
Ich möchte nicht so verarscht werden, und ich möchte auch nicht dass
sich der TO so selber verarscht.
Michael schrieb:> | ist Unterlassung aber unauffällig da> Port trotzdem getoggelt wird ein Pin als Ausgang geschaltet ist. What> now???
Der Pullup wird auch abgeschaltet, und damit endet die kontrollierte ISR
Aufruferei.
Datt is now!
Steve van de Grens schrieb:> Rolf schrieb:>> Es sind Endlos-Schleifen:>> while (Light_mode==0) {>> statt 'while' muss es 'if' heißen. :-))>> Stimmt, Adlerauge sei wachsam!>
also mit if funktioniert das, wie gewünscht. Das bedeutet nun wirklich,
dass diese while-Schleifen nicht unterbrechbar sind. WEiß jemand warum
das so ist???
Danke Euch allen. Wünsche einen fleißigen Weihnachtsmann.
Michael
Michael schrieb:> Das bedeutet nun wirklich,> dass diese while-Schleifen nicht unterbrechbar sind.
Natürlich sind sie unterbrechbar!
break beendet eine Schleife
(allerdings nur von innen, nicht von außen)
Michael schrieb:> Kannst Du mir auch> erklären, warum das so ist???
Ja!
Weil C und C++ so definiert sind, wie sie definiert sind.
Es ist eine Spezifikation und kein "ich wünsch mir was"
> Der Pullup wird auch abgeschaltet, und damit endet die kontrollierte ISR> Aufruferei.> Datt is now!
Wenn Du meinst, teste doch mal auf Deinem Digi den Script. Da wirst Du
staunen.
Michael schrieb:> Das bedeutet nun wirklich, dass diese while-Schleifen nicht> unterbrechbar sind.
Nochmal, weil es offenbar untergegangen ist: klar können die jederzeit
**unterbrochen** werden, aber sie können eben durch diese
Unterbrechungen nicht **abgebrochen** werden.
> WEiß jemand warum das so ist???
Ja: weil es die Sprachdefinition so verlangt.
Und weil es zudem die alltäglich erlebte Logik nahelegt: ein "Interrupt"
ist eben nur eine "Unterbrechung" und kein "Teminator", der jede
aktuelle Tätigkeit abbricht. Nach einer "Unterbrechung" arbeitet der
Prozessor dort weiter, wo er unterbrochen wurde.
Wenn du Milch koscht und der Postbote klingelt an der Tür, dann holst du
schnellstmöglich das Einschreiben ab und siehst zu, dass du mit der
Milch weitermachen kannst. Denn sonst könnte ja der Postbote den Pudding
zur Nachspeise durch sein Klingeln vehindern.
Arduino F. schrieb:> break beendet eine Schleife
Sie könnten auch bei jedem Druchlauf ganz regulär abgebrochen werden,
wenn einfach die Schleifenbedingung nicht mehr zutrifft.
Michael schrieb:> Wenn Du meinst, teste doch mal auf Deinem Digi den Script. Da wirst Du> staunen.
Da gibts nichts zu testen!
Vielleicht solltest du nochmal ins Datenblatt schauen und die C
Operatoren = und ~ in der Sprachdoku nachlesen.
Michael schrieb:> PORTB=~PORTB;
Dort werden alle Bits im PORTB Register invertiert/getoggelt
Und damit ist der Pullup an PB2 auch aus.
Lothar M. schrieb:> Sie könnten auch bei jedem Druchlauf ganz regulär abgebrochen werden,> wenn einfach die Schleifenbedingung nicht mehr zutrifft.
Ich danke dir für die Korrektur, aber wenn du so bist, solltest du evtl
auch noch return erwähnen.
Harald K. schrieb:> Warum nur bereitet mir das ... Unbehagen?>> _delay_ms in einer ISR aufrufen? Warum nur?
Weil der Taster prellt. HIER ist das OK, denn der Controller hat so oder
so nix zu tu.
> Michael schrieb:>> Das bedeutet nun wirklich, dass diese while-Schleifen nicht>> unterbrechbar sind.> Nochmal, weil es offenbar untergegangen ist: klar können die jederzeit> **unterbrochen** werden, aber sie können eben durch diese> Unterbrechungen nicht **abgebrochen** werden.
ok, dass war eine falsche Schlussfolgerung von mir.
> ein "Interrupt"> ist eben nur eine "Unterbrechung" und kein "Teminator", der jede> aktuelle Tätigkeit abbricht. Nach einer "Unterbrechung" arbeitet der> Prozessor dort weiter, wo er unterbrochen wurde.
stimmt, war wieder mein Gedankenfehler
> Arduino F. schrieb:> Sie könnten auch bei jedem Druchlauf ganz regulär abgebrochen werden,> wenn einfach die Schleifenbedingung nicht mehr zutrifft.
Jetzt hab ich es gefressen. Meine While-Schleife wertet das Flag nicht
aus und verbleibt. Ich müsste also die Variable Modus in der ISR
inkrementieren, denn die wertet die While-Schleife aus.
Richtig???
Falk B. schrieb:> Harald K. schrieb:>> Warum nur bereitet mir das ... Unbehagen?>>>> _delay_ms in einer ISR aufrufen? Warum nur?>> Weil der Taster prellt. HIER ist das OK, denn der Controller hat so oder> so nix zu tu.
genauso sah ich das auch
Michael schrieb:> Jetzt hab ich es gefressen. Meine While-Schleife wertet das Flag nicht> aus und verbleibt. Ich müsste also die Variable Modus in der ISR> inkrementieren, denn die wertet die While-Schleife aus.>> Richtig???
16:45 so ist es, nun geht alles, wie ich es wollte.
Thread kann geschlossen werden.
Michael schrieb:>> @Michael>> Du scheinst die Vorstellung zu haben, mit einem Interrupt könne man>> Schleifen verlassen. Nein, kann man nicht. Eine Schleife kann man nur>> durch eine 'break'-Anweisung verlassen, oder dadurch, dass die>> Schleifenbedingung nicht (mehr) erfüllt ist.>> Ja, die hatte ich, dass war zumindest beim Z80 so. Kannst Du mir auch> erklären, warum das so ist???
Oh nein, das war nicht so. Und das war und ist todsicher bei keiner CPU
so. Es ist eine Eigenschaft der Programmiersprache.
Übrigens hast du gesagt, du habest Z80 "in HEX-Code" programmiert. Da
gibt es gar keine while-Schleifen und auch sonst keine Schleifen, die
der Prozessor als solche erkennen kann.
Arduino F. schrieb:> Dort werden alle Bits im PORTB Register invertiert/getoggelt> Und damit ist der Pullup an PB2 auch aus.
Daraus folgt aber nicht, dass die IR-Routine nicht aufgerufen wird.
Michael schrieb:> Jetzt hab ich es gefressen. Meine While-Schleife wertet das Flag nicht> aus und verbleibt.
Ja
> Ich müsste also die Variable Modus in der ISR> inkrementieren,
Nein. Du musst dein Software umstrukturieren.
Beitrag "Re: Interrupt ATtiny85 - Hilfe"
Arduino F. schrieb:> Wenn das Programm sonst nichts zu tun hat, dann brauchts auch keinen> Interrupt für einen Taster.
Doch, wenn man Strom sparen will und keinen extra Hauptschalter einbauen
will.
Michael schrieb:> Daraus folgt aber nicht, dass die IR-Routine nicht aufgerufen wird.
Habe ich auch nicht behauptet!
Das sind meien Aussagen dazu:
Arduino F. schrieb:> 2. damit stirbt der Pullup ab, und danach ist nix mehr mit> kontrolliertem "rising"Arduino F. schrieb:> Der Pullup wird auch abgeschaltet, und damit endet die kontrollierte ISR> Aufruferei.> Datt is now!
Da steht ausdrücklich nicht, dass keine rising ISR mehr kommen kann,
sondern dass du und der Taster damit die Kontrolle über den Zeitpunkt
verloren hast.
Natürlich kann ein floatender Pin jederzeit HIGH werden.
Arduino F. schrieb:>> Doch, wenn man Strom sparen will>> Das habe ich dem Code nirgendwo erkennen können.> Habe ich da was übersehen?
Ist auch nicht sichtbar, aber der OP bastelt an einer Taschenlampe.
Rolf schrieb:> Übrigens hast du gesagt, du habest Z80 "in HEX-Code" programmiert. Da> gibt es gar keine while-Schleifen und auch sonst keine Schleifen, die> der Prozessor als solche erkennen kann.
richtig, die mussten selbst gebaut werden. Denn einen Assembler
geschweige denn eine IDE mit einer höheren Progranmmiersprache hatte ich
nicht.
Arduino F. schrieb:> Da steht ausdrücklich nicht, dass keine rising ISR mehr kommen kann,> sondern dass du und der Taster damit die Kontrolle über den Zeitpunkt> verloren hast.>> Natürlich kann ein floatender Pin jederzeit HIGH werden.
Dann sind wir uns ja einig, dass die ISR-Routine funktioniert, wie
behauptet hatte, dass es prinzipiell io ist.
Falk B. schrieb:> Arduino F. schrieb:>> Wenn das Programm sonst nichts zu tun hat, dann brauchts auch keinen>> Interrupt für einen Taster.>> Doch, wenn man Strom sparen will und keinen extra Hauptschalter einbauen> will.
so ist es gedacht.
Falk B. schrieb:> Michael schrieb:>> Jetzt hab ich es gefressen. Meine While-Schleife wertet das Flag nicht>> aus und verbleibt.>> Ja>>> Ich müsste also die Variable Modus in der ISR>> inkrementieren,>> Nein. Du musst dein Software umstrukturieren.>> Beitrag "Re: Interrupt ATtiny85 - Hilfe"
Ja, das wars. In der IR-Routine die Variable noch inkrementiert und
schon geht alles. Ebenso wie die case-Variante als auch die if Variante.
Lothar M. (Firma: Titel) (lkmiller) (Moderator) hat mir auf die Sprünge
geholfen.
Michael schrieb:> Dann sind wir uns ja einig, dass die ISR-Routine funktioniert, wie> behauptet hatte, dass es prinzipiell io ist.
Wenn der Kontrollverlust für dich OK ist, wenn du das so beabsichtigt
hast, dann ja.
Was das Unterbrechen der while Schleife angeht, habt ihr die Frage von
Michael offenbar missverstanden. Ich zitiere nochmal den relevanten
Code:
1
ISR(INT0_vect){
2
Button_event=true;
3
...
4
}
5
6
intmain(void){
7
...
8
while(1){
9
10
if(Button_event==true){
11
Button_event=false;
12
Light_mode++;
13
}
14
15
...
16
17
while(Light_mode==0){
18
...
19
}
20
21
while(Light_mode==1){
22
...
23
}
24
25
while(Light_mode==2){
26
...
27
}
28
29
while(Light_mode==3){
30
...
31
}
32
}
33
}
Mit ungewollten "Endlosschleifen" meinte Rolf die inneren while
Schleifen für die vier Licht-Modi.
Michael ist der Meinung, dass die Schleifen nur solange laufen, wie der
jeweilige Licht-Modus gewählt ist. Sobald man den Licht-Modus ändert,
wird die gerade laufende while Schleife verlassen und die nächste while
Schleife für den nächsten Licht-Modus ausgeführt. Zumindest was das sein
Plan.
Der Knackpunt ist, dass jedoch nichts den Licht-Modus ändert, während
eine der vier inneren Schleifen läuft. Zwar gibt es eine ISR, die auf
Tastendruck reagiert. Diese ändert aber nur die Variable Button_event
und nicht Light_mode.
Wenn die ISR den Light_mode ändern würde, dann würde es wie gewünscht
funktionieren. Etwa so:
1
ISR(INT0_vect){
2
Light_mode++;
3
...
4
}
5
6
intmain(void){
7
...
8
while(1){
9
while(Light_mode==0){
10
...
11
}
12
13
while(Light_mode==1){
14
...
15
}
16
17
while(Light_mode==2){
18
...
19
}
20
21
while(Light_mode==3){
22
...
23
}
24
}
25
}
Die Variable, die innerhalb der ISR geändert wird und außerhalb der ISR
gelesen wird sollte volatile sein. Ansonsten kann es passieren, dass der
vom Compiler erzeugte Code die Variable in einem Register cached und
dann Änderungen im RAM durch die ISR gar nicht mit bekommt:
1
ISR(INT0_vect){
2
Light_mode++;
3
...
4
}
5
6
intmain(void){
7
...
8
9
R5=Light_mode
10
while(1){
11
while(R5==0){
12
...
13
}
14
15
while(R5==1){
16
...
17
}
18
19
while(R5==2){
20
...
21
}
22
23
while(R5==3){
24
...
25
}
26
}
27
}
Der Compiler macht das, weil Zugriffe auf Register schneller sind, als
Zugriffe auf RAM. Leider ist der Compiler so "dumm", nicht zu
berücksichtigen, dass Light_mode verändert wird während main() läuft. Er
liest die Speicherzelle nur einmal und benutzt dann wiederholt das
schnellere Register R5.
Volatile ist eine einfache Holzhammer Methode, den Compiler dazu zu
zwingen, jeden schreib- und Lesezugriff sofort im RAM auszuführen. Also
nicht in einem Register zu cachen.
Die Registernummer sei hier nur ein Beispiel. Der Compiler kann dafür
jedes beliebige freie Register verwenden. Man sieht das ggf. im
Assembler-Listing.
Harald K. schrieb:> Warum nur bereitet mir das ... Unbehagen?>> _delay_ms in einer ISR aufrufen? Warum nur?
Weil der Kommentar falsch ist.
Delay war noch nie eine gut funktionierende Entprellung und wird es auch
nie sein.
Peter D. schrieb:> Delay war noch nie eine gut funktionierende Entprellung> und wird es auch nie sein.
Das wird der Michael schon noch merken, wenn seine Projekte komplexer
werden. Genehmigt ihm die Zeit zum lernen, die er braucht.
Michael schrieb:> Ja, die hatte ich, dass war zumindest beim Z80 so. Kannst Du mir auch> erklären, warum das so ist???
Das bezweifle ich mal stark, daß Interrupts beim Z80 das blanke Chaos
hinterlassen.
Im Gegenteil, die CPU sichert den Status soweit, daß sich Seiteneffekte
auf die unterbrochenen Programmteile vollständig vermeiden lassen. Dazu
wurden auch PUSH/POP implementiert, damit keine Register zerstört
werden.
Nach einem Interrupt wird also genau da weiter gemacht, wo die
Unterbrechung erfolgte.
In C kann man jedoch mit setjmp/longjmp einen Abbruch erzwingen. Aus
guten Grund sind das aber mit Abstand die am seltensten benutzten
Funktionen, da man genau wissen muß, was man tut.
Steve van de Grens schrieb:> Leider ist der Compiler so "dumm", nicht zu> berücksichtigen, dass Light_mode verändert wird während main() läuft.
Das ist nicht "leider dumm", sondern das Konzept von C/C++: Möglichst
effizient sein.
Solche Veränderungen können ja auch in einer anderen Quelldatei
vorkommen. Um das sicher zu erkennen, müsste der Compiler grundsätzlich
immer die Inhalte aller Quelltexte beachten. Das wäre aufwendig und
würde trotzdem nicht immer gut funktionieren, weil der Compiler die
zeitlichen Abläufe nicht erkennen kann und deswegen auf Verdacht zu
vorsichtig sein müsste. Das Compilieren würde also unnötig langsam, und
das Compilat würde unnötig ineffektiv.
Außerdem kann der Compiler nur Quelltexte beachten, die er kennt. Die
von vorübersetzten Libs (womöglich in anderen Sprachen) kennt er aber u.
U. gar nicht.
Falk B. schrieb:>> Wenn das Programm sonst nichts zu tun hat, dann brauchts auch keinen>> Interrupt für einen Taster.> Doch, wenn man Strom sparen will und keinen extra Hauptschalter einbauen> will.
Dann würde ich in diesem Fall zwar einen Interrupt auf den Taster
setzen, aber die Interrupt-Routine leer lassen und alles in der
Hauptschleife machen:
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(true) { sei(); sleep_cpu(); doAll(); }
Wäre einfacher/übersichtlicher und kürzer.