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]
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.
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.
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?
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.
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
@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.
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???
@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???
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.
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
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.
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 im Beitrag #7552539:
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.
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
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.
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.
Ü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.
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.
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.
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.
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.
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: