Forum: Mikrocontroller und Digitale Elektronik AVR Einstieg / Probleme mit Programm Interrupt


von Daniel S. (dsn1980)


Lesenswert?

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
int WaitInMS;
14
15
void GetWaitTime()
16
{
17
  for (int i = 0; i < WaitInMS; i++)
18
    _delay_ms(1);
19
}
20
21
int main(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

von STK500-Besitzer (Gast)


Lesenswert?

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!

von holger (Gast)


Lesenswert?

>int WaitInMS;

volatile uint16_t WaitInMS;

>  WaitInMS = WaitInMS -10;

Was machst du wenn WaitInMS hier Null war;)

von Daniel S. (dsn1980)


Lesenswert?

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?

von Andreas W. (geier99)


Lesenswert?

Daniel Stratmann schrieb:
>
1
/*
2
>   DDRD  &= ~(1<<DDD2) | (1<<DDD3);                              //setzt
3
> PD2 (INT0) und PD3 (INT1) auf Eingang
4
> 
5
> }

aber nur zufällig, da PD3 nach dem Reset schon auf Eingang konfiguriert 
ist.

richtig wäre:

DDRD  &= ~((1<<DDD2) | (1<<DDD3));

Gruß
 Andi

von Daniel S. (dsn1980)


Lesenswert?

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.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

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?

von Daniel S. (dsn1980)


Lesenswert?

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.

von holger (Gast)


Lesenswert?

>  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.

von Steffen H. (avrsteffen)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Daniel S. (dsn1980)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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)

von Karl H. (kbuchegg)


Lesenswert?

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!

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Daniel Stratmann schrieb:
> 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.

Eventuell ein Problem in der Hardware? Vielleicht sind zwei Leitungen 
irgendwie verbunden?

von Steffen H. (avrsteffen)


Lesenswert?

Hänge bitte nochmal dein jetziger komlettes Programm hier an, wo es mit 
PORTB nicht klappt. Sonst ist das hier nur RÄTSELRATEN

von holger (Gast)


Lesenswert?

>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?

von Daniel S. (dsn1980)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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 ) )

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von Daniel S. (dsn1980)


Lesenswert?

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.

von Daniel S. (dsn1980)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Daniel S. (dsn1980)


Lesenswert?

Da muß ich dir ja recht geben. Auf dem PC stört es mich halt nicht so. 
habs mal direkt geändert.

von bitte löschen (Gast)


Angehängte Dateien:

Lesenswert?

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

von STK500-Beistzer (Gast)


Lesenswert?

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?!

von bitte löschen (Gast)


Lesenswert?

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!

von STK500-Besitzer (Gast)


Lesenswert?

Philipp K. schrieb:
> Offensichtlich nicht mal den ersten Absatz!

Nee, da war mir schon zu viel Geschwafel bei.

von bitte löschen (Gast)


Lesenswert?

STK500-Besitzer schrieb:
> (Gast)

Feigling!

von STK500-Besitzer (Gast)


Lesenswert?

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.

von bitte löschen (Gast)


Lesenswert?

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!

von Gerhard W. (dd4da) Flattr this


Lesenswert?

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.

von Daniel S. (dsn1980)


Lesenswert?

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.

von Gerhard W. (dd4da) Flattr this


Lesenswert?

----- 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

von STK500-Besitzer (Gast)


Lesenswert?

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).

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von Karl H. (kbuchegg)


Lesenswert?

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_t key_state;
2
uint8_t key_counter;
3
volatile uint8_t key_press;
4
5
ISR( ... Overflow ... )
6
{
7
  uint8_t input = 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_t get_key_press()
24
{
25
  uint8_t result;
26
27
  cli();
28
  result = key_press;
29
  key_press = FALSE;
30
  sei();
31
32
  return result;
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.

von Gerhard W. (dd4da) Flattr this


Lesenswert?

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.

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.