C Tasterabfrage Interrupt

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Hallo Freunde,

Ich möchte gerne meine Taste in einer ISR Routine abfragen! Das klappt wunderbar! Nur wenn ich die Taste nicht schnell genug los lasse, wird mein Wert direkt noch mal erhöht!
Wie kann ich das umgehen? Sprich ich möchte die Taste quasie gedrückt halten aber der Wert wird nur einmal erhöhr! Drücke ich die Taste noch einmal, so wird der wieder nur einmal erhöht...

Code:
ISR(TIMER0_COMPA_vect) // Sorgt dafür das die Akkus / Batterien geschont werden! (Abschaltung nach ca. 10 min.)

	{
		Auto_Off = Auto_Off+1; // Jede 10ms Auto_Off um 1 erhöhen!
		
		if (!(PIND & (1<<key_UP)))
			{
				Auto_Off=0;
				keystatus |= (1<<0);
				OCR1A = OCR1A +13110;
			}
			
		if (!(PIND & (1<<key_DOWN)))
			{
				Auto_Off=0;
				keystatus |= (1<<0);
				OCR1A = OCR1A -13110;
			}

	
	}
 
Du musst dir ein Flag setzen wenn die Taste gedrückt ist und erst wieder löschen wenn die Taste losgelassen wird.
Wenn du dann abfragst ob die Taste gedrückt und das Flag gesetzt ist, hast du je Tastedruck nur eine Auswertung.
 
Du musst dir ein Flag setzen wenn die Taste gedrückt ist und erst wieder löschen wenn die Taste losgelassen wird.
Wenn du dann abfragst ob die Taste gedrückt und das Flag gesetzt ist, hast du je Tastedruck nur eine Auswertung.

Kannst du mir dazu bitte mal ein beispiel aufzeigen ? Ich tuh mir dabei immer sehr schwer :(
 
Code:
if (!(PIND & (1<<key_UP)))&& (! Taste gedrückt)
               Taste gedrückt =1; // Wenn gesetzt wird die Abfrage nur einmal durchlaufen
               Dein Code

if ((PIND & (1<<key_UP)))&& ( Taste gedrückt)
               Taste gedrückt =0; // Wenn die Taste nicht mehr gedrückt ist wird das Flag wieder zurückgesetzt.

Ich hoffe es ist verständlich, Klammern habe ich nicht kontrolliert.
 
okay das hört sich logisch an ! ist das auch effektiv ?! ich würde dann ja für meine Taschenlampe (hoch/runter) dimmen... 4 x if Abfragen benötigen!
Würde das auch noch besser / anderst gehen?
 
Hi
Leider kann ich kein C (oder will es nicht können :eek: ) aber vielleicht hilft dir die Erklärung mit einem Assemblerbeispiel. Dazu hab ich zwei Variablen Old_in und New_In.
In New_In liegen die entprellten Signale der Eingänge. Nun folgt eine Exclusiv-Oder Opeeration zwischen Old_In und New_In. Nur veränderte Bits haben eine logische "1". Mit einer Und-Operation zwischen dem Ergebnis und der Variablen Old_In hast du den Wechsel von "1" nach "0". Mit einer Und-Operation zwischen Ergebnis und New_In erkennst du den Wechsel von "0" nach "1". Nach dieser Auswertung kopierst du New_In nach Old_In. Die ermittelten Bits nimmst du als Flankenbits und rufst entsprechend die Aktionen auf, die du damit bearbeiten willst. Nach Bearbeitung wird das Flankenbit gelöscht. Eine erneute Bearbeitung wird erst beim nächsten Flankenwechsel erfolgen. Das heißt: Taster loslassen und erneut drücken.
In Assembler
LDS r 16, old_in
LDS r 17, New_In
EOR r 17, r16 ; Ergebnis in r17
AND r16, r17 ; Ergebnis steht in r16
STS In_To_Low,r16 ; Wechsel von 1 nach 0
LDS r16, New_In ; nochmal laden
AND r17, r16 ; Ergebnis steht in r16
STS in_To_High, r17 ; Wechsel von 0 nach 1
STS old:In, r16 ; neu nach alt

Ach ja, Taster in interrupt ist nicht sehr sinnvoll. Du weißt nichts über das Prellverhalten. Ich frage im Polling ab, da ein Controller idR schnell genug ist, ein paar hundert mal vorbeizurauschen, bevor ich den taster wieder loslassen kann. Entprellt wird mit Zeitzählern ( ca. 5mSek) Auch hier hilft EOR oder Exclusiv.
Angenommen , deine Eingänge werden in einer Variablen InDebounce gehalten, bis sie Stabil sind ist auch hier das Vorgehen:
Port laden, Exclusiv mmit der Variablen InDebouncce. <> 0 dann Zeitzählerwert auf 5
Zeitzähler wird in der Timer ISR runtergezählt.
Ist der Zeitzähler 0 ist nix passiert und die folgenden Schritte werden übersprungen. Erreicht der Zeitzähler den Wert 0 beim Runterzählen, dann ist dein Eingang stabil und kann nach New_In kopiert werden. Kommen deine Eingänge von verschiedenen Ports, musst du statt der direkten Portabfrage eine Variable mit den gesammelten Bits zwischenschalten. Hier mal eine kleine Assemblerroutine, die von Port B 4 Bits benutzt.
Read_IO:
In r16, PortB ; lesen Port B
ANDI r16, 0b00001111 ; ungültige Bits ausblenden
LDS r17, InDebounce ; Lade letzen Bitstatus
EOR r17, r16 ; Unterschiedlich zur Ablage InDebounce ?
BREQ weiter ; wenn nicht, dann Ende
STS InDebounce, r16 ; sonst gelesenen Wert in die Ablage
LDI r16, 5 ; Zeitzähler mit 5 setzen
STS Debounce_Time, r16
Weiter:
RET

Timer_ISR:
.... ; irgendwelche Bearbeitung

Is_mSek: ; Einstieg mSek , kann aber auch Anfang sein
LDS r16, Debounce_Time ; Prellzeit laden
CPI r16, 0 ; ist 0 ?
BREQ Next_mSek ; dann überspringen
DEC r16 ; runterzählen
STS Debounce_Time, r16 ; aktuelle Prellzeit speichern
BRNE next_mSek ; nicht 0 , dann überspringen
LDS r16, InDebounce ; InDebounce ist stabil
STS New_In, r16 ; dann nach New_In kopieren
Next_mSek:
.... ; weitere Interrupt -Bearbeitungen

RETI

Die Flankenauswertung erfolgt dann auch in der Hauptschleife und wird einfach bei jedem Zyklus durchlaufen. Zum Beispiel mit einer kleinen Subroutine Set_IO_Event:

Ok, ich weiß, du sprichst C, aber das Prinzip ist in C genauso abzuarbeiten. Deshalb und für andere Neugierige hab ich es mal nieder geschrieben.
gruß oldmax
 
Tasterabfrage

Hallo
Es gibt doch ein sehr schöner Teil von Peter Dannegger über Abfrage von Tastern über Interrupt (alles ind C). Dabei wird sogar die Länge des Tastendrucks berücksichtigt. Habe sehr gute Erfahrungen damit gemacht.
achim
 
Naja, Konkurrenz, was die Atmosphäre hier bei uns und dort angeht, ganz sicher nicht.

@Topic: Die Entprellung nicht vergessen!
 
Stichwort Flankenerkennung...

Jetzt mal ein paar Codeschnippsel, vllt. hilft das dir auch weiter.....

Das hier ist allerdings ein Beispiel für das STK500 Board / Pullups + Tasten masseschaltend
Beachte Eingänge werden mit ~PINA invertiert eingelesen !
EDIT:
Eingänge werden invertiert eingelesen, da die Tasten auf dem STK500 auf Masse schalten

Normalerweise finde ich es nicht notwendig, dass man Tasten über Interrupts einliest?! - was macht das für einen Sinn?
Ich denke kaum, dass ein kompletter Durchlauf deines Programms länger wie 10ms dauert......

Code:
int main(void)
{
	
	//Eingang initialisieren - alle Pins des Registers als Eingang
	//0 = Eingang
	DDRA = 0;
	

	//Ausgang initialisieren - alle Pins des Registers als Ausgang
	//1 = Ausgang
	DDRB=0xFF; //oder binär : DDRB=0b1111111;
	
	
	//alle LEDs aus
	PORTB=0b11111111;

	uint8_t tasten		= 0;
	uint8_t tasten_alt  = 0;
	
    while(1)
    {
       
	   tasten=~PINA; // Zustand PortA in Variable speichern

		if((tasten & TASTE_0) && !(tasten_alt & TASTE_0)) // Taste gedrückt....
		{
			//tu etwas......
		}
		tasten_alt=tasten;
	     
    }
}


Ansonsten vllt so:
(sind nur codeschnipsel.... ISR Einstellungen fehlen.... wieder ein Beispiel für STK500 .... Eingänge invertiert eingelesen)
EDIT:
Eingänge werden invertiert eingelesen, da die Tasten auf dem STK500 auf Masse schalten

Variable für die Zeit und Taste
Code:
volatile uint8_t tWait0 = ????;
#define TASTE_0		0x01
// oder #define TASTE_0		(1<<PINA)  //oder welchen Portpin auch immer..


ISR
Code:
ISR(TIMER0_COMP_vect)
{       
	if (tWait0) tWait0--; 
}

in der While-Schleife:
Code:
while (1)
{   
		// Task Scheduler
		if (tWait0 == 0)	{ task0(); }   // wird aufgerufen, wenn Zeit abgelaufen....
}


Funktion:
Code:
void task0(void) 
{
           static tasten_alt=0;

	   tasten=~PINA; // Zustand PortA in Variable speichern

		if((tasten & TASTE_0) && !(tasten_alt & TASTE_0)) // Taste gedrückt 
		 {
			//tu etwas......
		}
		tasten_alt=tasten;
	
}
 
Erstmal kurz was zu den Pullups:
Wenn ein, an einen Pin angeschlossener Taster als digitaler I/O ausgewertet werden soll (also binär), kann man das generell entweder machen, indem man den Flankenwechsel als solchen detektiert, oder indem man den Pegel überprüft, und feststellt, daß irgendwann mal mindestens einer stattgefunden hat.
In jedem Fall müssen 2 unterschiedliche Zustände (low/high) erkennbar sein. Also ein Grundzustand bei offenem Taster, und der andere bei geschlossenem Taster. Welcher jetzt welcher ist, ist erstmal egal. Der Pin wird bei offenem Taster über einen Pull-Widerstand auf einen sauberen Pegel gezogen, wird der Taster geschlossen, wird der Pin gegen den R auf den anderen Pegel gezwungen. Dabei ist egal, ob der R gegen Vcc oder gegen Gnd geht. Wenn man aber einen R nach Vcc verwenden kann/will, kann man auf den AVR-internen Pullup zurückgreifen, und sich das physische externe Bauteil sowie dessen Verbindung mit Vcc ersparen. Einen internen Pulldown gibt es nicht.
Außerdem kann man (zB zum Stromsparen) den internen Pullup auch deaktivieren, solange man den Taster nicht überprüft.

Aber zum eigentlichen Thema: Tasterabfrage und Interrupts:
Warum soll es denn keinen Sinn machen, die Abfragen an einen IRQ zu koppeln? Irgendwie ist das a oben etwas oberflächlich gewesen...
Wie bereits von mir angedeutet kann man entweder auf die Flanke selbst reagieren, oder die Pegel vergleichen.
Bei ersterem muß man zwingend den entsprechenden IRQ verwenden, und zwar den Flankeninterrupt des Beinchens (das ging früher nur mit INT0/INT1... und mit ein paar Klimmzügen auch mittels Zweckentfremdung anderer Hardware - inzwischen gibt es mmer mehr PCINTs).

Der zweite Weg (und um den gings hier bisher immer) ist der, daß man (mehr oder weniger) regelmäßig den Pegel des Beinchens prüft, und mit dem letzten Zustand des Beinchens vergleicht. Polling eben. Ob das nun bei jedem soundsovielten Durchlauf einer (mehr oder weniger dynamischen) Hauptprogrammschleife, oder eben in festen Intervallen (zB durch Timer IRQs) macht, ist dabei doch kein Unterschied.
 
Erstmal kurz was zu den Pullups:
Wenn ein, an einen Pin angeschlossener Taster als digitaler I/O ausgewertet werden soll (also binär), kann man das generell entweder machen, indem man den Flankenwechsel als solchen detektiert, oder indem man den Pegel überprüft, und feststellt, daß irgendwann mal mindestens einer stattgefunden hat.
In jedem Fall müssen 2 unterschiedliche Zustände (low/high) erkennbar sein. Also ein Grundzustand bei offenem Taster, und der andere bei geschlossenem Taster. Welcher jetzt welcher ist, ist erstmal egal. Der Pin wird bei offenem Taster über einen Pull-Widerstand auf einen sauberen Pegel gezogen, wird der Taster geschlossen, wird der Pin gegen den R auf den anderen Pegel gezwungen. Dabei ist egal, ob der R gegen Vcc oder gegen Gnd geht. Wenn man aber einen R nach Vcc verwenden kann/will, kann man auf den AVR-internen Pullup zurückgreifen, und sich das physische externe Bauteil sowie dessen Verbindung mit Vcc ersparen. Einen internen Pulldown gibt es nicht.
Außerdem kann man (zB zum Stromsparen) den internen Pullup auch deaktivieren, solange man den Taster nicht überprüft.

Aber zum eigentlichen Thema: Tasterabfrage und Interrupts:
Warum soll es denn keinen Sinn machen, die Abfragen an einen IRQ zu koppeln? Irgendwie ist das a oben etwas oberflächlich gewesen...
Wie bereits von mir angedeutet kann man entweder auf die Flanke selbst reagieren, oder die Pegel vergleichen.
Bei ersterem muß man zwingend den entsprechenden IRQ verwenden, und zwar den Flankeninterrupt des Beinchens (das ging früher nur mit INT0/INT1... und mit ein paar Klimmzügen auch mittels Zweckentfremdung anderer Hardware - inzwischen gibt es mmer mehr PCINTs).

Der zweite Weg (und um den gings hier bisher immer) ist der, daß man (mehr oder weniger) regelmäßig den Pegel des Beinchens prüft, und mit dem letzten Zustand des Beinchens vergleicht. Polling eben. Ob das nun bei jedem soundsovielten Durchlauf einer (mehr oder weniger dynamischen) Hauptprogrammschleife, oder eben in festen Intervallen (zB durch Timer IRQs) macht, ist dabei doch kein Unterschied.

1. mir brauchst du nicht erklären, was ein Pull-Up macht :D
ich habe es nur erwähnt, dass das Beispiel die Eingänge eben invertiert einliest, damit lässt es sich später auch besser verarbeiten, ich möchte eben eine "1" sehen, wenn die Taste gedrückt wurde, ansonsten hätte ich in meiner Variable eine 0, wegen Pull-Up eben; und da die Tasten auf dem STK500 auf Masse schalten, wenn gedrückt wird (ok, das hab ich vergessen zu erwähnen :D )
Dass die Tasten auf dem STK500 gegen Masse schalten, ist natürlich ein wichtiges Detail - das ich vergessen habe zu erwähnen....

2.
um einfach Tasten einzulesen, würde ich nicht die INT0/INT1 verwenden, die braucht man noramlerweise für zeitkritische(re) Dinge => das habe ich eigentlich gemeint
Eine Taste z.B. jede 20ms einzulesen, ok.... das sehe ich ein, die muss man nicht permanent in jedem Schleifendurchlauf einlesen....

Ich entschuldige mich vielmals, wenn ich mich nicht präzise genug in meiner Hektik ausgedrückt habe und wichtige Details vergessen habe , also "keep cool" :D :cool:
Ich habe mein vorheriges Posting nochmals editiert....
 
Hey... ich wollte Dich damit nicht angreifen oder so... habe halt bloß meine Gedanken dazu geschrieben/ergänzt;)

ABER irgendwo weiter vorn hatte Janiiix3 so nebenbei mal die Funktion des Projektes erwähnt: es soll eine Taschenlampe gedimmt werden...

Sind zwar nicht viele Fakten, aber ich spekuliere mal:
Der AVR steuert (mehr oder weniger direkt?) eine (mehrere?) LED(s) via PWM an. Der (die durchschnittliche Helligkeit bestimmende) DutyCycle soll über 2(?) Taster eingestellt werden.

In diesem Falle wäre sogar die Verwendung von externen Interrupts der Beinchen sinnig, weil -> Konzept:
-Flimmerfreie PWM-Frequenz für die LEDs irgendwo bei 50-100Hz wählen
-da es sicher nicht auf extrem genaue Frequenzen ankommen wird, internen Oszillator als Taktquelle (weniger Bauteile, weniger Strombedarf...)
-während die Lampe leuchtet, kann der Controller trotzdem eingeschläfert werden, wobei ein Mode verwendet werden muß, in dem der Timer weiterläuft
-ist die Lampe aus, sollte in einen tieferen Modus gewechselt werden
-die Taster müssen also in der Lage sein, den Controller wieder zu wecken, der "Hoch"-Taster sogar aus dem tiefstmöglichen Schlafmodus ->INT0/1 (oder PinChange?)
-In den ISRs der Taster-Pins ist die Entprellung zu realisieren, sowie das OCRegister anzupassen, und ggf der SleepModus zu wechseln. Anschließens wird der Controller wieder schlafen geschickt.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)