Forum: Mikrocontroller und Digitale Elektronik Interrupt ATtiny85 - Hilfe


von Michael (oti)


Angehängte Dateien:

Lesenswert?

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
int main(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
void Test_LED_toggle() {
65
  PORTB=~PORTB;  
66
  //_delay_ms(1000);
67
}
68
69
void Full_Light() {
70
  
71
  PORTB |= (1 << LED_Vcc_Warning);          // red on blue 0ff
72
}
73
74
void Half_Light() {
75
  PORTB |= (1 << LED_Light);              // blue on red off
76
  PORTB &=~(1 << LED_Vcc_Warning);                          
77
}
78
79
void SOS() {
80
  PORTB |= (1 << LED_Vcc_Warning);          // rot blau an               
81
}
82
83
void Flash_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]

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

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.

von Steve van de Grens (roehrmond)


Lesenswert?

Michael schrieb:
> Mit der meiner While() komme ich nicht weiter.

Was ist denn dein Problem mit der while Schleife?

von Michael (oti)


Lesenswert?

Vermute, dass die IR da nicht reinkommt.

prinzipielle Funktionsweise der IR habe ich getestet mit (funzt auch).
Vielleicht hast Du, Steve, eine Idee?.

/*Interrupt Service Routine for INT0*/
ISR(INT0_vect)
{
  PORTB=~PORTB;    /* Toggle PORTC */
  _delay_ms(50);    /* Software debouncing control delay */
}

int main(void)
{
  //DDRB=0xFF;    /* Make PORTC as output PORT*/
   // PB2 - input
  DDRB &= ~ (1<< PB2);                            // PB2 input
  PORTB |= (1 << PB2);                            // Pull Up PIN7-PB2

  // PB1 - output
  DDRB = (0<< PB1);                              // PB1 output
  PORTB |= (1 << PB1);                            //PB1 (PIN6)

  // PB4 - output
  DDRB = (0<< PB4);                              // PB4 output
  PORTB &= ~(1 << PB4);                            //PB4 (PIN3)

  GIMSK = 1<<INT0;                              /* Enable INT0*/
  MCUCR = 1<<ISC01 | 1<<ISC00;                        /* Trigger INT0 on 
rising edge */
  sei();                                    /* Enable Global Interrupt 
*/

  while (1);
}

von Steve van de Grens (roehrmond)


Lesenswert?

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.

von Steve van de Grens (roehrmond)


Lesenswert?

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?

von Steve van de Grens (roehrmond)


Lesenswert?

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.

von Rolf (rolf22)


Lesenswert?

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.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

So könnte es funktionieren.

von Michael (oti)


Lesenswert?

>
> 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?

von Steve van de Grens (roehrmond)


Lesenswert?

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
        case 4: {
10
          Light_mode=0;
11
        }  
12
13
        case 0:
14
          Full_Light();
15
          break;
16
17
        case 1:
18
          Half_Light();
19
          break;
20
21
        case 2:
22
          SOS();
23
          break;
24
25
        case 3:
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.

von Michael (oti)


Lesenswert?

Danke, das probiere ich gleich.

von Steve van de Grens (roehrmond)


Lesenswert?

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
      }

außerhalb der "endlosen" while Schleife liegt.

von Steve van de Grens (roehrmond)


Lesenswert?

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.

von Michael (oti)


Lesenswert?

die funktioniert natürlich, Sonst hätte ich (hier hast Du Dein fehlendes 
ICH) nicht weitergemacht.

von Michael (oti)


Lesenswert?

ja ist logisch

von Michael (oti)


Lesenswert?

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

von Michael (oti)


Lesenswert?

> Ich nehme an, dass das Schlüsseldorf "volatile" vor "Button_event=true;"
> helfen wird. Probiere das aus.

probiere ich auch nochmal
Danke

von Michael (oti)


Lesenswert?

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++;
      }

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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.

: Bearbeitet durch User
von Michael (oti)


Lesenswert?

> Ich nehme an, dass das Schlüsseldorf "volatile" vor "Button_event=true;"
> helfen wird. Probiere das aus.
Danke, das habe ich versucht, aber ergebnislos.

von Rolf (rolf22)


Lesenswert?

@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.

: Bearbeitet durch User
von Michael (oti)


Lesenswert?

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???

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Reduziert, und getestet mit einem Digispark in der Arduino IDE.
Als C und nicht C++ kompiliert
1
#include <Arduino.h>
2
3
/*Interrupt Service Routine for INT0*/
4
ISR(INT0_vect)
5
{
6
  PINB  = (1 << PB1); // toggle
7
}
8
9
int main(void)
10
{
11
  PORTB |= (1 << PB2);    // Pullup
12
  DDRB  |= (1 << PB1);    // output
13
14
  GIMSK = 1<<INT0;              // Enable INT0
15
  MCUCR = 1<<ISC01 | 1<<ISC00;  // Trigger INT0 on rising edge
16
  sei();                        // Enable Global Interrupt 
17
18
  while (1);
19
}

von Michael (oti)


Lesenswert?

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???

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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!

: Bearbeitet durch User
von Michael (oti)


Lesenswert?

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

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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"

: Bearbeitet durch User
von Michael (oti)


Lesenswert?

> 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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> solltest du evtl auch noch return erwähnen.
... und konsequenterweise auch goto.

: Bearbeitet durch Moderator
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Lothar M. schrieb:
> auch goto

abort();

von Harald K. (kirnbichler)


Lesenswert?

Michael schrieb:
1
/*Interrupt Service Routine for INT0*/
2
ISR(INT0_vect)
3
{
4
  PORTB=~PORTB;    /* Toggle PORTC */
5
  _delay_ms(50);    /* Software debouncing control delay */
6
}

Warum nur bereitet mir das ... Unbehagen?

_delay_ms in einer ISR aufrufen? Warum nur?

von Falk B. (falk)


Lesenswert?

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.

von Michael (oti)


Lesenswert?

> 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???

von Michael (oti)


Lesenswert?

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

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Wenn das Programm sonst nichts zu tun hat, dann brauchts auch keinen 
Interrupt für einen Taster.
Polling wäre dann voll ausreichend.

von Michael (oti)


Lesenswert?

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.

von Rolf (rolf22)


Lesenswert?

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.

von Michael (oti)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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"

von Falk B. (falk)


Lesenswert?

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.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Falk B. schrieb:
> Doch, wenn man Strom sparen will

Das habe ich dem Code nirgendwo erkennen können.
Habe ich da was übersehen?

von Falk B. (falk)


Lesenswert?

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.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Falk B. schrieb:
> Ist auch nicht sichtbar,
Aha, ja.....

von Michael (oti)


Lesenswert?

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.

von Michael (oti)


Lesenswert?

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.

von Michael (oti)


Lesenswert?

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.

von Michael (oti)


Lesenswert?

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.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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.

von Steve van de Grens (roehrmond)


Lesenswert?

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
int main(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
int main(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
int main(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.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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.

von Steve van de Grens (roehrmond)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Rolf (rolf22)


Lesenswert?

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.

von Rolf (rolf22)


Lesenswert?

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.

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.