Forum: Mikrocontroller und Digitale Elektronik Tastenentprellung Problem


von Sven (Gast)


Lesenswert?

möchte per tastendruck eine variable erhöhen. das problem liegt nun 
darin dass diese variable bei jedem tastendruck nicht wie gewünscht nur 
um 1 erhöht wird, sondern immer um 2. keine ahnung wieso um zwei? habe 
unten die wichtigen programmstücke angefügt. sieht gerade hier jemand 
ein problem?

uC: ATmega8

void init(void){
  sei();
  DDRB=0xff;
  DDRD=0xff;
  //external interrupts
  MCUCR|=((1<<ISC11)|(1<<ISC10));
  GICR|=(1<<INT1);
}


ISR(INT1_vect){
GIFR|=(1<<INTF1);  //flag löschen
x++;  //variable erhöhen
_delay_ms(5); //entprellen, glaube das geht so oder?
}


int main (void){
init();
while(1);
}

von Fabian B. (fabs)


Lesenswert?

deine entprellung ist bestenfalls ausreichend- G

lies dich mal etwas schlau: Entprellung

Gruß
Fabian

von Johannes M. (johnny-m)


Lesenswert?

Vor allem musst Du vor dem Ende der ISR (nach dem delay) das 
Interrupt-Flag wieder löschen, sonst kannste noch so lange warten. Das 
Löschen beim Einsprung in die ISR macht die Hardware schon selbst.

Übrigens:
Tasten fragt man nicht über externe Interrupts ab. Die sind dafür 
eigentlich gar nicht geeignet (weil sie eben zu schnell sind und 
zusätzliche Entprell-Maßnahmen erforderlich machen). Frage Deine(n) 
Taster besser zyklisch alle paar zig Millisekunden mit Hilfe eines 
Timer-Interrupt ab. Da brauchste jeweils nur schauen, ob sich am Taster 
was geändert hat oder nicht, und zusätzliche Entprellung mit 
irgendwelchen delay-Funktionen ist überflüssig.

von Boxi B. (boxi)


Lesenswert?

Sven wrote:
> ISR(INT1_vect){
> GIFR|=(1<<INTF1);  //flag löschen
> x++;  //variable erhöhen
> _delay_ms(5); //entprellen, glaube das geht so oder?
> }

Das mag ja ne nette Idee in deinem kleinen Progrämmchen sein, aber sowas 
solltest du dir garnicht erst angewöhnen. Sinnlos warten in einer ISR 
(_delay()) ist tödlich! Tu das nie wieder!
Für die meisten Fälle gilt: Tu in der ISR nur das absolut notwendige und 
das möglichst perfekt.

Gruß
Boxi

von Johannes M. (johnny-m)


Lesenswert?

Boxi Boxitec wrote:
> Das mag ja ne nette Idee in deinem kleinen Progrämmchen sein, aber sowas
> solltest du dir garnicht erst angewöhnen. Sinnlos warten in einer ISR
> (_delay()) ist tödlich! Tu das nie wieder!
Wenn das sein einziger Interrupt ist, stellt das gar kein Problem dar 
und ist keinesfalls tödlich. Schließlich wird durch das Warten nix 
anderes blockiert.

Angewöhnen sollte man es sich aber trotzdem nicht. Warteschleifen haben 
in ISRs eigentlich nichts verloren.

von Boxi B. (boxi)


Lesenswert?

^    ^    ^    ^
|    |    |    |


@Johnny: Sehr gut reproduziert Johnny. Das gibt ne 3 minus.

von Peter D. (peda)


Lesenswert?

Johannes M. wrote:

> Wenn das sein einziger Interrupt ist, stellt das gar kein Problem dar
> und ist keinesfalls tödlich. Schließlich wird durch das Warten nix
> anderes blockiert.

Das hat nix mit der Anzahl der Interrupts zu tun, das Main steht 
solange.

Ich hab sowas schonmal gesehen, ne Uhr mit Anzeigeroutine im Main.
Sie hat funktioniert und ging auch richtig, bloß waren die Sekunden auf 
der Anzeige unterschiedlich lang.


Peter

von Johannes M. (johnny-m)


Lesenswert?

@Peter && Boxi:
Stimmt, da fehlte ein "u.U." oder so ähnlich. Wenn das Programm sowieso 
nichts anderes zu tun hat, dann kann es wurscht sein. Aber ist schon 
richtig: Gar nicht erst angewöhnen...

von Sven (Gast)


Lesenswert?

danke erstmal für die vielen antworten, doch eine antwort steht dabei 
leider noch nicht. übrigens verwende ich nur diesen einzigen interrupt 
und deshalb stellt das kein problem dar. einen timer ständig laufen zu 
lassen ist doch auch nicht gerade stromsparend oder. aber wieso erhöht 
sich meine variable jetzt immer um 2 ????????? irgendwie empfängt der 
interrupt eingang immer schon wieder einen interrupt wenn ich das erste 
mal in der isr bin oder so. und ich kann das flag dann nicht 
löschen?????

von Schorsch (Gast)


Lesenswert?

Nimm doch einfach die debounce()-Funktion aus dem tutorial zum 
entprellen, die läuft ohne probleme.

und dann einfach if (debounce(PINX, PXX)) x++; sollte genügen

von T. H. (pumpkin) Benutzerseite


Lesenswert?

1
void init(void)
2
{
3
  DDRB = 0xFF;
4
  DDRD = 0xFF;
5
6
  MCUCR |= ( (1<<ISC11) | (1<<ISC10) );
7
  GICR  |= (1<<INT1);
8
9
  sei();
10
}
11
12
13
ISR(INT1_vect)
14
{
15
  x++;
16
  _delay_ms(5); // Vllt etwas höher als 5 probieren.
17
  GIFR |= (1<<INTF1);
18
}

Was ich jetzt nicht geguckt habe: Triggerst du tatsächlich 
ausschließlich auf eine Flanke, oder doch gar auf steigende und 
fallende Flanke?

von Schorsch (Gast)


Lesenswert?

Debuggen ist auch immer ne nette lösung... ;-)

von Peter D. (peda)


Lesenswert?

Sven wrote:
> einen timer ständig laufen zu
> lassen ist doch auch nicht gerade stromsparend oder.

Ich sach ma, Du wirst keinen Unterschied messen können, ob der Timer 
zählt oder nicht.


> aber wieso erhöht
> sich meine variable jetzt immer um 2

Das ist völlig normal bei mehreren Impulsen.
Der 1. Impuls setzt das Flag und der Interrupt wird angesprungen.
Der 2. Impuls setzt wieder das Flag, aber da Du bereits im 
Interrupthandler bist, wird es nicht gelöscht.
Erst wenn Du den Interrupt beendest, wird er sofort wieder angesprungen 
und das Flag gelöscht.
Du mußt also das Flag unmittelbar vor dem Interruptende (RETI) nochmal 
löschen.


> und ich kann das flag dann nicht
> löschen?????

Doch, aber irgendein Spitzbube bei Atmel hat das so gelöst, das 
Interruptbits gesetzt werden müssen, um sie zu löschen.
Darauf fällt absolut jeder AVR-Beginner rein.

Es gibt auch einige Nicht-Interruptbits, die sich so verhalten.


Peter

von Boxi B. (boxi)


Lesenswert?

Während du in deiner ISR die 5ms wartest, sagen wir, im ersten 
Prellimpuls nach einem Tastendruck, kommt der nächste Prellimpuls dieses 
Tastendrucks, wodurch dein Interruptflag schon wieder gesetzt wird. Wenn 
du fertig gewartet hast, springt der Controller aufgrund des gesetzten 
Interruptflags wieder in die ISR und führt sie ein zweites mal aus.
Daher hat T.H. wohl vorgeschlagen das
1
GIFR |= (1<<INT1);

nach dem Warten auszuführen. Dies wird das Problem beheben, aber eine 
wirklich saubere Entprellung ist das nicht.

Nimm dir lieber erstmal die Zeit, anzuschauen, wie man das vernünftig 
macht. Viele der oben gegebenen Antworten helfen dir dabei.

Gruß
Boxi

von Boxi B. (boxi)


Lesenswert?

mist, zu langsam ;(

von AVRFan (Gast)


Lesenswert?

>einen timer ständig laufen zu
>lassen ist doch auch nicht gerade stromsparend oder.

Wenn Du Wartezeiten durch Schleifen wie "delay_ms()" bewerkstelligst, 
läßt Du den Controller zigtausendmal "nop" abarbeiten.  Dabei bleibt der 
gesamte Controller aktiv.

Legst Du den µC dagegen via "sleep"-Instruktion schlafen, und läßt ihn 
von einem Timer erst wieder aufwecken, wenn es wieder was zu tun gibt, 
dann wird während des Sleeps der fetteste Teil des Controllers, nämlich 
der, der die Instruktionen lädt und ausführt (die ALU), abgeschaltet. 
Aktiv bleibt außer dem Clocksystem nur das kleine, autonom 
weiterzählende Timermodul.

Was glaubst Du, welche Warte-Methode stromsparender ist?

von T. H. (pumpkin) Benutzerseite


Lesenswert?

Peter Dannegger wrote:
> Darauf fällt absolut jeder AVR-Beginner rein.

Pah! Hörensagen! Manche können auch lesen.  ;^)


Boxi Boxitec wrote:
> Daher hat T.H. wohl vorgeschlagen das
> GIFR |= (1<<INT1);

Was der Themenstarter übrigens auch schon gemacht hat, aber an der 
falschen Stelle.

von Peter D. (peda)


Lesenswert?

T. H. wrote:
> Peter Dannegger wrote:
>> Darauf fällt absolut jeder AVR-Beginner rein.
>
> Pah! Hörensagen! Manche können auch lesen.  ;^)

Ich weiß ja nicht, ob Du ein Datenblatt erstmal auswendig lernst, ehe Du 
den Chip ausprobierst.

Nachlesen tut man nur solche Sachen, die einem unklar sind.

Als logisch denkender Mensch geht man ganz selbstverständlich davon aus, 
daß Bits gelöscht werden mit 0 reinschreiben.


Peter

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.