Forum: Mikrocontroller und Digitale Elektronik Tastenentprellen während externen Interrupt - ATmega16


von Alex P. (alex2203)


Lesenswert?

Guten Abend Forengemeinde,

ich mach zur Zeit meinen Techniker. Im Mikrocontrollerunterricht stellte 
uns nun unser Lehrer folgendes Problem:

Ihr habt nen Taster der eine LED schalten (toggeln) soll. Dieser Taster 
ist am Externen Interrupt 0 angeschlossen. Wie kann man softwareseitig 
verhindern, dass der Taster nachprellt und somit die LED mehrfach 
schaltet?

Die Lösung lautet wohl cli(); als ersten Befehl in die ISR zu schreiben. 
Erst danach den togglebefehl für die LED. (Wir verwenden das STK 500 mit 
ATmega16)

Ich kann es nicht so recht glauben und hoffe hier auf kompetente Antwort 
auf folgende Fragen:

1. Geht das wirklich so? Ich meine es werden doch während ISR Ausführung 
eh alle Interrupts standardmäßig gesperrt.
2. Wenn nein, wie gehts? (Ausser mit solch großem Aufwand wie die 
Entprellfunktion die hier in den Tutorials zu finden ist)
3. Ist es da nicht sinnvoller am Ende der ISR das GIFR Register zu 
bereinigen?
4. Wenn ich die ISR verlasse, wird dann mein Interrupt Flag automatisch 
wieder gesetzt?

Würde mich freuen wenn mir bei diesen Fragen jemand Aufklärung leisten 
könnte.

Grüße

Alex

von Jupp (Gast)


Lesenswert?

Durch Int0 wird in die ISR gesprungen. LED toggeln. Delay für die Länge 
Prellzeit warten. Da das INT0 Flag durch die Prellerei wieder gesetzt 
ist, vor Rücksprung also Verlassen der ISR noch das INT0 Flag "von Hand" 
löschen.

Zurücklehenen und genießen.

von m.n. (Gast)


Lesenswert?

Alexander Pl schrieb:
> Die Lösung lautet wohl cli(); als ersten Befehl in die ISR zu schreiben.

Wie Du selber schon bemerkt hast, werden beim Einsprung in eine ISR 
weitere Interrupts gesperrt. Daher ist ein cli() überflüssig.
Entprellen heißt, nach einem Signal vom Taster eine gewisse Zeit zu 
warten und dann zu prüfen, ob er gedrückt oder geöffnet ist. Per 
Software wird man je nach Qualität des Tasters ca. 50 ms warten müssen. 
Einfach auf Verdacht die LED sofort an- oder auszuschalten geht nicht, 
da die ISR zu INT0 auch durch einen Störimpuls angesprungen werden kann.

Alexander Pl schrieb:
> 2. Wenn nein, wie gehts? (Ausser mit solch großem Aufwand wie die
> Entprellfunktion die hier in den Tutorials zu finden ist)

Um einen gewissen 'Aufwand' kommst Du nicht umhin.

Dein Lehrer will eine Software-Lösung sehen, sodaß ich Dir eine 
Hardware-Lösung zeigen kann, ohne Deine Arbeit zu machen.
Einen Taster per Interrupt auszuwerten, ist in der Regel dann sinnvoll, 
wenn gleichzeitig der µC aufgeweckt werden soll oder keine Zeit ist, um 
das Prellen des Tasters im zig-Millisekundenbereich abzuwarten.
http://www.mino-elektronik.de/power_at90s/powerat90s.htm
Hier kannst Du auch die realen Signale eines Tasters sehen.

von Thomas E. (thomase)


Lesenswert?

Alexander Pl schrieb:
> 1. Geht das wirklich so? Ich meine es werden doch während ISR Ausführung
> eh alle Interrupts standardmäßig gesperrt.
Nein, so geht es nicht. Ja, die Interrupts werden gesperrt.

> 2. Wenn nein, wie gehts? (Ausser mit solch großem Aufwand wie die
> Entprellfunktion die hier in den Tutorials zu finden ist)

Genau mit einer Entprellung, wie im Tutorial beschreiben.

> 3. Ist es da nicht sinnvoller am Ende der ISR das GIFR Register zu
> bereinigen?

Kann sein. Aber nur, wenn du ein Delay in der ISR hast. Das ist aber 
absoluter Unsinn.

> 4. Wenn ich die ISR verlasse, wird dann mein Interrupt Flag automatisch
> wieder gesetzt?

Welches Flag? Ein Interrupt-Flag wird gesetzt, wenn das entsprechende 
Ereignis eintritt. Und zwar immer. Wird die ISR dazu aufgerufen, wird 
dieses Flag gelöscht. Tritt das Ereignis während der Ausführung der ISR 
wieder ein, wird es wieder gesetzt. Dann wird nach dem Verlassen der ISR 
diese nochmal aufgerufen.

Alexander Pl schrieb:
> Ihr habt nen Taster der eine LED schalten (toggeln) soll. Dieser Taster
> ist am Externen Interrupt 0 angeschlossen. Wie kann man softwareseitig
> verhindern, dass der Taster nachprellt und somit die LED mehrfach
> schaltet?

Das verhindert man, indem man den Interrupt gar nicht benutzt, sondern 
den Pin per Timer alle 1-10ms pollt. Entweder erzählt euch euer Lehrer 
Unsinn, weil er es nicht besser weiss oder es ist Ziel dieser 
Aufgabenstellung, dass ihr erkennt, dass es so nicht geht. Bzw., dass 
man sich mit dieser einfachen "Lösung" jede Menge Probleme aufhalst.

Setze ein Delay von etwa 10ms in deine ISR, schalte deine LED und lösche 
vor dem Verlassen das INT0-Flag im GIFR. Dann wird es funktionieren.

Aber, wie schon geschrieben, ist das ziemlicher Unsinn. Zu dieser 
Erkenntnis werdet ihr dann wohl im weiteren Verlauf des Kurses kommen. 
Und dann führt an einer vernünftigen Entprellung kein Weg mehr vorbei.

mfg.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Setze ein Delay von etwa 10ms in deine ISR, schalte deine LED und lösche
> vor dem Verlassen das INT0-Flag im GIFR. Dann wird es funktionieren.

Nee, nee, das ist zu schlicht!
Ein gedrückter Taster kann immer noch prellen, was dann wiederholt die 
LED schalten würde. Ich denke, das ist nicht beabsichtigt.

Thomas Eckmann schrieb:
> Und dann führt an einer vernünftigen Entprellung kein Weg mehr vorbei.

Ein wenig Hardware ist dabei sehr vernünftig ;-)

von DerDaniel (Gast)


Lesenswert?

Falls der uC nur dafür da ist, genau diese Aufgabe zu erledigen, könnte 
man eine Entprellroutine die ohne Interrupts auskommt in die ISR packen.
Das ist jedoch die unsaubere Methode und wir spätestens bei Programmen 
die mehr als nur das machen zu interessantem verhalten führen.

Sauber wäre, die ISR nur zum Wecken des uCs zu verwenden, dann im 
Hauptprogramm eine PIN abfrage und die normale Entprellroutine laufen 
lassen. INT0 muss jedoch noch innerhalb der ISR abgeschaltet werden, 
sonst wird dauernd wieder rein gesprungen.
So bald die Entprellroutine gelaufen ist und ein Tastendruck erkannt 
wurde, muss PIN jedoch regelmäßig gecheckt werden und bei der nächsten 1 
die erkannt wird INT0 wieder aktiviert werden.
Den Grund für extra HW Aufwand sehe ich nicht.

von m.n. (Gast)


Lesenswert?

DerDaniel schrieb:
> ... und wir spätestens bei Programmen
> die mehr als nur das machen zu interessantem verhalten führen.

Wieso das denn, drohst Du mit diffusen Höllenqualen?
Wenn man in der ISR andere Interrupts wieder zuläßt und für 50 ms den 
INT0-Eingang auf stabilen Zustand abfragt, ist das doch kein Problem.
An anderer Stelle ein Flag für den Tastendruck zu setzen und es in einer 
Warteschleife abzufragen, blockiert den Prozessor ebenfalls und dauert 
genauso lange.

Ich denke, wenn der TO das so hinbekommt, wird sein Lehrer sehr 
zufrieden sein.

von Alex P. (alex2203)


Lesenswert?

Super!
Danke Leute, aufgrund eurer Hilfe hab ich denk ich ne ganz passable 
Lösung gefunden.
Echt geiles Forum ;)

Gruß

Alex

von Kai M. (kai_mauer)


Lesenswert?

Alexander Pl schrieb:
> Danke Leute, aufgrund eurer Hilfe hab ich denk ich ne ganz passable
> Lösung gefunden.
> Echt geiles Forum ;)

Stelle die Lösung doch hier vor. Das könnte auch andere Leute als Deinen
Lehrer interessieren.

von Martin V. (oldmax)


Lesenswert?

Hi
Abgesehen von einer Interruptverarbeitung für einen einfachen Taster 
verstehe ich nicht, wieso das Entprellen eines Tasters den µC blockieren 
soll. Also, Signal kommt und wird in ein Flag geschrieben. Dann einfach 
einen Zähler setzen, der im Timer heruntergezählt wird. (Geht auch mit 
Programmzyklus für den Anfang). Erreicht der Wert des Zählers die 0, ist 
der Eingang gültig und kann im Programm verwendet werden. Da braucht 
kein Interrupt gesperrt oder der Interrupt mit einer Schleife belegt 
werden.
Also einfach mal den Pseudocode nachvollziehen

IO-Interrupt:
Signal kommt, Zeit und Bit setzen

Zyklus:
Zeit = 0 dann kein Ereignis und weiter
Zeit -1 ->   Zeit = 0, dann Status Bit übernehmen

oder Timer-Interrupt:
Zeit = 0 dann kein Ereignis und weiter
Zeit -1 ->   Zeit = 0, dann Status Bit übernehmen

Ist also gar nicht so kompliziert....
Gruß oldmax

von Jupp (Gast)


Lesenswert?

Martin Vogel schrieb:
> Also einfach mal den Pseudocode nachvollziehen
>
> IO-Interrupt:
> Signal kommt, Zeit und Bit setzen
>
> Zyklus:
> Zeit = 0 dann kein Ereignis und weiter
> Zeit -1 ->   Zeit = 0, dann Status Bit übernehmen
>
> oder Timer-Interrupt:
> Zeit = 0 dann kein Ereignis und weiter
> Zeit -1 ->   Zeit = 0, dann Status Bit übernehmen
>
> Ist also gar nicht so kompliziert....

Und das ganze so lange in einer Schleife bis man es verstanden hat. Dein 
"Pseudocode" ist mir unverständlicher als weggelassene Kommentare.

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


Lesenswert?

Alexander Pl schrieb:
> Dieser Taster ist am Externen Interrupt 0 angeschlossen.
Genau an dieser Stelle beginnt das Problem.

Ein Taster, der von einem Bediener mit maximal 10Hz betätigt wird, 
gehört nicht an einen Interrupt.

Und das hier schlägt dem Fass dann den Boden aus:
Jupp schrieb:
> Durch Int0 wird in die ISR gesprungen. LED toggeln.
> Delay für die Länge Prellzeit warten.
Ein delay_ms() in einer ISR ist Selbstmord. Denn der uC kann solange 
nicht anderes tun. Auch nicht auf einen anderen Interrupt reagieren. Und 
das ist z.B. dann hässlich, wenn es ein Timerinterrupt ist, der während 
der Prellzeit (die ja durchaus einige ms lang sein kann) mehrmals kommen 
könnte.

Alexander Pl schrieb:
> Die Lösung lautet wohl cli(); als ersten Befehl in die ISR zu schreiben.
Nein, die Lösung ist, diese schäbig langsamen Taster in der 
Hauptschleife einzulesen und zu verarbeiten.
Was? Ihr habt keine Hauptschleife?
Dann ist die Lösung, erst mal das Softwarekonzept so umzustellen, dass 
es eine solche Hauptschleife gibt, und in dieser die ganze Arbeit (oder 
mindestens 95% davon) erledigt wird. Eine ISR ist immer so kurz wie 
möglich zu halten. Ein delay_ms() in einer ISR ist ein Kündigungsgrund.

m.n. schrieb:
> Wenn man in der ISR andere Interrupts wieder zuläßt und für 50 ms den
> INT0-Eingang auf stabilen Zustand abfragt, ist das doch kein Problem.
Es ist Murks.

> Ich denke, wenn der TO das so hinbekommt, wird sein Lehrer sehr
> zufrieden sein.
Ich denke, dann hat er sich den falschen Programmierstil begebracht. 
Hoffentlich sagt ihm der Lehrer das dann auch...

: Bearbeitet durch Moderator
von Martin V. (oldmax)


Lesenswert?

Hi
@Jupp
Wo ist das Problem? kanst du mit Pseudocodes nicht umgehen? Das tut mir 
leid für dich, aber du hast ja zum Glück nicht das Problem zu lösen.
Falls unverständlich dann etwas anders.
1
 Programmschleife:
2
    tuwas1
3
    tuwas2
4
    tuwas3 
5
    wenn Zeit>0 dann
6
       Zeit = Zeit- 1
7
       Wenn Zeit = 0 dann invertiere Ausgang
8
    ende wenn
9
    Tuwas4
10
 fang von vorn an
11
  
12
 IO-Interrupt
13
    Zeit = 200
14
 ende Interrupt
Und wenn für die Entprellroutine ein Timer eingesetzt wird, kommt 
einfach der Wenn-Abschnitt in den Timer-Interrupt.
Die Entprellroutine belastet den Zyklus kaum und ob nun noch ein Bit mit 
einer weiteren Bearbeitung eingesetzt wird, spielt auch kaum eine Rolle.
Gern darfst du das in irgendeine Sprache packen...
Gruß oldmax

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Lothar Miller schrieb:
> Nein, die Lösung ist, diese schäbig langsamen Taster in der
> Hauptschleife einzulesen und zu verarbeiten.

Da ist ja das Allerletzte. Wenn das Programm mit einer Nebenschleife zu 
tun hat, wird der Tastendruck ja garnicht erkannt.
Die Tastererkennung muß im Hintergrund ablaufen. Entweder zyklisch per 
Timer oder aber auch per zugeordnetem Interrupt, wenn man verstanden 
hat, wie das geht ;-)

von Simpel (Gast)


Lesenswert?

Als Erstes sollte mal dediziert angegeben werden, ob der INT0 flanken- 
oder levelgetriggert eingesetzt werden soll. Das erfordert jeweils eine 
unterschiedliche Weiterverarbeitung des Events....



@Martin Vogel

Ganz so einfach ist das nicht. Wenn es nur ein einzelner Stör-Spike war 
und die Zeit ist abgelaufen, wird bei deinem Prinzip auch ein Toggle 
ausgelöst.... du musst überwachen, ob der Eingang innerhalb des 
Zeitfensters ununterbrochen die Stabil-Bedingung erfüllt hat. Wenn du 
nur einen Merker setzt, bei z.B. der Hi-Lo_Flanke, heisst das nicht 
zwingend, dass dieser Zustand auch nach Ablauf der Zeit noch am Eingang 
anliegt.

Das geht nur durch Levelüberwachung bzw. (was auf das Selbe rausläuft) 
über das Auschliessen eines weiteren Flankenevents innerhalb des 
Zeitfensters.

von Klaus (Gast)


Lesenswert?

Martin Vogel schrieb:
> Dann einfach
> einen Zähler setzen, der im Timer heruntergezählt wird. (Geht auch mit
> Programmzyklus für den Anfang). Erreicht der Wert des Zählers die 0, ist
> der Eingang gültig und kann im Programm verwendet werden.

Na solange will man doch nicht warten. Sobald der Kontakt das erste mal 
schließt solls losgehen.

MfG Klaus

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


Lesenswert?

m.n. schrieb:
> Da ist ja das Allerletzte. Wenn das Programm mit einer Nebenschleife zu
> tun hat, wird der Tastendruck ja garnicht erkannt.
> Die Tastererkennung muß im Hintergrund ablaufen. Entweder zyklisch per
> Timer oder aber auch per zugeordnetem Interrupt, wenn man verstanden
> hat, wie das geht ;-)
Wenn man verstanden hat, wie das geht, dann programmiert man seine 
Software so, dass die Hauptschleife immer in z.B. maximal 10ms 
durchlaufen wird. Damit wird eine gefühlte "Echtzeit" auf 
Benutzereingriffe erreicht. Und dann braucht man das ganze Herumgemurkse 
mit einer Tastenauswertung im Interrupt nicht mehr. Und die 
Tastenerfassung kann ja durchaus in einem Dreizeiler in einer 
Interruptroutine gemacht werden. So wie es z.B. PeDa im Timerinterrupt 
macht.

Dieser Programmierstil "Programmieren in einer Schleife" läuft übrigens 
weltweit zigmilliardenfach auf jeder SPS. Es ist so simpel, dass es 
jeder Schlosser kapiert. Dieser Schlosser programmiert da dann 
Zustandsautomaten, dass manchem uC-Programmierer die Ohren schlackern. 
Und er hat keine Angst vor diesen Automaten, weil er sie einfach 
Schrittketten nennt und mit Merkern abhandelt...

: Bearbeitet durch Moderator
von Martin V. (oldmax)


Lesenswert?

Hi
@simpel
du magst ja Recht haben, trotzdem ist es einfach. Dann eben beide 
Flanken mit Interrupt erfassen. Kommende Flanke: Zeit setzen, gehende 
Flanke Zeit löschen. So, nun muss das Signal stehen bleiben, damit die 
Zeit abläuft und der Übergang nach 0 erkannt und ausgewertet wird. Ist 
es nur ein Störimpuls, dann wird durch die Abfrage "Zeit > 0" schon gar 
nichts mehr bearbeitet.

@Klaus
Nun, das ist zwar der einzige Grund, warum ein mech. Kontakt in einer 
ISR erfasst werden soll, aber die Prellzeit musst du schon abwarten, 
sonst ist der Zustand ziemlich zufällig. Es müssen aber nicht 200 
Programmzyklen sein, vielleicht reichen auch 10 oder 20. Wenn es im 
Timer bearbeitet wird, denke ich, das in der Regel 5 ms ausreichen, also 
eine Zeit von 5 bei entsprechnedem Timer-Interrupt.

Die Diskussion ob Taster mit einer ISR zu erfassen sind oder ob Pollen 
völlig ausreicht, ist hier m. E. nicht zu führen, da es eine Aufgabe 
ist, die dem angehenden Techniker gestellt wird.
gruß oldmax

von m.n. (Gast)


Lesenswert?

Lothar Miller schrieb:
> Dieser Programmierstil "Programmieren in einer Schleife" läuft übrigens
> weltweit zigmilliardenfach auf jeder SPS. Es ist so simpel, dass es
> jeder Schlosser kapiert. Dieser Schlosser programmiert da dann
> Zustandsautomaten, dass manchem uC-Programmierer die Ohren schlackern.
> Und er hat keine Angst vor diesen Automaten, weil er sie einfach
> Schrittketten nennt und mit Merkern abhandelt...

Die Aufgabe war aber nicht, eine SPS zu programmieren, sondern bei einem 
ATmega16 einen Taster an INT0 auszuwerten.
An diese Vorgabe sollte man sich bei der Lösung schon halten.

von Alex P. (alex2203)


Lesenswert?

Bei Aufruf der ISR habe ich die Ausführung des externen Interrupts 
gesperrt bis Ende der ISR...

ISR(INT0_vect)
{
GICR|=(0<<INT0);

//restliche Anweisung

GICR|=(1<<INT0);
}

Funktioniert soweit tadellos.

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


Lesenswert?

m.n. schrieb:
> Die Aufgabe war aber nicht, eine SPS zu programmieren, sondern bei einem
> ATmega16 einen Taster an INT0 auszuwerten.
Durchaus nicht.
Alexander Pl schrieb:
>>> Im Mikrocontrollerunterricht stellte uns nun unser Lehrer folgendes
>>> Problem:
>>> Ihr habt nen Taster der eine LED schalten (toggeln) soll. Dieser Taster
>>> ist am Externen Interrupt 0 angeschlossen. Wie kann man softwareseitig
>>> verhindern, dass der Taster nachprellt und somit die LED mehrfach
>>> schaltet?
Man kann diesen Interrupt-Pin als normalen Eingangspin verwenden...

Alexander Pl schrieb:
> Bei Aufruf der ISR habe ich die Ausführung des externen Interrupts
> gesperrt bis Ende der ISR...
Das brauchst du nicht, das macht der Interruptcontroller schon ganz 
allein...

> Funktioniert soweit tadellos.
Schon wieder falsches Zeug gelernt.

: Bearbeitet durch Moderator
von Thomas E. (thomase)


Lesenswert?

Lothar Miller schrieb:
> Schon wieder falsches Zeug gelernt.

Eine Taste per Interrupt abzufragen, erscheint vielen Anfängern als 
logisch. Solange, bis man gelernt hat, dass das nichts taugt.

Das ist ein µC-Kurs in der Technikerschule. Im nächsten Schritt wird 
wahrscheinlich die einzelne LED zum Lauflicht ausgebaut. Dann sieht 
jeder, dass die Interruptabfrage Mist ist. Finde ich didaktisch völlig 
OK. Zumindest unter der Annahme, dass der Dozent kein Idiot ist.

mfg.

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


Lesenswert?

Ich meinte vorrangig das Sperren des Interrupts in der ISR.
Wobei diese Anweisung sowieso nichts bringt, weil sie nichts ändert :
> GICR|=(0<<INT0);
Hier wären Grundlagen zum Thema Bitmanipulation mit C recht gut.

Thomas Eckmann schrieb:
> Finde ich didaktisch völlig OK. Zumindest unter der Annahme, dass der
> Dozent kein Idiot ist.
Du hast recht und ich hoffe dass deine Annahme zutrifft.

: Bearbeitet durch Moderator
von m.n. (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Das ist ein µC-Kurs in der Technikerschule. Im nächsten Schritt wird
> wahrscheinlich die einzelne LED zum Lauflicht ausgebaut. Dann sieht
> jeder, dass die Interruptabfrage Mist ist.

Oder es wird der Taster durch einen Hallsensor ersetzt, der von einem 
drehenden Rad ganz kurze Impulse abgibt.
Dann sieht man sofort, wie sinnvoll die Interuptabfrage ist ;-)

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


Lesenswert?

m.n. schrieb:
> Oder es wird der Taster durch einen Hallsensor ersetzt, der von einem
> drehenden Rad ganz kurze Impulse abgibt.
Dafür gibt es 1. Zähler im uC und 2. prellt dieser Geber nicht...

> Dann sieht man sofort, wie sinnvoll die Interuptabfrage ist ;-)
Nicht einfach Kraut mit Rüben vergleichen. Natürlich kann ein Interrupt 
sinnvoll sein (sonst gäbe es sowas nicht). Aber eben nicht für eine 
Taste, die eine LED schaltet.

: Bearbeitet durch Moderator
von m.n. (Gast)


Lesenswert?

Lothar Miller schrieb:
> Nicht einfach Kraut mit Rüben vergleichen.

Gut. Dann lassen wir das mit der SPS, steuern kein Lauflicht an, und für 
die Abtastung der Raddrehzahl nehm ich einen prellenden Taster mit 
Nockenbetätigung ;-)

von Martin V. (oldmax)


Lesenswert?

Hi
Wenn es nur um kurze Impulse geht ist der Interrupt völlig ok. Wenn ich 
aber von Klimmzügen lese: Interrupt gesperrt, Delay in ISR und ähnliches 
Zeug, zeigt es mir, das der Sinn eines Interrupts nicht verstanden 
wurde. Wenn eine ISR "verlängert" wird, indem dort ein Delay abläuft, 
macht er keinen Sinn. Ich weiß, den Experten ist das auch klar, aber 
einem Anfänger nicht. Und leider sind auch manche Dozenten aus diesem 
Stadium noch nicht raus...
@Alexander Pl
Falsch ist eine ISR, in der eine Warteschleife abläuft. Interrupt heißt 
doch, auf etwas sofort zu reagieren. Etwas, das jederzeit und extrem 
kurz auftritt. Ein Tastendruck ist für einen Controller eine Ewigkeit, 
wenn er auch ohne Zeitschleifen auskommt. Und das ist, was ich 
angedeutet habe. Laß ihn in seiner Schleife laufen und frage nur 
Bedingungen ab. So wie das mit der Zeit angedeutet, kann man auch 
Ereignisbits nehmen und abfragen. Sind sie gesetzt, werden Subnroutinen 
abgearbeitet und die Bits gelöscht. So ist erst eine erneute Bearbeitung 
fällig, wenn dises Bit wieder gesetzt wird. Das sind die "Tuwas" 
Anweisungen. Wenn du davon ausgehst, das eine Tuwas- Anweisung mal grob 
geschätzt 10 Taktzyklen benötigt, dann verplemperst du mit jedem Aufruf 
bei 1 MHz Systemtakt mindestens 10 µs, wenn der Controller nur die 
Bedingungen abfragt, aber keine Aufgabe vorfindet. Wird in Tuwas auch 
noch bei erfüllter Bedingung etwas erledigt, verlängert sich die Zeit 
entsprechend der Anzahl der Takte, die deine Befehlsverarbeitung 
benötigt. Da aber nicht alle Tuwas-Anweisungen bei jedem Zyklus etwas 
ausführen, verteilt sich die Zeit und selbst umfangreiche Programme 
liegen selten über ein paar ms mit der Zykluszeit. Ersetz mal "Tuwas" 
mit "Tuwas wenn Event = 1". Das sollte es deutlich machen, und wenn du 
die Aufgabe erledigt hast setzt du das Event auf 0. So, und nun wieder 
dein Taster. Wenn bei jedem Zyklus einmal die Peripherie eingelesen 
wird, meinst du, du kannst deinen Taster so schnell betätigen, das er 
ein Fenster von ca. 10 ms verfehlt? Bei 8 MHz hättest du dann nur noch 
eine Zykluszeit bei 2,5 ms. Also, einen normalen IO- Taster mit 
Interrupt zu erfassen dient lediglich der Übung. Es gibt aber natürlich 
auch Ausnahmen, doch das führt hier zu weit.
Gruß oldmax

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


Lesenswert?

m.n. schrieb:
> Gut. Dann lassen wir das mit der SPS, steuern kein Lauflicht an, und für
> die Abtastung der Raddrehzahl nehm ich einen prellenden Taster mit
> Nockenbetätigung ;-)
Dann lasse ich die Erfassung des Pins in einem 1ms-Timer-Interrupt 
(wie die Tastenerfassung eben auch) laufen. Die Auswertung des 
erfassten Ereignisses erfolgt in der Hauptschleife...

von Paul Baumann (Gast)


Lesenswert?

Alexander Pl schrieb:
> Super!
> Danke Leute, aufgrund eurer Hilfe hab ich denk ich ne ganz passable
> Lösung gefunden.
> Echt geiles Forum ;)

Der TO hat schon lange eine Lösung, ist aber so undankbar, sie hier 
nicht
zu posten.
:-(

MfG Paul

von Jupp (Gast)


Lesenswert?

Paul Baumann schrieb:
> Der TO hat schon lange eine Lösung, ist aber so undankbar, sie hier
> nicht
> zu posten.
> :-(

Kann man so nicht sagen und seine Lösung wurde schon einige Beiträge 
lang kommentiert.

Alexander Pl schrieb:
> Bei Aufruf der ISR habe ich die Ausführung des externen Interrupts
> gesperrt bis Ende der ISR...
>
> ISR(INT0_vect)
> {
> GICR|=(0<<INT0);
>
> //restliche Anweisung
>
> GICR|=(1<<INT0);
> }
>
> Funktioniert soweit tadellos.

von Paul Baumann (Gast)


Lesenswert?

Aha. Ich dachte nicht daß das die endgültige Lösung wäre.
Aber: Jeder, wie er will und wie er damit zufrieden ist.

Ich mache mir immer einen Timerinterrupt zurecht, denn einen zyklischen
Aufruf kann man immer gebrauchen (und sei es, sich einen Uhrentakt
abzuleiten). Am besten sind 10ms.

Jetzt geht's los:
Ich frage in diesem Interupt ab, ob ein (Tasten)-Pin auf L-Pegel
gegangen ist. Wenn ja, zähle ich eine Variable um 1 hoch, sonst lasse
ich die Variable auf Null. Wenn die Variable den Wert 5 erreicht hat,
sage ich: "Das war ein ernstgemeinter Tastendruck!" und setze einen 
Merker.
Im Hauptprogramm lasse ich dann die Tätigkeit ausführen, die bei dieser
gedrückten Taste getan werden soll, setze Variable und den Merker wieder 
zurück und bin wieder für den nächsten Druck bereit.

Hinweis: Es mag bessere und/oder jede Menge anderer Methoden geben. Das
ist die von mir Verwendete und die funktioniert tadellos.

MfG Paul

von Jupp (Gast)


Lesenswert?

Paul Baumann schrieb:
> Ich mache mir immer einen Timerinterrupt zurecht, denn einen zyklischen
> Aufruf kann man immer gebrauchen (und sei es, sich einen Uhrentakt
> abzuleiten). Am besten sind 10ms.
>
> Jetzt geht's los:
> Ich frage in diesem Interupt ab, ob ein (Tasten)-Pin auf L-Pegel
> gegangen ist. Wenn ja, zähle ich eine Variable um 1 hoch, sonst lasse
> ich die Variable auf Null. Wenn die Variable den Wert 5 erreicht hat,
> sage ich: "Das war ein ernstgemeinter Tastendruck!" und setze einen
> Merker.
> Im Hauptprogramm lasse ich dann die Tätigkeit ausführen, die bei dieser
> gedrückten Taste getan werden soll, setze Variable und den Merker wieder
> zurück und bin wieder für den nächsten Druck bereit.

Klingt besser als die tuwas if Schleifen in dem unformatierten Text von 
hier: Martin Vogel schrieb:

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


Lesenswert?

Paul Baumann schrieb:
> Das
> ist die von mir Verwendete und die funktioniert tadellos.
Genau so macht es im Grunde auch Peter Danegger, nur eben mit 8 Bits 
gleichzeitig...
Siehe dort bei 3.3:
http://www.mikrocontroller.net/articles/Entprellung

von Route_66 H. (route_66)


Lesenswert?

Jupp schrieb:
> if Schleifen

Nicht schon wieder!

von Paul B. (paul_baumann)


Lesenswert?

>> Das
>> ist die von mir Verwendete und die funktioniert tadellos.
Lothar Miller schrieb:
> Genau so macht es im Grunde auch Peter Danegger, nur eben mit 8 Bits
> gleichzeitig...

Ja, ich weiß.
Diese (meine) Methode habe ich aber davon unabhängig in Bascom 
realisiert, weil die vorgefertigte Möglichkeit (Debounce) einen Timer 
nutzt, den ich dann nicht mehr "für mich" habe.

MfG Paul

von Peter D. (peda)


Lesenswert?

Paul Baumann schrieb:
> weil die vorgefertigte Möglichkeit (Debounce) einen Timer
> nutzt, den ich dann nicht mehr "für mich" habe.

Warum darf denn ein Timer nicht mehrere Sachen machen?

Man erinnere sich an die Schule, Stichwort größter gemeinsamer Teiler.
Z.B. ich brauche 20ms und 25ms Takt. Dann reicht dafür ein 5ms 
Interrupt:
GGT(20, 25) = 5

von Paul Baumann (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Warum darf denn ein Timer nicht mehrere Sachen machen?

Ich habe nicht behauptet, daß ein Timer nicht mehrere Sachen machen 
kann.
Wie kommst Du darauf? Ich nutze ja den (oder manchmal auch die) Timer,
um mehrere Sachen zu tun.

MfG Paul

von Martin V. (oldmax)


Lesenswert?

Hi
@Jupp
Da du scheinbar mit Beschreibungen nicht viel anfangen kannst, hilft dir 
vielleicht ein PAP zur besserem Verständnis. Und da du auch der einzige 
bist, dem Pseudocode nix sagt, adressiere ich mal diesen 
Programmablaufplan. Nicht perfekt, aber du sollst ja noch ein bisschen 
zum Lästern haben.

@Alexander Pl
Natürlich soll die Skizze dir auch ein wenig helfen. Mit einem PAP hast 
du dir schnell mal ein Abbild vom Ablauf geschaffen und es muss nicht 
immer ein Programm sein, um solche Skizzen zu erstellen. Papier und 
Bleistift hift da manchmal auch schon.
Gruß oldmax

von Martin V. (oldmax)


Angehängte Dateien:

Lesenswert?

Hi
ich sollte vielleicht den PAP auch anhängen.. sorry
Gruß oldmax

von Peter D. (peda)


Lesenswert?

Ich habe gemerkt, daß es deutlich zuverlässiger ist, auch das Loslassen 
zu entprellen.

Muskeln werden nicht statisch gesteuert, sondern mit Nervenimpulsen, 
d.h. die Drückkraft schwankt ständig leicht, auch ohne Parkinson.
Auch kann sich bei älteren Tastern eine Oxydschicht auf den Kontakten 
bilden oder Verschmutzungen, die kurze Unterbrechungen bewirken.

Beide Wechsel zu entprellen ist sogar einfacher, man macht ein EXOR 
zwischen Eingangszustand und entprelltem Zustand. Bei Ungleich wird mit 
der Zeitbasis gezählt, z.B. 4-mal und dann übernommen.

von Elektroniker (Gast)


Lesenswert?

In welcher Sprache ist das Programm geschrieben? Assembler? Ich könnte 
dir ein Programm zur verfügung stellen, falls es C oder Assembler ist.

von Hannes (Gast)


Lesenswert?

Lothar Miller schrieb:
> Genau so macht es im Grunde auch Peter Danegger, nur eben mit 8 Bits
> gleichzeitig...
> Siehe dort bei 3.3:
> http://www.mikrocontroller.net/articles/Entprellung

Den Code gibt es im Tutorial auch noch einmal. Mir leuchtet nicht ein, 
was der da machen soll. Ich habe mir das aufgezeichnet und Schritt für 
Schritt auf Papier iteriert, aber key_press für die Auswertung im "main" 
wird mMn nie verändert.

Jetzt habe ich mir den Teil aus der ISR herauskopiert und key_pin mit 
0b01111111 (active low) initialisiert (also eine Taste gedrückt, siehe 
in -> mov in der zweite Zeile vom loop, weil es ja kein I/O-Register 
ist; die restlichen Register sind mit 0 initialisiert) und steppe das im 
Debugger (Atmel Studio 6.2, Atmega8, Simulator) durch. Das Register r20 
(key_press) ist immer 0. Da ändert sich auch nach dem 100. Loop nichts:

1
.def iwr0 = r16
2
.def iwr1 = r17
3
.def key_old = r18
4
.def key_pin = r19
5
.def key_press = r20
6
.def key_state = r21
7
8
    ldi     key_pin, 0b01111111
9
10
loop:
11
    mov     iwr0, key_old
12
    mov     key_old, key_pin
13
    eor     iwr0, key_old
14
    com     key_old
15
    mov     iwr1, key_state
16
    or      key_state, iwr0
17
    and     iwr0, key_old
18
    eor     key_state, iwr0
19
    and     iwr1, iwr0
20
    or      key_press, iwr1
21
22
    rjmp    loop

Da stimmt doch irgendwas nicht. Ich dachte auch, irgendwo gelesen zu 
haben, dass das Assembler-Beispiel eine Übersetzung aus C-Code ist, 
finde das aber nicht mehr. Eventuell hat sich ja da ein Fehler 
eingeschlichen?

Aber eigentlich interessiert mich, wie da GEZÄHLT wird. Im Artikel 
steht, dass die Taste 4 mal als gedrückt erkannt werden muss, um als 
valide zu gelten. Meine Vermutung war, dass die Register als 
Schieberegister benutzt werden und ein gültiges Bit immer eins 
weitergeschoben wird, bis es als valides Bit in key_press landet. Auf 
dieser Vermutung habe ich eine eigene Lösung gebaut, die zwar eins, zwei 
Zeilen länger und eins, zwei Register mehr braucht, aber auch 
funktioniert.

Aber dennoch - wie wird da gezählt? Kann mir da bitte jemand auf die 
Sprünge helfen?


Danke und einen schönen Abend!

von Peter D. (peda)


Lesenswert?

Hannes schrieb:
> Da ändert sich auch nach dem 100. Loop nichts:

Kann auch nicht.
key_press signalisiert nicht den Zustand des gedrückt haltens.
Es signalisiert den Übergang von losgelassen nach gedrückt.
Jedes Drücken ergibt also nur ein Ereignis.
Man will ja z.B., daß eine LED bei jedem Drücken wechselt und nicht, daß 
sie ständig flackert, solange man die Taste gedrückt hält.

Die Routine ist absichtlich so geschrieben, daß man nach einem Reset 
erstmal loslassen muß, ehe ein Drücken erkannt werden kann.


Hannes schrieb:
> Im Artikel
> steht, dass die Taste 4 mal als gedrückt erkannt werden muss, um als
> valide zu gelten.

Das ist nur bei der C-Routine der Fall.

: Bearbeitet durch User
von Hannes (Gast)


Lesenswert?

Guten Morgen!

Aha, das probiere ich aus.

Danke!

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.