Hallo,
Mein Programmaufbau macht nicht das was es soll, um es mal einfach
auszudrücken.
Ich habe ein Lauflicht programmiert mit 10 LEDs von links nach rechts
und wieder zurück. Alles recht einfach gehalten.
Bin Zwar Anwendungsentwickler aber mit dem Controller habe ich meine
Probleme.
Das Lauflicht ansich funktioniert. Nun wollte ich noch mimttels zwei
Taster via Interrupt die Geschwindigkeit ändern aber genau das geht
nicht. Habe hier und da gesucht und mir auch etwas zusammengestrikt aber
ohne Erfolg.
1
/*
2
* GccApplication1.c
3
*
4
* Created: 2012-11-14 17:13:01
5
* Author: dsn
6
*/
7
8
#define F_CPU 3686400
9
#include<avr/io.h>
10
#include<util/delay.h>
11
#include<avr/interrupt.h>
12
13
intWaitInMS;
14
15
voidGetWaitTime()
16
{
17
for(inti=0;i<WaitInMS;i++)
18
_delay_ms(1);
19
}
20
21
intmain(void)
22
{
23
DDRB=0b111111;
24
DDRC=0b111111;
25
DDRD&=~(1<<DDD2)|(1<<DDD3);//setzt PD2 (INT0) und PD3 (INT1) auf Eingang
26
27
MCUCR|=(1<<ISC01)|(1<<ISC00)|(1<<ISC11)|(1<<ISC10);//(Ausloesen des Interrupts bei steigende Flanke an INT0 und INT1)
28
GICR|=(1<<INT0)|(1<<INT1);//Aktiviert den Pin PD2 (INT0) und den Pin PD3 (INT1) des Atmega8
29
30
WaitInMS=50;
31
32
sei();
33
34
while(1)
35
{
36
37
PORTB=0b100000;GetWaitTime();
38
PORTB=0b010000;GetWaitTime();
39
PORTB=0b001000;GetWaitTime();
40
PORTB=0b000100;GetWaitTime();
41
PORTB=0b000010;GetWaitTime();
42
PORTB=0b000001;GetWaitTime();
43
PORTB=0b000000;
44
45
PORTC=0b000001;GetWaitTime();
46
PORTC=0b000010;GetWaitTime();
47
PORTC=0b000100;GetWaitTime();
48
PORTC=0b001000;GetWaitTime();
49
50
PORTC=0b000100;GetWaitTime();
51
PORTC=0b000010;GetWaitTime();
52
PORTC=0b000001;GetWaitTime();
53
PORTC=0b000000;
54
55
PORTB=0b000001;GetWaitTime();
56
PORTB=0b000010;GetWaitTime();
57
PORTB=0b000100;GetWaitTime();
58
PORTB=0b001000;GetWaitTime();
59
PORTB=0b010000;GetWaitTime();
60
61
}
62
}
63
64
ISR(INT0_vect)
65
{
66
WaitInMS=WaitInMS+10;
67
}
68
69
ISR(INT1_vect)
70
{
71
WaitInMS=WaitInMS-10;
72
}
Wo liegt mein Fehler? Hab ich schon den falschen Ansatz?
Gruß
Daniel
Daniel Stratmann schrieb:> Das Lauflicht ansich funktioniert. Nun wollte ich noch mimttels zwei> Taster via Interrupt die Geschwindigkeit ändern aber genau das geht> nicht.
Schon falsch. Keine Taster an Interruptpins!
Ist geändert wenn es <= 0 ist dann wirds auf 10 zurück gesetzt. Ja
stimmt wohl, hab ich nicht berücksichtigt.
Warum keine Taster an Interrupts?
Welche Möglichkeit habe ich denn sonst hier?
Es läuft nun :)
Hab am Anfang den MCUCR und den GICR falsch gesetzt.
Da wäre aber gleich noch ein kleines Problem und noch die offene Frage
warum ich nicht Taster als interrups nutzen sollte?
möchte das Lauflicht nun so aussehen haben:
1000000001
0100000010
0010000100
0001001000
0000110000
1
PORTB=0b100000;
2
PORTC=0b001000;
3
GetWaitTime();
da läuchtet allerdings nur PORTC Pin 4 PORTB Pin 1 bleibt dagegen aus,
warum ist dem so? Es sind doch unterschiedliche Ports die ich dort
setze.
Daniel Stratmann schrieb:> (...) und noch die offene Frage> warum ich nicht Taster als interrups nutzen sollte?
Das weiß ich allerdings auch nicht.
Vielleicht mag uns "STK500-Besitzer (Gast)" aufklären?
Na dann bin ich ja halbwegs beruhigt, denn so einige Beiträge auf meiner
Suche beschäftigten sich mit Tastern an Interrupts.
Ist nichts besonderes da mit meinr Schaltung aber ich freu mir schon
irgendwie nen Ast ab. Immerhin habe ich das Board schon nen Jahr hier
rum liegen.
> PORTB = 0b100000;> PORTC = 0b001000;>da läuchtet
Da drehen sich mir gleich die Fussnägel hoch.
"Leuchtet" heisst das.
> allerdings nur PORTC Pin 4
Es ist PORTC3.
>PORTB Pin 1 bleibt dagegen aus,
Kein Wunder wenn du PORTB5 setzt.
>Bin Zwar Anwendungsentwickler
Für was?
>Ist geändert wenn es <= 0 ist dann wirds auf 10 zurück gesetzt. Ja
Zeig mir mal eine Anwendung für eine Zeit kleiner Null.
Daniel Stratmann schrieb:> Warum keine Taster an Interrupts?> Welche Möglichkeit habe ich denn sonst hier?
Eigentlich solltest du selber darauf kommen ;)
Aber ich versuch es mal zu erklären:
Wenn du deinen Taster nicht hardwareseitig mittels einem RC-Glied
entprellt hast, dann wird dir ein unentprellter Taster nicht nur einmal
in den Interrupt springen sondern öfters.
Das Resultat: Du zählst mehr als du wolltest
Deswegen macht man eine Tasterabfrage wenigstens über einen Timer.
Dieser wird z.B. aller 20ms angesprungen und du fragst da die Taste (den
Portpin) einfach ab.
Wenn du es noch besser machen willst, dann merkst du dir immer den
letzten Zustand deines Tasten-States. Und reagierst nur dann auf die
Taste/Tasten wenn der letzte Tasten-State auch gleich dem neuen jetzigen
State ist.
Peter Danegger (ich hoffe ich ihn richtig geschrieben) hat da einen
schönen code zu geschrieben den hier fast alle benutzen.
Beitrag "Re: Tasten entprellen - Bulletproof"
Und hier hat sich mal jemand mit der Erklärung versucht:
Beitrag "Re: Tasten entprellen - Bulletproof"
Gruß Steffen
Kein Taster am Interrupt
weil du die nicht vernünftig entprellt bekommst
Lauflicht mittels Delays
Ist schon mal der falsche Ansatz
Du musst weg von deiner PC-Denkweise. Was du brauchst ist eine
Event-basierte Denkweise. Und die führt bei Zeitsteuerungen praktisch
immer über einen Timer mit einer ISR. Und dort ist dann auch der Platz,
an dem man die Tasten pollt und entprellt.
Ich verstehe, dass man nicht alles gleichzeitig lernen kann und das man
mal irgendwo anfangen muss. Aber ausser für die ersten einfachen
Programmen, ist es so, dass _delay_ms NICHT die Lösung ist, sondern das
Problem. Praktisch jedes AVR-Programm hat immer einen Timer mitlaufen,
welcher den Zeittakt des Programms generiert. Und der Rest ist dann
einfach nur noch eventbasiertes Programmieren, wobei ein derartiger
Event dann der vom Timer verursachte Zeittakt ist.
Schreibfehler passieren manchmal schäm
PORTB ist komplett geschaltet und von PORTC die ersten 4
1
PORTB=0b100000;
2
GetWaitTime();
3
PORTC=0b001000;
4
GetWaitTime();
Somit leuchten nun beide nacheinander. Nehme ich den ersten GetWaitTime
wieder weg, so leuchtet nur PORTC3. Das wollte ich eigentlich damit
ausdrücken.
Es geht doch nur um die Absicherung 0 und kleiner 0, klar hätte auch
kleiner 1 schreiben können aber das macht hier doch nun wirklich keinen
Unterschied.
Hauptsächlich für Verwaltungssoftware.
und die Verwendung von int vergisst du gleich wieder.
Auf einem Rechner der Klasse eines AVR willst du genau Kontrolle über
die Bitzahlen.
uint8_t unsigned int mit 8 Bit (0 .. 255)
int8_t signed int mit 8 Bit ( -128 ... 127 )
uint16_t unsigned int mit 16 Bit (0 .. 65565)
int16_t signed int mit 16 Bit (-32768 ... 32767)
Daniel Stratmann schrieb:> Es geht doch nur um die Absicherung 0 und kleiner 0,
Ein derartiger Zeitraum kann sowieso nie negativ sein. Das macht
überhaupt keinen Sinn. Warum daher in erster Linie int?
Da ist ein unsigned int angebracht!
>> Somit leuchten nun beide nacheinander. Nehme ich den ersten GetWaitTime> wieder weg, so leuchtet nur PORTC3. Das wollte ich eigentlich damit> ausdrücken.
Eventuell ein Problem in der Hardware? Vielleicht sind zwei Leitungen
irgendwie verbunden?
>PORTB ist komplett geschaltet und von PORTC die ersten 4>> PORTB = 0b100000;> GetWaitTime();> PORTC = 0b001000;> GetWaitTime();>>Somit leuchten nun beide nacheinander.
Fein.
>Nehme ich den ersten GetWaitTime>wieder weg, so leuchtet nur PORTC3. Das wollte ich eigentlich damit>ausdrücken.
Das wär dann
PORTB = 0b100000;
PORTC = 0b001000;
GetWaitTime();
PORTB5 müsste trotzdem an sein.
Tut mir leid, aber kann ich nicht nachvollziehen
was du da treibst.
Kommen wir dann noch mal zu atomarem Zugriff auf 16 Bit Variablen
bei 8Bit uC die in Interrupts geändert werden.
Das wird in die Hose gehen:
for (int i = 0; i < WaitInMS; i++)
Das geht so nicht.
Und wieso ist i int? Kann dein Delay negativ werden?
Funktioniert nun. Der Chip saß nicht richtig...
Die Zahl WaitInMS kann negativ werden als reiner int definiert, logisch.
GetWaitTime habe ich mir aus irgend einem Forum kopiert. Ist natürlich
nun auch in int16_t geändert. Darum steht dahinter ja nun auch wenn <= 0
dann 10. Somit gibts da doch kein Problem.
Ich verstehe eure Beiträge ja schon aber das tut hier für das direkt
Ausführen nichts zur Sache.
Es ist immerhin mein erstes "Projekt" um mich mal eben damit vertraut zu
machen.
Zu den Tastern. Es handelt sich hier um das MyAVRBoard MK2 und aus den
Demo Dateien habe ich das mit den Tastern so übernommen. Das es nicht
die perfekte Lösung ist, ist doch erst einmal ein anderes Thema.
Habt ihr nicht einmal klein angefangen und einfach mal drauf los
geschrieben?
Aber noch einmal zu den Tastern, wenn man eine behauptung aufstellt wie
es STK500-Besitzer getan hat, sollte man auch gleich in dem Moment
natürlich auch anfüge warum dem so ist. So steht der kleine Anfänger mit
noch einem Fragezeichen auf dem Kopf da.
Habe es ja nun auch begriffen das diese entprellt werden müssen.
Zu dem Delay, es handelt sich hier doch nur um ein Lauflicht aus 10
LEDs. Ich finde es hier nun nicht so problematisch mit dem Delay. Es tut
doch nun genau das was es soll.
Möchte hiermit nicht aufmüpfig klingen, da ich ja Hilfe benötige und
nehme diese auch gerne an.
Edit: Was ich später einmal haben möchte, ist ine kleine Schaltung mit
IR-Empfänger und einer angeschlossenen Relaiskarte zur Steuerung meiner
Wohnwand. Mit ist auch bewußt das es bis dahin noch ein weiter weg ist
aber das ist mal mein ziel für den Anfang.
Daniel Stratmann schrieb:> Zu dem Delay, es handelt sich hier doch nur um ein Lauflicht aus 10> LEDs. Ich finde es hier nun nicht so problematisch mit dem Delay. Es tut> doch nun genau das was es soll.
Deine Argumentation ist schon klar. Wir alle verstehen, dass man mal
irgendwo anfangen muss.
versuch doch einfach mal mehrere Lauflichtmuster zu machen, die du zu
jedem beliebigen Zeitpunkt(!) umschalten kannst. Denn genau da bricht
dann dieser naive Ansatz das erste mal zusammen.
> Möchte hiermit nicht aufmüpfig klingen, da ich ja Hilfe benötige und> nehme diese auch gerne an.
Na ja, ich dachte eigentlich, dass du als Anwendungsentwickler dafür ein
gewisses Verständnis hast und das man bei deiner Vorbildung da dann auch
in schnelleren Schritten vorgehen kann. Da hab ich mich wohl geirrt.
Es ist schon ok, wenn du übst und es ist auch klar, dass Rom nicht an
einem Tag erbaut wurde. Wenn du mitnimmst, dass diese Art der
Programmierung nicht der Weisheit letzter Schluss ist und du im
Hinterkopf behältst, dass man das in einem realen Programm anders macht,
ist das für mich ok.
Denn dein nächster Punkt ist ein ganz anderer.
Das komplette Setzen eines Ports auf ein Butmuster ist im Allgmeinen
nicht das was du tun willst. Hier bei deinem Luaflicht ist das noch ok,
weil da sowieso nur LEDs involviert sind. Wenn du aber ein Blinklicht an
einem Portpin brauchst, oder von mir aus auch 2 Blinklichter mit
unterschiedlicher Frequenz und an einem 3. Portpin hängt eine Sirene,
dann wirst du schnell in unübersichtliche Programm laufen, wenn du
ständig den Port komplett beschreibst.
D.h. dein nächster Punkt lautet:
Einzelbitoperationen an einem Port
Bit setzen (am Beispiel Pin 3)
PORTB |= ( 1 << PB3 );
Bit löschen
PORTB &= ~( 1 << PB3 );
Abfragen, ob Bit gesetzt ist
if( PIND & ( 1 << PD4 ) )
> GetWaitTime habe ich mir aus irgend einem Forum kopiert. Ist natürlich
nun auch in int16_t geändert. Darum steht dahinter ja nun auch wenn <= 0
dann 10. Somit gibts da doch kein Problem.
Doch.
Du verschenkst völlig unnötig ein Bit.
Deine maximale Wartezeit ist ca 32 Sekunden. Sie könnte aber auch 64
Sekunden sein, wenn du nur beherzigen würdest, dass ein Delay nicht
negativ sein kann und du es daher mit einem unsigned Wert zu tun hast.
Und in weiterer Folge geht es gar nicht so sehr um den Delay an sich.
Den den Delay wirst du in ein paar Tagen sowieso nicht mehr benutzen. Es
geht darum, dass du akzeptierst und verstehst, dass die Wahl des
richtigen und angemessenen Datentyps wichtig ist. Auf einem PC kann man
sich da ein paar Freiheiten erlauben. Auf einem AVR kann das aber enorme
Unterschiede machen.
Karl Heinz Buchegger schrieb:> versuch doch einfach mal mehrere Lauflichtmuster zu machen, die du zu> jedem beliebigen Zeitpunkt(!) umschalten kannst. Denn genau da bricht> dann dieser naive Ansatz das erste mal zusammen.
Das ist schon passiert. Habe in einem Switch nun 3 verschiedene
Variationen und es läuft natürlich die gesamte Routine erst durch bevor
es umspringt.
Karl Heinz Buchegger schrieb:> Na ja, ich dachte eigentlich, dass du als Anwendungsentwickler dafür ein> gewisses Verständnis hast und das man bei deiner Vorbildung da dann auch> in schnelleren Schritten vorgehen kann. Da hab ich mich wohl geirrt.
Das würde ich so nicht unterschreiben. Es ist schon ein gravierender
Unterschied zwischen einem Programm auf dem PC und eines auf einem
Controller. Davon ab ist C nicht wirklich die Sprache die ich spreche,
ist aber ein anderes Thema. :) Dies hat auch weniger mit Verständnis und
der Geschwindigkeit zu tun.
Vor dem Problem mit der Einzelbitoperation stand ich vor ein paar
Stunden auch schon. Da ich ja im aktuellen fall die letzten beiden Pins
des PortC nicht so nutzen kann.
Es war eben frei nach dem Motto anschließen, ein paar Zeilen schreiben
und schauen was dabei raus kommt. Bei einem Problem mal eben Google
angeschmissen und geschaut wie es andere gemacht haben und von da kommt
dann eben auch solche Dinge wie eben mein Umweg mit der Wait Schleife.
Da denkt man sich ja auch im ersten Moment super es klappt und freut
sich. Ich empfinde das als das "Hello World".
Aus meiner normalen Programmierung ist es halt so jeden möglichen Fall
abzufangen.
Wenn die Operation lautet Zeit = Zeit - Wert das in dieser
Rechenoperation nun die Zeit auch 0 und negative Zahlen vorkommen
könnten. Das ein Delay nicht negativ werden kann ist mir shr wohl
bewußt. Hatte ja am Anfang geschrieben, das ich dies vergessen hatte.
Ich hätte auch schreiben können solange Zeit - 10 > 0 ist fürhe diese
Operation aus. Das Ergebnis bleibt aber jedoch das selbe ich prüfe ob
der Wert im positiven Bereich liegt.
Karl Heinz Buchegger schrieb:> Es> geht darum, dass du akzeptierst und verstehst, dass die Wahl des> richtigen und angemessenen Datentyps wichtig ist. Auf einem PC kann man> sich da ein paar Freiheiten erlauben. Auf einem AVR kann das aber enorme> Unterschiede machen.
Genau das meine ich ja, man kann das nicht so vergleichen Programmieren
auf einem PC Systen und die des Controllers.
Daniel Stratmann schrieb:> Wenn die Operation lautet Zeit = Zeit - Wert das in dieser> Rechenoperation nun die Zeit auch 0 und negative Zahlen vorkommen> könnten.
Aber nur dann, wenn du die Operation auch ausführst
1
if(Zeit>Wert)
2
Zeit=Zeit-Wert;
3
else
4
Zeit=0;
jetzt kann nichts 'negatives' mehr rauskommen. Nie. In keinem einzigen
Fall.
In diesem Fall also:
nicht im Nachhinein ein fehlerhaftes Ergebnis korrigieren, sondern von
vorne herein feststellen ob die Operation überhaupt zulässig ist und nur
dann durchführen, wenn sie möglich ist. ---> kein Mensch ist mehr
darauf angewiesen, dass Zeit ein signed Wert ist.
Ich komme auch aus der PC-Programmierecke, beschäftige mich seit 3
Jahren hin und wieder (also soweit meine 3-jährige Tochter mir als
Hausmann das erlaubt ;-)) mit µCs und mit diesem Forum, und wurde
bezüglich der Nichtverwendung von Interrupts noch nicht überzeugt.
Ein Polling, wie es hier immer wieder beschrieben wird nimmt einem die
Möglichkeit, den µC mit einem Tastendruck aus dem Tiefschlaf zu
erwecken.
Das mit dem Entprellen geht meiner Meiner Meinung nach auch mit
Interrupts.
Um das zu demonstrieren, habe ich diesen Thread als Anlass genommen, ein
kleines Programm zu schreiben, das den Einsatz des INT0 am ATmega8
sowohl für eine einzelne Taste als auch für eine kleine 3*3
Matrixtastatur implementiert.
Ich denke, ich habe den Code ausreichend kommentiert, so dass ich mich
in der Beschreibung auf den Kern beschränken werde. (Was ich nicht
kommentiert habe, ist der peripher benötigte rudimentäre UART-FIFO, weil
der trivial ist.)
Es wird auf Drücken und Loslassen reagiert, für das Drücken wird der
ISC-Modus 0 ("The low level of INT1 generates an interrupt request")
genutzt, so dass der µC während des Wartens auf einen Tastendruck in den
Tiefschlaf geschickt werden kann, und für das Loslassen wird der
ISC-Modus 3 ("The rising edge of INT1 generates an interrupt request")
genutzt.
Herzstück und recht knapp gehalten ist natürlich die INT-ISR. Sie
deaktiviert als erstes den INT0-Interrupt, damit das Prellen keine
weiteren Interrups auslösen kann, und erlaubt dann als höfliche ISR
wieder andere Interrupts. Dann wird ein Timer gestartet, der für eine
weitere Verarbeitung nach Ablauf der Prell-Zeit in der Timer-ISR sorgt.
Abhängig davon, ob ein Drücken- oder Loslassen-Interrupt behandelt wird,
werden hier die entsprechenden User Callback Funktionen
VerarbeiteTastendruck() oder VerarbeiteLoslassen() aufgerufen. Die
Callback-Funktionen sollten also "schnell" sein, d.h. keine
Warteschleifen, Delays oder Ähnliches enthalten.
Es gibt ein Flag namens bWarteTastendruck das gesetzt ist, wenn
einTastendruck erwartet wurde, und gelöscht, wenn auf das Loslassen der
Taste gewartet wurde.
In der Timer-ISR wird dann erst der Timer selbst deaktiviert und das
Flag bWarteTastendruck auf den jeweils anderen Wert gesetzt. Abhängig
von bWarteTastendruck wird der entsprechend andere Interrupt Sense
Control Modus ausgewählt, wenn der Status des Eingangspins noch dem
entspricht. Ansonsten, also wenn der Eingangspin nicht der Erwartung
entspricht, wird davon ausgegangen, dass der Anwender die Taste
schneller losgelassen bzw. wiederholt gedrückt hat, als als Prell-Zeit
konfiguriert ist, und die entsprechende Callback-Funktion wird aus der
Timer-ISR heraus (statt aus der INT-ISR) aufgerufen.
Die Erweiterung um die Möglichkeit, Matrix-Tastaturen einlesen zu
können, ist sehr einfach. Die entsprechende User Callback Funktion
TastaturMatrixEinlesen() wird aus der Timer-ISR nach Ablauf der
Prellzeit, wenn die Taste noch gedrückt ist (also im Normalfall)
aufgerufen. Wenn der User die Matrix-Taste schneller als die Prell-Zeit
wieder losgelassen hat, wird der Tastendruck ignoriert.
Die entsprechenden Code-Fragmente sehen so aus:
1
ISR(TASTE_INTERRUPT_VEKTOR)
2
{
3
// Würde gerne Interrupts einschalten, ohne ständig aufgerufen zu werden:
4
DeaktiviereTasteInterrupt();
5
PrellenAbwartenTimerStarten();
6
7
if(bWarteTastendruck)
8
{
9
VerarbeiteTastendruck();
10
}
11
else
12
{
13
VerarbeiteLoslassen();
14
}
15
}
16
17
ISR(TIMER0_OVF_vect)
18
{
19
--nWarteZaehler;
20
if(!nWarteZaehler)
21
{
22
TCCR0=(0<<CS00);// Timer deaktivieren
23
if(bWarteTastendruck)
24
{
25
// Der Tastendruck wurde verarbeitet und die Taste ist wahrscheinlich noch gedrückt.
26
bWarteTastendruck=0;
27
if(!(TASTE_PIN&(1<<TASTE_BIT)))
28
{
29
#ifdef MATRIXKBD
30
TastaturMatrixEinlesen();
31
#endif
32
SetzeInterruptBeiFlankeNullNachEins();// Interrupt beim loslassen
33
}
34
else
35
{
36
// Der unwahrscheinliche Fall ist eingetreten, dass der User innerhalb 100ms losgelassen hat,
37
// was wir nicht per Interrupt mitbekommen haben, da INTx während der Prellphase deaktiviert ist.
38
// Das könnte noch prellen. Also noch eine Warterunde, bis SetzeInterruptBeiPegelNull()
39
// aufgerufen wird.
40
// Dass im Falle einer Matrixtastatur eine zu schnell gedrücke Taste nicht erkannt wird, ist
41
// die günstigste Variante.
42
PrellenAbwartenTimerStarten();
43
VerarbeiteLoslassen();
44
}
45
}
46
else
47
{
48
// Das Loslassen wurde verarbeitet und die Taste ist wahrscheinlich noch losgelassen.
49
bWarteTastendruck=1;
50
if(TASTE_PIN&(1<<TASTE_BIT))
51
{
52
SetzeInterruptBeiPegelNull();// Interrupt beim Drücken
53
}
54
else
55
{
56
// Der unwahrscheinliche Fall ist eingetreten, dass der User innerhalb 100ms wieder gedrückt hat,
57
// was wir nicht per Interrupt mitbekommen haben, da INTx während der Prellphase deaktiviert ist.
58
// Das könnte noch prellen. Also noch eine Warterunde, bis SetzeInterruptBeiFlankeNullNachEins()
59
// aufgerufen und ggf. die Matrix-Tastatur eingelesen wird.
60
PrellenAbwartenTimerStarten();
61
VerarbeiteTastendruck();
62
}
63
}
64
}
65
}
Ich würde mich freuen, wenn sachlich darüber diskutiert wird. :-)
Ich habe mir nicht deinen ganzen Post angesehen.
Man kann natürlich einen externen Interrupt dazu verwenden, einen Taster
daran zu hängen. Sofern man dessen Prellen auch unterdrückt (was im
ersten Post nicht beachtet wurde).
Wenn man dann aber sowieso mit einem Timer arbeitet, kann man auch
gleich ein Polling damit betreiben und sich solch umständliche
Geschichten wie das Umschalten der Reaktionsflanke der Interruptquelle
umzuschalten, sparen.
Siehe Peter Danneggers Entprellroutine.
Vielleicht war meine Antwort auf das Eingangsposting etwas schoff,
sollte aber zur Selbstreflexion anregen.
Ausserdem hatte ich keine Lust zum x-ten Mal diese Problematik zu
rezitieren.
Das Thema wurde hier schon bis zum Erbrechen diskutiert => mal die
Suchfunktion des Forums benutzen?!
STK500-Beistzer schrieb:> Wenn man dann aber sowieso mit einem Timer arbeitet, kann man auch> gleich ein Polling damit betreiben und sich solch umständliche> Geschichten wie das Umschalten der Reaktionsflanke der Interruptquelle> umzuschalten, sparen.Philipp K. schrieb:> Ein Polling, wie es hier immer wieder beschrieben wird nimmt einem die> Möglichkeit, den µC mit einem Tastendruck aus dem Tiefschlaf zu> erwecken.STK500-Beistzer schrieb:> Ich habe mir nicht deinen ganzen Post angesehen.
Offensichtlich nicht mal den ersten Absatz!
Philipp K. schrieb:> STK500-Besitzer schrieb:>> (Gast)>> Feigling!
Und was bringt es, wenn ich mich hier mit meinem richtigen Namen
anmelde?
Die Lösung würde das nicht verbessern. Und das Geschwafel würde mich
immer noch nerven. Das liegt vermutlich an meinem ADHS...
Ausserdem gibt es eine praktikable Lösung (siehe Peter Danneggers
Abfrageroutine). Den Interrupt zum Aufwecken des Controllers benutzt
sogar Atmel in einer Application Note zum Them Matrix-Tastatur - Ist
also keine Neuheit.
STK500-Besitzer schrieb:> Die Lösung würde das nicht verbessern. Und das Geschwafel würde mich> immer noch nerven. Das liegt vermutlich an meinem ADHS...
Ach so, der Herr hat ADHS. Das erlaubt es ihm natürlich, andere zu
beleidigen. Schade, dass bei meinem ADHS keiner das Verständnis
aufbringt, das Du erwartest.
> Ausserdem gibt es eine praktikable Lösung (siehe Peter Danneggers> Abfrageroutine).
Den Nachteil der Methode hatte ich genannt. Darauf bist Du nicht
eingegangen, weil Du es gar nicht gelesen hattest. Da kann ich nichts
für!
Ich habe mir Peter Dannegers Routinen angesehen, und sie glänzen
dadurch, dass sie besonders effizient, maschinennah und platzsparend
programmiert sind, aber nicht mit Einfachheit.
STK500-Beistzer schrieb:> umständliche Geschichten wie das Umschalten der Reaktionsflanke der
Interruptquelle ..
Was ist daran umständlich? Das würde jeder mittelmäßige C-Programmierer
beim Lesen des Codes sofort verstehen. Über die Funktionsweise von den
Danneger-Routinen wurde im genannten Thread intensiv herum diskutiert.
> Den Interrupt zum Aufwecken des Controllers benutzt> sogar Atmel in einer Application Note zum Them Matrix-Tastatur - Ist> also keine Neuheit.
Jetzt widersprichst Du Dich aber. Zuerst behauptest Du, ohne auf die
Geschichte mir dem Power Down Mode einzugehen, dass man Interrupts nicht
zur Tastenabfrage benutzt, dann zitierst Du Atmel, dass die dazu eine
Application Note haben. Willst Du damit sagen, dass die von Atmel
Vollpfosten sind?
Ich habe übrigens gar nicht behauptet, dass es neu oder etwas besonderes
ist.
Schade, dass http://www-maintenace.atmel.com gerade icht läuft, sonst
würde ich gerne mal die AppNote lesen.
mawin, cyblord und nun auch STK500-Besitzer. Langsam wird es hier immer
ungemütlicher für mich. Das Schweigen der anderen werte ich mal als
Zustimmung. - Ich glaube, es ist besser, wenn ich Euch erst mal eine
Weile in Ruhe lasse. Das Forum frisst eh zu viel Zeit!
Woow, was man so als Antwort auf eine recht simple Frage bekommt, ist
schon beeindruckend.
Leute, Daniel wollte doch nur ein Taster via IRQ abfragen und kein
Atomkraftwerk steuern. Die Hinweise auf die APP-Notes oder
Hardware-Design Finessen wollte Daniel auch nicht wissen. Ein Lauflicht
steuern, darum geht es und nicht ein in Echtzeit gesteuerter Roboter -
wo bleibt die Verhältnismäßigkeit bei den Lösungen ?
@Daniel
Dir dürfte wohl kaum entgangen sein, dass dieses kleine Problem mit dem
ewig prellenden Tasten,Lösungen unterschiedlicher Art auf den Plan
rufen. Nun kommt es im wesentlichen darauf an, wo Du am besten ansetzten
kannst oder möchtest. Die Hardware-Lösung sieht ein RC-Glied am Taster
vor. Sowas sieht man schon mal an einem Controller-Board und wird beim
ziehen des Reset benutzt wird. Im einfachsten Fall habe ich auch schon
mal nur ein kleinen Kondensator parallel zu Taster gelegt. Das
fnktioniert auch - ist aber ohne "Längswiderstand zum Taster" nicht ganz
"Ohne".
Die Software-Lösung lässt sich mit Hilfe des Timers umsetzten. Leider
hat man den nicht immer zur Verfügung da er für andere Aufgaben benötigt
wird.
Option B ist, den IRQ einfach nach erstem Auslösen einfach zu blockieren
und innerhalb der ISR ca. 20ms Zeit zu vergeuden.
ISR(INT0_vect, ISR_BLOCK)
{
delay_ms (20);
....
}
Diese Lösung ist Quick and Dirty - funktioniert in Deinem unkritischen
Fall ausreichend gut.
In einer ISR macht man eigentlich so viel wie nötig ist, um die Laufzeit
so kurz wie möglich zu halten. Das delay_ms in der ISR ist eine Krücke
und wiederspricht der Forderung nach kurzer Laufzeit der ISR, ist aber
in deinem Fall völlig unkritisch, und darum geht es.
Globale Variablen verändert in einer ISR kann zu unschönen
Seiteneffekten führen. Diese werden grundsätzlich Volatile definiert,
damit die CPU die Variablen vor manipulation immer erst einmal liest.
Leider reicht diese Methode nur bei 8-Bit Variablen. Sobald ein
Variablen-Typ breiter ist, kann es passiern, dass die Variable in der
MainLoop beschrieben wird und gleichzeitig ein IRQ auftritt, in dessen
ISR die Variable erneut gelesen werden muss. Der Schreibvorgang des
kompletten Inhalts wurde aber zuvor in der Mainloop noch nicht
vollstandig abgeschlossen - z.B. nur 8Bit statt der 16Bit (oder
breiter). Damit dürfe die ISR einen Variableninhalt verarbeiten der
ungültig ist. Um dass zu verhindern gibt es die Funktion
"ATOMIC_BLOCK(type)" und die wird in <util/atomic.h> ausführlich
beschrieben. Bitte sehe sie dir mal an. Mich hat es Stunden gekostet bis
ich in Erwägung gezogen habe, das dieses Situation überhaupt eintreffen
könnte.
Ich hoffe dass es Dir einwenig weiterhelfen wird. Das Thema "Tasten
entprellen" lässt sich auf unterschiedliche Arten lösen. Es kommt immer
auf den Anspruch und der Verhältnismäßigkeit an. Die beste Lösung ist,
dem IRQ-PIN kein Prellen anzubieten. Aufwändig mit FlipFlop oder
Register, weniger Auwändig mit RC-Glied, und preiswert mit Software /
Timer.
Gerhard Wesser schrieb:> Woow, was man so als Antwort auf eine recht simple Frage bekommt, ist> schon beeindruckend.> Leute, Daniel wollte doch nur ein Taster via IRQ abfragen und kein> Atomkraftwerk steuern.
Ohne jetzt den anderen vor den Kopf stoßen zu wollen oder mich als
unfähig zu lernen darzustellen ist es richtig. Ich wollte nur mal eben
schnell einen Taster Abfragen. Ich hab es mir von woanders kopiert und
stand vor dem Problem das dieses nicht funktioniert.
Karl Heinz Buchegger schrieb:> Einzelbitoperationen an einem Port>> Bit setzen (am Beispiel Pin 3)>> PORTB |= ( 1 << PB3 );>> Bit löschen>> PORTB &= ~( 1 << PB3 );>> Abfragen, ob Bit gesetzt ist>> if( PIND & ( 1 << PD4 ) )
Das habe ich natürlich auch schon umgesetzt. Macht das schreiben schon
deutlich einfacher und man muß nicht ewig rumrechnen. Ist schon ein
Sprung nach vorne gewesen. Das ich mit meinem aktuellen Konstrukt nicht
mine Wohnwand steuern kann ist mir ebenfalls klar. Da muß halt noch
etwas Einarbeitung nötig sein, das wird aber sicher auch schon.
Finde es aber schön das man mich direkt auf den richtigen Weg leiten
will, auch wenn es noch keine AKW Steuerung ist. ;) Die Masse an
Informationen ist dennoch sehr enorm.
----- Snippy ---
> Ohne jetzt den anderen vor den Kopf stoßen zu wollen oder mich als> unfähig zu lernen darzustellen ist es richtig. Ich wollte nur mal eben> schnell einen Taster Abfragen. Ich hab es mir von woanders kopiert und> stand vor dem Problem das dieses nicht funktioniert.
---- Snippy ---
Finde es aber schön das man mich direkt auf den richtigen Weg leiten
> will, auch wenn es noch keine AKW Steuerung ist. ;) Die Masse an> Informationen ist dennoch sehr enorm.
Ich selber tendiere auch ehr dazu, einfach umfassend informieren zu
wollen. Das führt aber dazu, dass der Fragende einfach zu viel lernen
muss um mit den Antworten etwas anfangen kann. Letztenendes bleibt die
eigentliche Frage unbeantwortet oder versteckst sich in der gegebenen
Informationsfülle. Wer solche Fragen stellt, ist eindeutig noch nicht in
der Lage, beurteilen zu können, welche Lösung für welchen Fall
geeigneter ist und welche nicht. Da Hilft die "Beste Lösung" nicht
wirklich weiter.
Ich versuche pragmatisch zu bleiben und gebe noch ein paar Rand-Tipp's
die das etwas vereinfachen. Danach sollte der Fragende mal selber
probieren.
Diese Thread ist aber auch noch in einigen Jahren verfügbar. Das
bedeutet dass man mit Hilfe der Foren-Such-Funktion auch noch Info
recherchieren kann. Das Hilft derzeit evtl. nicht weiter, aber Dritte
sind möglicherweise schon weiter und finden Info in diesem Thread für
Ihr Problem.
Hier sind wirklich viele gute Leute mit weitgehender Hilfsbereitschaft.
Deren Wissen und Bereitschaft zu Helfen sollte man durch zuvor
durchgeführten eigenen recherechen - hier oder Google in der
Fragestellung einfließen lassen. Ich habe schon wiklich kniffelige
Problemstellungen durch einen Kontakt aus diesem Forum erfolgreich lösen
können und man lernt erstaunliche Dinge dazu.
Leider ist es aber auch nicht selten, dass auf eine Frage recht dumme
und einfältige Beiträge kommen. Deren Fragestellung lässt schnell
durchblicken dass es nicht ums Thema ging, sondern um der
Selbstdarstellung. Andere recherchieren vor Fragestellung nicht selber ;
frei nach dem Motto: lass nicht mich suchen sondern andere. Die ernten
auch nicht besonders viel Lob. :-)
Die Lösungsansätze die möglich sind, sind dir vorgstellt und und kannst
Du ans Werk gehen und testen. In den Datenblättern steht viel - die
Erata dazu sind durchaus lesenswert. Wenn man nicht weiter kommt hilft
manchmal das AVRGCC Manual oder die Doku und SOurcen der AVRLIB. Klasse
Lösungen findet man zu Themen wie RS232, RS485, SPI, LCD (CHar) hier und
bei Google von einschlägig bekannten Programmierern. Peter Flury, Peter
Danegger, sowie Jörg Wunsch,DL8DTL (bei Compiler oder Library Fragen ein
klasse Adresse)
Ob du mit dem dann erworbenen Wissen nun ein Atomkraftwerk sicher
steuern kannst, kann ich nicht beantworten. :-)
Viel SPass beim Basten / Programmieren
Erstmal abwesend schrieb:> Ach so, der Herr hat ADHS. Das erlaubt es ihm natürlich, andere zu> beleidigen. Schade, dass bei meinem ADHS keiner das Verständnis> aufbringt, das Du erwartest.
Nope. ADHS erlaubt mir nicht, andere Mensch zu beleidigen. Dafür sorgt
meine schlechte Erziehung und die damit verbundenen schlechten
Umgangsformen.
Erstmal abwesend schrieb:> Jetzt widersprichst Du Dir aber. Zuerst behauptest Du, ohne auf die> Geschichte mir dem Power Down Mode einzugehen, dass man Interrupts nicht> zur Tastenabfrage benutzt, dann zitierst Du Atmel, dass die dazu eine> Application Note haben. Willst Du damit sagen, dass die von Atmel> Vollpfosten sind?
Nein, natürlich nicht.
Nur wird dort die Interrupt-Geschichte dazu verwendet, um den Controller
zu wecken. Mehr wird da nicht über externe Interrupts gemacht.
Einfacher wäre es meiner Meinung nach (die AN habe ich jetzt mehr ganz
genau im Kopf), einfach den Timer aufgrund des externen Interrupts zu
starten und dann die Entprellroutine "wie immer" per durchlaufendem
Timer anzutriggern.
Den Timer kann man natürlich auch einfach durchlaufen lassen.
Je nach Sleep-Mode interessiert sich der Controller gar nicht dafür...
Wenn man einen Timer in einem sinnvollen Intervall Interruptsauslösen
lässt, kann man ganz wunderbar neben dem Entprellen auch noch andere
sinnvolle Sachen machen lassen. Aber Timer an, Timer aus,... ist ne
ziemliche Ressourcenverschwendung (meine Meinung nach).
> Leider hat man den nicht immer zur Verfügung da er für andere> Aufgaben benötigt wird.
Schlechtes Argument.
Denn es ist nicht verboten in einer Timer-ISR mehrere Dinge zu
bearbeiten. Und gerade bei der Tastenentprellung ist man sehr flexibel.
Ob die ISR alle 5ms oder alls 30ms aufgerufen wird, oder irgendein Wert
zwischen den beiden, spielt praktisch gesehen für die Funktionsfähigkeit
so gut wie keine Rolle. Hat man einen Timer laufen, dann kann man die
PeDa Entprellung praktisch immer in irgendeinener Form mit dazunehmen.
Und hat man keinen Timer laufen, dann erübrigt sich das ganze Argument.
Aber um das mal abzukürzen und nicht immer gegen derartige Lösungen
1
ISR(INT0_vect,ISR_BLOCK)
2
{
3
delay_ms(20);
4
....
5
}
argumentieren zu müssen (in der sowieso wieder mal ein Teil fehlt, denn
nur weil man in einer ISR einen delay macht, wird ja nicht das Interrupt
Flag bei einem Preller nicht nochmal gesetzt)
PeDas Code macht, reduziert auf eine einzige Taste nichts anderes als:
1
uint8_tkey_state;
2
uint8_tkey_counter;
3
volatileuint8_tkey_press;
4
5
ISR(...Overflow...)
6
{
7
uint8_tinput=KEY_PIN&(1<<KEY0);
8
9
if(input!=key_state){
10
key_counter--;
11
if(key_counter==0xFF){
12
key_counter=3;
13
key_state=input;
14
if(input)
15
key_press=TRUE;
16
}
17
}
18
else
19
key_counter=3;
20
21
}
22
23
uint8_tget_key_press()
24
{
25
uint8_tresult;
26
27
cli();
28
result=key_press;
29
key_press=FALSE;
30
sei();
31
32
returnresult;
33
}
Das ist, in Langform, das, was die PeDa Entprellung bei nur 1 Taster
macht. Ein Zähler wird von 3 heruntergezählt, solange sich der
Tastenzustand vom letzten entprellten Zustand unterscheidet. Und wenn
der Zähler unterläuft, dann wird bei gedrückter Taste ein entsprechender
Tastendruck in key_press registriert.
Der einzige Unterschied: PeDas Code macht das für 8 Tasten gleichzeitig
und sein Code für 8 Tasten ist compiliert kürzer als diese Langform für
lediglich 1 Taste, bei gleichzeitig mehr Funktionalität. Denn ein
Autorepeat ist in dieser Langform noch gar nicht eingebaut. Und
spätestens dann, wenn da eine 2-te Taste dazukommt, ist der
SRAM-Speicherverbrauch in der Langform auch noch höher.
Und allen die immer rufen "das ist so kompliziert, das versteht keiner",
möchte ich zurufen: Könnt ihr eine Wurzel-Funktion sqrt() schreiben, die
ähnlich effizient arbeitet wie die, die ihr in der math-Lib benutzt?
Wenn nein - eigentlich müsstet ihr dann eurer eigenen Argumentation nach
euch selbst eine sqrt() Funktion schreiben. Das wär nur konsequent.
Karl Heinz Buchegger schrieb:> Schlechtes Argument.
Deine Aussage ist nicht allgemeingültig.
Manchmal legt die Aufgabe fest, welche Resourcen zur Verfügung stehen
und welche nicht. Keine Timer mehr verfügbar, kein ISR zum
Tastenentprellen. Eleganz, Flexibilität und die eigenen Vorzügen haben
nicht immer Projektpriorität.
Ich schrieb "manchmal", was impliziert, dass es nicht immer so sein
muss.
Den Timer einzusetzten ist auf jeden Fall eine saubere und flexible
Lösung - dem stimme ich zu.