mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Tastenanschläge zählen in c (ATMEGA32)


Autor: MicroDave (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich hab ein glaub ich relativ einfaches Problem, welches ich aber 
irgendwie nicht gelöst bekomme. Also ich löse mit einem Taster (an PD2 
angeschlossen) die ISR aus. Nun möchte ich das ich bei jedem durchlauf 
dieser Routine um 1 hochgezählt wird. Also auf Deutsch ich möchte die 
Tastenanschläge von PD2 zählen und diese in einer Variable an die main 
übergeben? Entprellt hab ich den Taster und in die ISR komm ich 
auch(klappt mit einem anderen Programm super)!

Kann irgendwer helfen??

MfG

Autor: SF (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohne deinen Code zu kennen, gibt es leider zu viel unbekanntes, als das 
man deine Frage wirklich beantworten könnte.

Wo liegt denn dein konkretes Problem?

Kannst du die Tastenanschläge nicht zählen? Weil eventuell zu viele 
gezählt werden, Dann funktioniert dein entprellen nicht richtig!

Oder Zählst du zu wenige Tastenanschläge? Auch dann würde ich auf deine 
Entprellung tippen.

Oder schaffst du es nicht, die richtig gezählte Anzahl aus der ISR an 
das Hauptprogramm zu übermitteln?

Für letzteres würde man eine globale Variable verwenden, die allerdings 
volatile deklariert werden muss, damit es vernünftig klappt.

Außerdem musst du, wenn die Variable größer als 1 Byte ist, dafür 
sorgen, das der Zugriff auf die Variable im Hauptprogramm nicht durch 
die ISR unterbrochen werden kann. Also am besten kurz vorher die 
Interrupts sperren und danach freigeben: 
http://www.mikrocontroller.net/articles/Interrupt#...

Autor: MicroDave (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hey Danke für die schnelle Antwort!

Hier ist mein Code:

#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>

volatile int32_t a = 0;
ISR(INT0_vect)
  {
   a=a+1;
  }

int main(void)

{
  DDRB |= (1<<PB4);

  DDRD &= ~(1<<PD2);
  PORTD |= (1<<PD2);

  MCUCR |= (1<<ISC01) | (1<<ISC00);
  GICR |= (1<< INT0);
  sei();

if(a==1)
{
  PORTB |= (1<<PB4);
}

while(1);
}

Autor: SF (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das erste, was mir aufgefallen ist, ist, das a als int32_t deklariert 
ist. Muss das sein? Der Atmel ist ein 8-Bit Prozessor. 32Bit 
Additionen/Vergleiche sind für einen 8-Bitter recht aufwendig. Erwartest 
du wirklich 2.14 Billionen Tastendrücke? Ich würde a auch nicht als int 
sondern als unsigned int deklarieren.

Dann fiel mir auf, das du im Code überhaupt keine Entprellung hast! Ein 
real existierender Taster prellt aber immer! Ein Tastendruck prellt 
etliche male nach, so das du diesen einen Tastendruck 10, 20mal zählst.

Ist ist deshalb nicht besonders klug den Taster direkt einen Interrupt 
auslösen zu lassen. Besser ist es den Taster innerhalb eines 
periodischen Timerinterrupts abzufragen, oder falls du keine 
zeitkritische Anwendung im Sinn hast, diesen mit der Warteschleifen 
Methode abzufragen.

Entprellung: http://www.mikrocontroller.net/articles/Entprellung

Dann ist natürlich auch sofort klar weshalb dein Code nicht 
funktioniert:
if(a==1)
{
  PORTB |= (1<<PB4);
}

Aufgrund des Prellen ist es sehr unwahrscheinlich das a genau zu dem 
einen und einzigen Zeitpunkt, wo du a abfragst den Wert 1 hat. Der 
Vergleich wird also niemals true!

Außerdem ist die Position des Vergleichs schlecht gewählt. Er müsste in 
der while schleife sein und ist leider auch nicht mit cli()/sei() 
interruptfest gestaltet.

Autor: MicroDave (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also entprellt ist die Taste mit mit na schönen 100nF Kapazität!

Ist der Zähler sonst in Ordnung so? also müsste a um 1 erhöht werden 
wenn ich die Taste drücke?

Autor: Modernator (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
int main(void){
  DDRB |= (1<<PB4);

  DDRD &= ~(1<<PD2);
  PORTD |= (1<<PD2);

  MCUCR |= (1<<ISC01) | (1<<ISC00);
  GICR |= (1<< INT0);
  sei();

  if(a==1)
  {
    PORTB |= (1<<PB4);
  }

  while(1) {
    //hier sollte der "a"-Vergleich stehen!
  } //ENDLOSSCHLEIFE
}

Deine Code ist falsch. Du prüfst am Anfang genau einmal, ob die Anzahl 
der Tastendrücke stimmt, danach gehst Du in eine Endlosschleife und dein 
Hauprprogramm steht still.

Prüfe die Anzahl der Tastendrücke am Besten innerhalb der 
Endlosschleife!

Autor: MicroDave (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aber wenn ich in die ISR springe, was er durch Tastendruck ja macht, 
läuft er die main doch von vorne durch oder nicht? das heißt er müsste 
den wert von a doch wieder neu vergleichen!?! oder versgteh ich das 
falsch??

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
MicroDave schrieb:
> Aber wenn ich in die ISR springe, was er durch Tastendruck ja macht,
> läuft er die main doch von vorne durch oder nicht?

Das wäre aber fatal.

Nein.
Ein Interrupt unterbricht die momentane Programmausführung, bei dir die 
while-Schleife.

Nach Beendigung der ISR geht es genau dort weiter, wo der Interrupt 
unterbrochen hat. Genau das ist ja der Sinn eines Interrupts: Das 
normale Hauptprogramm arbeitet vor sich hin. Plötzlich tritt ein 
Ereignis auf, welches von der ISR kurzfristig behandelt wird und danach 
macht das Hauptptogramm weiter als ob nichts passiert wäre.

Du bastelst an deinen Projekten ja auch dort weiter wo du aufgehört hast 
wenn die Unterbrechung "Mittagessen" vorbei ist und fängst nicht 
jedesmal wieder von vorne an.

Autor: MicroDave (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh man stimmt! Sorry das wär ja sonst sowas wie ein Reset. Also ich habe 
jetzt mal meine Code etwas verändert! Ich will ja die Variable a wieder 
zurückgeben, aber irgendwie geht das nicht so wie ich mir das vorstelle. 
Er soll bei Tastendruck die Variable a toggeln und diesen neuen Wert an 
die do-while Schleife übergeben. Steh voll aufm Schlauch!

#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>

volatile int8_t a;
ISR(INT0_vect)
{
  a ^= 1;
}

int main(void)

{
  DDRB |= (1<<PB4);

  DDRD &= ~(1<<PD2);
  PORTD |= (1<<PD2);
  MCUCR |= (1<<ISC01) | (1<<ISC00);
  GICR |= (1<< INT0);
  sei();

do
{
   PORTB |= (1<<PB4);
}
while(a==1);

while(1)
{
  return a;
}

}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
MicroDave schrieb:
> Oh man stimmt! Sorry das wär ja sonst sowas wie ein Reset. Also ich habe
> jetzt mal meine Code etwas verändert! Ich will ja die Variable a wieder
> zurückgeben,

zurückgeben an wen?
Dein Programm ist schon im Hauptprogramm, da ist nichgts mehr, an das du 
einen Wert zurückgeben könntest!
int main(void)
{
  DDRB |= (1<<PB4);

  DDRD &= ~(1<<PD2);
  PORTD |= (1<<PD2);
  MCUCR |= (1<<ISC01) | (1<<ISC00);
  GICR |= (1<< INT0);
  sei();
 
  while( 1 ) {
    if( a > 1 )
      PORTB |= (1<<PB4);
  }
}

Du solltest wirklich mit einfacheren Dingen als mit Interrupts anfangen, 
wenn dir das Verständnis dessen was hier passiert Schwierigkeiten macht.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Du solltest wirklich mit einfacheren Dingen als mit Interrupts anfangen,
> wenn dir das Verständnis dessen was hier passiert Schwierigkeiten macht.

Voll zustimm, das ist für Dich mehrere Nummern zu groß.

Also erstmal:
- LED an,
- LED blinken mit Delay,
- LED blinken mit Timer,
- LED blinken mit Timerinterrupt,
- LED mit Taste an/aus,
- LED mit Taste toggle
- 2 LEDs mit 2 Tasten toggle

Dann bist Du bereit.


Peter

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.