Forum: Mikrocontroller und Digitale Elektronik Sofortiges Verlassen einer Schleife mit Interrupt?


von H. G. (ledi)


Lesenswert?

Hallo!

Ich habe das Problem, das ich aus einer Schleife (siehe Code) beim 
Auslösen eines Interrupts nicht sofort herauskomme.
Der Interrupt wird bei Tastendruck sofort ausgelöst, aber die Schleife 
wird zuerst abgearbeitet. Wie kann ich die Schleife sofort verlassen?
1
else if (Taste == 11)                   
2
{  
3
      R=0; B=0; 
4
      unsigned char i;
5
      for (i=0; i<=254; i++)
6
      {  
7
        OCR1SB = valuetable[B+i];              // PSCOUT00 blue
8
        OCR1SA = valuetable[R+i];              // PSCOUT00 red
9
        _delay_ms(20);
10
      }
11
      
12
      R=0; B=255; i=0; 
13
      for (i=0; i<=254; i++)
14
      {  
15
        OCR1SB = valuetable[B-i];              // PSCOUT00 blue
16
        OCR1SA = valuetable[R+i];              // PSCOUT00 red
17
        _delay_ms(20);
18
      }
19
20
      R=0; B=0; i=0; 
21
}

von Volker Z. (vza)


Lesenswert?

Schleifen werden in C mit break verlassen.
Ich glaube aber nicht das das dein Problem (auf dauer) beheben wird.

Hier zu fehlen einfach zufiele Informationen.

Volker

von H. G. (ledi)


Lesenswert?

Das ist meine Interrupt-Routine.
Hier wird das FIFO eines Funkmoduls ausgelesen und die gedrückte Taste 
ermittelt.

in "main" soll für jede Taste (Taste 1 bis 12) eine eigene Routine 
ablaufen. Sobald wiederrum eine Taste gedrückt wird, löst ein ext. 
Interrupt den Int.2 aus, die neue Taste wird erkannt und in "main" soll 
die Routine für die aktuell erkannte Taste durchgeführt werden.
1
ISR(INT2_vect)    // Int.2 on PB5
2
{
3
  Payload_RX(0x61, 0x00);  // Read FIFO payload
4
5
  Taste = SPDR;
6
  Flush_RX();    // clear RX-FIFO
7
}

von Karl H. (kbuchegg)


Lesenswert?

Heimo G. schrieb:

> in "main" soll für jede Taste (Taste 1 bis 12) eine eigene Routine
> ablaufen. Sobald wiederrum eine Taste gedrückt wird, löst ein ext.
> Interrupt den Int.2 aus, die neue Taste wird erkannt und in "main" soll
> die Routine für die aktuell erkannte Taste durchgeführt werden.

komplett falscher Ansatz.

Schon die for-Schliefen, die einen kompletten Zyklus durch den Farbraum 
machen, sind schon vom Ansatz her falsch.

Du bist immer noch der 'sequentiellen Schleifenprogrammierung' 
verhaftet, wenn du schon längst auf 'Eventgetriggertes Programmieren' 
umsteigen müsstest.

Es gibt nur 1 Schleife, die ständig läuft und die ist die Endlosschleife 
in main!
Innerhalb dieser Schleife wird laufend gecheckt was es als nächstes zu 
tun gibt. Dann wird 1(!) kurzer Schritt der nächsten Aufgabe gemacht (zb 
um 1 Farbe weiterschalten) und weiter gehts in der Haupt-Endlosschleife.
Deine Tastendrücke steuern dann lediglich, wie die Weiterschaltung zur 
nächsten Farbe zu geschehen hat (wenn überhaupt, weiss ja nicht was du 
sonst noch so an Tasten hast). Aber auf keinen Fall gibt es da eine 
For-Schleife, die erst abgearbeitet werden muss, ehe der nächste 
Tastendruck Auswirkungen zeigen kann.

von Uwe .. (uwegw)


Lesenswert?

Ist Taste als volatile deklariert?

Bei jedem Schleifendurchlauf müsste zusätzlich abgefragt werden, ob 
Taste noch den richtigen Wert hat. Oder besser noch ein Flag, das in der 
ISR gesetzt wird, in der Schleife abgefragt und dann zurückgesetzt wird. 
Dann bleibst du maximal 20ms zu lange in der Schleife hängen...

(ich habe das Gefühl, dass sich das ganze ohne _delay_ms() deutlich 
besser lösen ließe, wenn die Zeiten über einen weiteren Timer erzeugt 
werden)

von H. G. (ledi)


Lesenswert?

OK!
Aber wie soll ich (ohne die for-Schleifen) eine automatische 
Farbmischung  (bis eine andere Taste gedrückt wird) realisieren?

von Karl H. (kbuchegg)


Lesenswert?

Heimo G. schrieb:
> OK!
> Aber wie soll ich (ohne die for-Schleifen) eine automatische
> Farbmischung  (bis eine andere Taste gedrückt wird) realisieren?

Ich weiß jetzt nicht was deine anderen Tasten machen.
Aber du könntest ja zb einen 'Job' erfinden, der durch mehrere Werte 
charakterisiert wird:
nämlich ersten, dass der Job überhaupt aktiv ist, d.h. dass ein 
Durchlaufen der Farbtabelle stattfindet.
nämlich zweitens, in welcher Richtung das jetzt stattfindet (steigen die 
Indizes oder fallen sie)
und drittens: welches der aktuell angezeigte Eintrag (oder der nächste 
anzuzeigende) der Farbtabelle ist.


D.h. das erkennen des Tastendrucks für Taste 11, setzt die initialien 
Werte und deine Hauptschleife sieht so aus

1
   while( 1 ) {
2
3
      ....
4
5
     if( ColorWheelRunning ) {
6
       // zeige den nächsten Eintrag an
7
       OCR1SB = valuetable[ B + actEntry ];
8
       OCR1SA = valuetable[ R + actEntry ];
9
10
       // bestimmen welches der nächste Eintrag sein wird
11
       // dabei aufpassen, dass an den Enden (255 bzw. 0) die Richtung
12
       // gewechselt werden muss: aus steigenden Indexnummern werden
13
       // fallende und umgekehrt
14
       if( Increasing ) {
15
         if( actEntry == 255 ) {
16
           Increasing = FALSE;
17
           actEntry = 254;
18
         }
19
         else
20
           actEntry++;
21
       }
22
23
       else {
24
         if( actEntry == 0 ) {
25
           Increasing = TRUE;
26
           actEntry = 1;
27
         }
28
         else
29
           actEntry--;
30
       }
31
32
       delay_ms( 20 );
33
     }
34
35
     ....
36
   }


solange ColorWheelRunning auf TRUE steht, wird immer immer wieder 
sukzessive der nächste Eintrag aus der Farbtabelle angezeigt. Bei jedem 
Durchlauf durch die zentrale while Schleife die jeweils nächste. Sobald 
ColorWheelRunning auf FALSE gesetzt wird (zb in der Auswertung, dass 
eine Taste gedrückt wurde) hört dieser Durchlauf sofort auf.

Mit dem _delay_ms( 20 ) bin ich noch unglücklich, eigentlich sollte das 
über einen Timer geregelt werden, aber getreu dem Grundsatz: nicht 
zuviele Änderungen auf einmal, kann das momentan auch so bleiben, zumal 
20ms nicht die Welt sind.

von Falk B. (falk)


Lesenswert?

Siehe Multitasking

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Die richtigen Ansätze wurden oben ja schon genannt.

Es gibt eine Quick & Dirty Lösung, die allerdings bis zu ein 
_delay_ms(20) durchwitschen lässt (wenn der for-Block mal angefangen 
hat) und den Restcode für diese Taste im if-Block abarbeitet.

Du setzt in der ISR ein Flag (volatile) und prüft die Schleifenzähler 
wie bisher gegen die Schleifenobergrenze und gegen das Flag.

Als Flag kannst du die schon vorhandene Variable Taste benutzen.

1
      for (i=0; i<=254 && Taste==11; i++)
2
...
3
      for (i=0; i<=254 && Taste==11; i++)
4
 }

von H. G. (ledi)


Lesenswert?

Stefan B. schrieb:
.......
> Als Flag kannst du die schon vorhandene Variable Taste benutzen.
>
>
>
1
>       for (i=0; i<=254 && Taste==11; i++)
2
> ...
3
>       for (i=0; i<=254 && Taste==11; i++)
4
>  }
5
>

OK!
Deine Lösung funktioniert! Vor allem habe ich hier nicht viel zu ändern.

Jetzt hat sich aber auch schon ein neues Problem ergeben!

Ich möchte z.B. mit der Taste 12 fünf verschiedene Sequenzen ablaufen 
lassen.
Ist Taste == 12 und das 1.mal gedrückt --> Sequenz 1
Ist Taste == 12 und das 2.mal gedrückt --> Sequenz 2
.
.
Ist Taste == 12 und das 5.mal gedrückt --> Sequenz 5

Da ich bei Tastendruck einen Low-Level-Interrupt auslöse (Bei 
Tastendruck alle 40ms ein Int.) kann ich in der ISR keinen 
Tastendruckzähler machen.

Wie kann ich das in der Routine unter main machen?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Heimo G. schrieb:
> Wie kann ich das in der Routine unter main machen?
Indem du kapierst, da nicht jeder "Wunsch" sich in ein belibieges 
Programmkonstrukt integrieren läßt!

von Stefan B. (stefan) Benutzerseite


Lesenswert?

1
main()
2
{
3
  while(1)
4
  {
5
    static uint8_t Taste_12_repeat = 0;
6
    if ( Taste == 1 )
7
    {
8
      Taste_12_repeat = 0;
9
    } 
10
    else if ( Taste == 12 )
11
    {
12
      Taste_12_repeat += 1;
13
      switch( Taste_12_repeat )
14
      {
15
        case 1: do_sequence_1();
16
                break;
17
        ...
18
      }
19
      Taste = 0;
20
    }
21
  }
22
}

Es werden allerdings beim Hochzählen der Sequenzen die Sequenzen auch 
ausgeführt.

Wenn das nicht gewünscht ist, muss man anderes programmieren z.b. 
relativ einfach mit einer speziellen Betätigungstaste zum Einleiten oder 
Ausleiten eines Multikommandos.

von oldmax (Gast)


Lesenswert?

Hi
Nun, du mußt abfragen, ob du im Eingang eine Änderung hast... Taster mit 
Interrupt ist keine gute Lösung. Ich frage solche Eingänge grundsätzlich 
in Polling in einer eigenen Routine ab.
Eingang lesen und mit dem Abbild vorangegangener Daten mit Exclusiv Oder 
verknüpfen. Ist eine Änderung aufgetreten, dann sind die geänderten Bits 
gesetzt. Nun das Ergebnis aus Exclusiv Oder mit den neuen Daten verunden 
und du hast die Flanke von 0 nach 1. Verundest du das Ergebnis aus 
Exclusiv -Oder mit den alten Daten hast du die Flanke von 1 nach 0.
Danach die neuen Daten in die Ablage alt schreiben
In Assembler sieht's so aus
1
 Read_Io:
2
   In   Reg_1, Port_x    ; 
3
   Push Reg_1            ; merken
4
   LDS  Reg_2, Old_In
5
   EOR  Reg_2, Reg_1     ; Ergebnis in Reg_ 1 geänderte Bits
6
   Push Reg_2            ; Änderungen merken
7
   AND  Reg_2, Reg_1     ; Ergebnis Bitänderung von 1 nach 0
8
   STS High_to_Low, Reg_2 ; Diese Bits dienen als Flankenmerker und
9
                          ; werden nach Bearbeitung zurückgesetzt
10
   POP Reg_2              ; Ergebnis aus Exklusiv Oder wieder holen
11
   LDS Reg_1, Old_in
12
   AND Reg_2, Reg_1       ; Ergebnis Bitänderung von 0 nach 1
13
   STS Low_To_High, Reg_2 ; Bits zur Bearbeitng ablegen
14
   POP Reg_1
15
   STS Old_In, Reg_1      ; gelesene neue Daten ablegen
16
RET
Diese Routine rufst du in deiner Main-Loop bei jedem Durchlauf auf. 
Danach testest du die Flankenbits und bearbeitest das Ereignis. 
Anschließend löscht du das Flankenbit, denn du willst ja zum Beispiel 
nur einen Counter hoch zählen.
Ich hab hier mal auf die Entprellroutine verzichtet, da ja nur das 
Prinzip deutlich werden sollte. Natürlich kannst du die entprellten 
Eingänge auch in eine Variable schreiben und diese satt dem Zugriff auf 
den Port benutzen.
Gruß oldmax

von H. G. (ledi)


Lesenswert?

Hallo Karl Heinz!

Ich denke, ich werde doch nicht drumherum kommen, deine Lösung zu 
verwenden.

Aber bevor ich damit loslege, möchte ich mein Vorhaben im Überlick 
darstellen und dich bitten, einen Vorschlag zu machen, wie ich am Besten 
an die Programmierung der Sache herangehen soll.

Für eine RGB-Hintergrundbeleuchtung möchte ich folgendes realisieren.
(Hardware ist bereits gemacht)
Mit 12 Eingabetasten erfolgt die Ansteuerung per Funk. Auf der 
Empfängerseite erhalte ich den Wert der gedrückten Taste (1...12) inkl. 
einen Interrupt (über den IRQ-Pin des Funkmodules). Bei gedrückter Taste 
alle 35ms einen Interrupt.

Folgendes möchte ich steuern:
Taste     Funktion
1         Startsequenz (irgendeine RGB-Farbmischung)
2         Heller
3         Dunkler
4         Geschwindigkeit der Farbmischung >>
5         Geschwindigkeit der Farbmischung <<
6         Stop (Szene anhalten)
7         nächste Szene (1 bis 5)
8         Zugabe Farbton Rot
9         Zugabe Farbton Grün
10        Zugabe Farbton Blau
11        Zugabe Farbton Gelb
12        Ein / Aus

Schon mal Vielen Dank für deine Hilfe!

von Karl H. (kbuchegg)


Lesenswert?

Heimo G. schrieb:
> Hallo Karl Heinz!
>
> Ich denke, ich werde doch nicht drumherum kommen, deine Lösung zu
> verwenden.

Denk drann:
Es geht ums Prinzip, nicht so sehr um die konkrete Implementierung die 
ich dir da oben angeboten habe.

Das Prinzip lautet: Bei jedem Durchlauf durch die zentrale 
while-Schleife immer nur 1 Schritt machen. Wenn mehrere Schritte zur 
Erfüllung einer AUfgabe notwendig sind, dann müssen die eben 
hintereinander bei mehreren Durchläufen gemacht werden. Dazu muss im 
schlimmsten Fall ein Durchlauf Informationen (in Variablen) für den 
nächsten Durchlauf hinterlassen.

> Folgendes möchte ich steuern:
> Taste     Funktion
> 1         Startsequenz (irgendeine RGB-Farbmischung)
> 2         Heller
> 3         Dunkler
> 4         Geschwindigkeit der Farbmischung >>
> 5         Geschwindigkeit der Farbmischung <<
> 6         Stop (Szene anhalten)
> 7         nächste Szene (1 bis 5)
> 8         Zugabe Farbton Rot
> 9         Zugabe Farbton Grün
> 10        Zugabe Farbton Blau
> 11        Zugabe Farbton Gelb
> 12        Ein / Aus

Ja, geht wunderbar.
Deine Schaltung ist in einem bestimmten Zustand (festgelegt durch die 
Variablen). Beim Drücken einer Taste wechselt sie in einen anderen 
Zustand und als Folge davon werden die farbbestimmenden Variablen 
verändert.
Bei jedem Durchlauf durch die while(1) Schleife wird abgefragt in 
welchem Zustand die Schaltung ist und eine entsprechende Aktion 
ausgeführt. Aber immer nur 1 Aktion, die zeitlich auch nicht zu lange 
sein soll, damit das ganze auf Benutzereingaben nicht zäh reagiert.

Kein Problem.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger schrieb:

> Kein Problem.


Oder doch?

Was genau stellst du dir unter einer "Sequenz" vor?
Was bedeutet es, wenn eine Sequenz 'rötlicher' präsentiert werden soll?

von Tobi (Gast)


Lesenswert?

Ich verstehe das so, dass eine Sequenz einfach eine folge von RGB Werten 
ist, welche in einem bestimmten Takt angezeigt werden sollen. In diesem 
Fall würde ich den Takt mit einem Timer generieren lassen.

Die einkommenen Daten würde ich nicht per externen Interrupt 
verarbeiten, sondern einfach per "Daten Empfangen" Interrupt. Dort 
einfach entsprechend ein paar Variablen ändern. Z.B. für die Auswahl 
einer Sequenz eine Variable, die in der Hauptschleife abgefragt wird, 
für mehr Farbe (z.B. Rot) einfach eine Variable inkrementieren, welche 
auf den entsprechenden RGB Wert draufaddiert wird.

von H. G. (ledi)


Lesenswert?

Ja, eine Sequenz ist eine Mischung aus den RGB-Anteilen wobei sich die 
Farbanteile überlagern und so unterschiedliche Farben entstehen sollen. 
Das Ganze soll sich dann in einer Art "Endlosschleife" solange 
wiederholen, bis erneut eine Taste gedrückt wird.

von H. G. (ledi)


Lesenswert?

Hallo Karl Heinz!

So ganz komme ich mit deinem Beispiel nicht klar. Kannst du mir das mit 
einem kleinen Beispiel (z.B. Taste 11) noch näher erläutern? Wie bzw. wo 
wird increasing definiert?

von Karl H. (kbuchegg)


Lesenswert?

Heimo G. schrieb:
> Hallo Karl Heinz!
>
> So ganz komme ich mit deinem Beispiel nicht klar. Kannst du mir das mit
> einem kleinen Beispiel (z.B. Taste 11) noch näher erläutern? Wie bzw. wo
> wird increasing definiert?

Die Definition der benutzen Variablen (mit sinnvollen Datentypen) hab 
ich dir überlassen.

Ich arbeite gerne so, dass ich zunächst den Algorithmus bzw. das 
Programm schreibe und mich erst mal nicht um die Definition von 
Variablen kümmere sondern nur um die Logik.

Ist dann alles fertig, dann seh ich nach welche Variablen noch nicht 
definiert sind und definiere sie nach. Ich brauch mir nur ansehen wie 
sie benutzt werden und kann dann entscheiden, welchen Datentyp ich dafür 
brauche.

Wie wird Increasing benutzt?
Es wird auf TRUE / FALSE abgefragt, bzw. es wird TRUE / FALSE 
zugewiesen. Also reicht ein uint8_t dafür aus.

Vergessen kann ich dabei nichts, denn das findet dann schon der Compiler 
:-)

von H. G. (ledi)


Lesenswert?

Super! Das habe ich jetzt hinbekommen! (siehe Code)

Wie kann ich nun mit der gleichen Taste auf die nächste Sequenz 
schalten?
D.h:
Taste 11 = das 1.mal gedrückt --> 1. Sequenz
Taste 11 = das 2.mal gedrückt --> 2. Sequenz usw...

Das Problem ist, das ich in der Schleife keine Zählvariable setzen kann, 
da der Tastenwert solange 11 ist, bis eine andere Taste gedrückt wird?

Wie kann ich das lösen?






Hier mein 1. Versuch (Eine Farbmischung von Rot und Blau:
1
if( Taste == 11 ) 
2
    {
3
    OCR1SA = valuetable[ B +actEntry_B ];
4
    OCR0SA = valuetable[ R +actEntry_R ];
5
6
      if ( Increasing_R == 1)
7
      {     
8
        if( actEntry_R == 255) 
9
        {
10
          if( actEntry_B == 255)
11
          {
12
            Increasing_B = 0;
13
            Increasing_R = 0;
14
          }
15
          else
16
            actEntry_B++;
17
          }
18
          else
19
            actEntry_R++;
20
       }
21
      
22
        else 
23
        {
24
            if( actEntry_R == 0 ) 
25
            {
26
                 Increasing_R = 1;
27
                 actEntry_R = 1;
28
            }
29
            else 
30
            {
31
                 actEntry_R--;
32
                 if ( actEntry_R == 200 )
33
                      Increasing_R = 1;
34
            }      
35
        }
36
        _delay_ms( 20 );
37
    }

von H. G. (ledi)


Lesenswert?

Niemand eine Idee?

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.