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?
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
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.
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.
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)
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.
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.
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?
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!
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.
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
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!
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.
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?
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.
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.
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?
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
:-)
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: