www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Atmega2561 Externer Interrupt


Autor: Buell24 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich bin seit einigen Tagen am Programmieren eines Atmel 2561 
Mikrokontrollers. Nun komme ich jedoch beim programmieren der Interrupts 
nicht mehr weiter. Ich habe bereits das AVR-GCC-Tutorial durchgelesen, 
es hat mich jedoch nicht weitergebracht. Nun ersuche ich euch um rat. Im 
prinzip möchte ich zu beginn nur eine LED über einen Taster ein bzw. 
ausschalten und dies über Interrupts. Ich bekomme dies aber einfach 
nicht hin!
Hier mein code-Gerüst:

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <inttypes.h>

#define BTN_UP 0x10  //taster

ISR(INT4_vect){
    Set_LED(LED_AUX,LED_TOGGLE);  //led toggle (Methode funktioniert)
}


int main(void){
  DDRE  = 0x0C; //0000'1100  PortE 4-7
  PORTE = 0xF0; //1111'0000
  sei(); //einschalten der Interrupts.
  serial_initialize(57600);
  while(1){
  }
  return 1;
}

Ich bin nicht gerade ein C spezialist, darum bitte ich euch um eine 
ausführliche Antwort.

Gruss Buell24

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Einen Interrupt musst du freischalten, bevor du ihn benutzen kannst.

"Taste" und "Interrupt" passt nicht zusammen: mechanische Tasten
prellen, du hast N Interrupts hintereinander dann, das willst du
nicht.  Schau dir die Entprellroutine in der Artikelsammlung an.

(Es gibt nur einen Grund, warum man eine mechanische Taste gelegentlich
doch einen Interrupt auslösen lassen will: aufwecken des Controllers
aus dem sleep.  Dann schaltet die ISR aber sofort den Externinterrupt
ab, und während die Applikation läuft, wird wieder die normale
Entprellfunktion benutzt.)

Autor: Grrrr (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Buell24 schrieb:
> Ich bekomme dies aber einfach
> nicht hin!

Buell24 schrieb:
> ...bitte ich euch um eine ausführliche Antwort.

Die Antwort wird so ausführlich sein, wie es die Frage erlaubt.
Auf die obige Aussage: "Ich bekomme dies aber einfach nicht hin!" kann 
man nur antworten: Du löst das Problem, in dem Du es so detailliert 
beschreibst, das Du Ansätze für eine Untersuchung der Ursache hast. Dann 
stellst Du die Ursache fest und beseitigst sie.

Siehe Netiquette und insbesondere den Artikel hier: 
http://www.tty1.net/smart-questions_de.html

Autor: Buell24 (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Jörg!

Vielen Dank für den Tipp mit der Entprellung! Habe mich da nun schlau 
gemacht. Im Artikel Entprellung hat es einen Quellcode welcher genau 
dem entspricht, was ich brauche. Das Interrupt-Verfahren (nach Peter 
Dannegger).
/************************************************************************/
/*                                                                      */
/*                      Debouncing 8 Keys                               */
/*                      Sampling 4 Times                                */
/*                      With Repeat Function                            */
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/*                      danni@specs.de                                  */
/*                                                                      */
/************************************************************************/
 
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
 
#ifndef F_CPU
#define F_CPU           1000000                   // processor clock frequency
#warning kein F_CPU definiert
#endif
 
#define KEY_DDR         DDRB
#define KEY_PORT        PORTB
#define KEY_PIN         PINB
#define KEY0            0
#define KEY1            1
#define KEY2            2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
 
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms
 
#define LED_DDR         DDRA
#define LED_PORT        PORTA
#define LED0            0
#define LED1            1
#define LED2            2
 
volatile uint8_t key_state;                                // debounced and inverted key state:
                                                  // bit = 1: key pressed
volatile uint8_t key_press;                                // key press detect
 
volatile uint8_t key_rpt;                                  // key long press and repeat
 
 
ISR( TIMER0_OVF_vect )                            // every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;
 
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
 
  i = key_state ^ ~KEY_PIN;                       // key changed ?
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll over ?
  key_state ^= i;                                 // then toggle debounced state
  key_press |= key_state & i;                     // 0->1: key press detect
 
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
     rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
  }
}
 
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}
 
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed long enough such that the
// key repeat functionality kicks in. After a small setup delay
// the key is reported beeing pressed in subsequent calls
// to this function. This simulates the user repeatedly
// pressing and releasing the key.
//
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}
 
///////////////////////////////////////////////////////////////////
//
uint8_t get_key_short( uint8_t key_mask )
{
  cli();                                          // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}
 
///////////////////////////////////////////////////////////////////
//
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}
 
int main( void )
{
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
 
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
  TIMSK |= 1<<TOIE0;        // enable timer interrupt
 
  LED_PORT = 0xFF;
  LED_DDR = 0xFF;                     
 
  sei();
 
  while(1){
    if( get_key_short( 1<<KEY1 ))
      LED_PORT ^= 1<<LED1;
 
    if( get_key_long( 1<<KEY1 ))
      LED_PORT ^= 1<<LED2;
 
                                                  // single press and repeat
 
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
      uint8_t i = LED_PORT;
 
      i = (i & 0x07) | ((i << 1) & 0xF0);
      if( i < 0xF0 )
        i |= 0x08;
      LED_PORT = i;      
    }
  }
}

Könnt Ihr mir nun sagen, was ich wie verändern muss, um diesen Code für 
meinen Atmega 2561 nutzen zu können? Die beschaltung habe ich angefügt. 
Die Schalter sind an Pin 6 und 7 angeschlossen

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

Bewertung
0 lesenswert
nicht lesenswert
Buell24 schrieb:

> Könnt Ihr mir nun sagen, was ich wie verändern muss, um diesen Code für
> meinen Atmega 2561 nutzen zu können?

Du setzt hier
#define KEY_DDR         DDRB
#define KEY_PORT        PORTB
#define KEY_PIN         PINB
#define KEY0            0
#define KEY1            1
#define KEY2            2
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
 
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20                        // every 200ms

die für dich relevanten Daten ein

* An welchem Port hängen die Tasten? Welches ist daher das DDR, welches
  das Port Register und welches das Pin Register

* Wieviele Tasten hast du?
  denen gibst du deinem Projekt angemessene Namen
  Hier im Beispiel heißen sie KEY0, KEY1 und KEY2.
  Das muss aber nicht so sein, du kannst sie nennen wie du
  willst. Zb FOWARD oder BACKWARD oder ALARM oder ON oder OFF
  Je nachdem was in deinem Projekt angemessen ist, so dass du
  in späterer Folge nicht durcheinander kommst, welchen Taster
  du gerade abfrägst.
  Wichtig ist nur, dass du bei jedem Makronamen dann auch noch den
  korrekten Pin angibts, an dem die Taste hängt. Bei dir dann eben
  6 und 7.

* Dann fasst du alle Tasten im Makro ALL_KEYS noch einmal zusammen

* Du legst fest, welche Tasten du mit Auto-Repeat haben willst
  und führst die im Makro REPEAT_MASK auf

* Die Repeat Zeiten kannst du erst mal so lassen wie sie sind.


Bei ALL_KEYS bzw. REPEAT_MASK orientierst du dich einfach an der 
Schreibweise, wie sie dort bereits steht.

Fertig.
Das erste Testprogramm kann entsprechend geschrieben (bzw. aus der Demo 
entsprechend abgeleitet) werden.

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

Bewertung
0 lesenswert
nicht lesenswert
Ach ja.
F_CPU korrekt einsetzen, jab ich noch vergessen. Aber speziell für diese 
Entprellung ist es auch nicht so schlimm, wenn der Wert nicht stimmt. 
Funktionieren wird sie trotzdem. Die Autorepeat Zeiten werden dann nicht 
ganz stimmen, aber da kommt man früh genug drauf (oder auch nicht)

Autor: Buell24 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Karl

Vielen Dank für deine schnelle Antwort. Ich habe die paar Zeilen Code 
nun angepasst. Müsste so in etwa stimmen oder?
#define KEY_DDR         DDRE
#define KEY_PORT        PORTE
#define KEY_PIN         PINE
#define UP             6
#define DOWN            7
#define ALL_KEYS        (1<<UP | 1<<DOWN )
 
#define REPEAT_MASK     (1<<UP | 1<<DOWN)       // repeat: key1, key2
#define REPEAT_START    50                        // after 500ms
#define REPEAT_NEXT     20 

Jedoch bekomme ich beim compilieren (AVR-Studio 4) Fehler:

../Entprellung.c:118: error: 'TCCR0' undeclared (first use in this 
function)
../Entprellung.c:118: error: (Each undeclared identifier is reported 
only once
../Entprellung.c:118: error: for each function it appears in.)
../Entprellung.c:119: error: 'TIMSK' undeclared (first use in this 
function)

alle betreffen die Funktionen TCCR0 und TIMSK;
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
  TIMSK |= 1<<TOIE0;        // enable timer interrupt

Betreffend der F_CPU habe ich die 1000000 stehen lassen. Oder von wo 
bekomme ich den richtigen Wert?

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

Bewertung
0 lesenswert
nicht lesenswert
Buell24 schrieb:

>
> ../Entprellung.c:118: error: 'TCCR0' undeclared (first use in this
> function)
> ../Entprellung.c:118: error: (Each undeclared identifier is reported
> only once
> ../Entprellung.c:118: error: for each function it appears in.)
> ../Entprellung.c:119: error: 'TIMSK' undeclared (first use in this
> function)
>
> alle betreffen die Funktionen TCCR0 und TIMSK;
>
>   TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
>   TIMSK |= 1<<TOIE0;        // enable timer interrupt
> 

Dann musst du dir eben raussuchen, wie die betreffenden Register des 
Timer 0 auf deinem µC heißen, bzw. welche Bits wo angesiedelt sind.

Also: Datenblatt hervorkramen.
In welchem Register muessen welche Bits gesetzt werden, damit der Timer 
einen Vorteiler von 1024 hat

In welchem Register muss welches Bit gesetzt werden, damit der Timer 0 
bei einem Overflow einen INterrupt auslöst.

Hinweis: Die Register bzw. Bits werden auf deinem Mega sehr ähnlich 
heißen.


> Betreffend der F_CPU habe ich die 1000000 stehen lassen. Oder von wo
> bekomme ich den richtigen Wert?

Den weißt du, weil nur du wissen kannst welchen Quarz (wenn überhaupt) 
du an deinem µC betreibst.

Autor: Buell24 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für eure Antworten. Ich habe dies mit der entprellung nun 
hinbekommen. Jetzt habe ich jedoch ein neues Progblem. Die Taster werden 
jetzt in der main Methode in einer endlos Schleife abgefragt:
int main( void ){

  serial_initialize(57600);
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
 
  TCCR0B = (1<<CS02)|(1<<CS00);      // divide by 1024
  TIMSK0 |= 1<<TOIE0;        // enable timer interrupt                    
 
  sei();
 
  while(1){
    if( get_key_short( 1<<UP ))
      printf( "UP gedrückt \n");
 
    if( get_key_long( 1<<UP ))
      printf( "UP lange gedrückt \n");

    if( get_key_short( 1<<LEFT ))
      printf( "UP gedrückt \n");
 
    if( get_key_long( 1<<LEFT ))
      printf( "UP lange gedrückt \n");
 
    if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN )){
      uint8_t i = 0;
 
      i = (i & 0x07) | ((i << 1) & 0xF0);
      if( i < 0xF0 )
        i |= 0x08;
      printf( "i= %d \n" ,i);      
    }

    // ----------------------------------------------------
    //                    weiterer Code
    // ----------------------------------------------------

  }
}

Nun habe ich jedoch in dieser Endlosschleife noch einen hauffen anderer 
Code, welcher dort abläuft. Dementsprechend geht es dann lange, bis die 
Taster wieder abgefragt werden. Giebt es da abhilfe?

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

Bewertung
0 lesenswert
nicht lesenswert
Buell24 schrieb:

> hinbekommen. Jetzt habe ich jedoch ein neues Progblem. Die Taster werden
> jetzt in der main Methode in einer endlos Schleife abgefragt:

Yep.
So ist das gedacht


> Nun habe ich jedoch in dieser Endlosschleife noch einen hauffen anderer
> Code, welcher dort abläuft.

Was macht der?

> Dementsprechend geht es dann lange, bis die
> Taster wieder abgefragt werden. Giebt es da abhilfe?

Nicht so große Haufen auf einmal abarbeiten. Keine _delay Funktionen.
Insbesondere letzteres ist der Schlüssel zu Programmen, die

* scheinbar mehrere Dinge gleichzeitig machen
* gutes Ansprechverhalten auf Benutzereingaben zeigen

Das Motto lautet: "Aktiv gewartet wird nicht. Auf nix und niemanden".

Wenn ein Programmablauf sich natürlich in mehrere Phasen unterteilen 
lässt (ist eigentlich meistens möglich), dann ist ein 
"Zustands-Maschine" Ansatz oftmals ein guter Weg.

Das Programm ist zu jeder Zeit in einem bestimmten Zustand, ausgedrückt 
durch einen Wert in einer Variable. Bestimmte Ereignisse können diesern 
Zutand ändern und abhängig vom Zustand macht das Programm bestimmte 
Dinge (je nach Aufgabenstellung)

uint8_t state;

int main()
{
  ...


  state = 0;

  while( 1 ) {

    switch( state ) {

      case 0:        // Grundzustand
        if( get_key_short( 1<<UP )) {    // Tastendruck
          LED_PORT |= ( 1 << LED_RED );
          state = 1;
        }
        break;

      case 1:
        time_counter = 1000;
        state = 2;
        break;

      case 2:
        time_counter--;
        if( time_counter == 0 )
          state = 3;
        break;

      case 3:
        LED_PORT &= ~( 1 << LED_RED );
        state = 0;
    }
  }
}

Nur so als Beispiel.
In einigen Zuständen können extern Ereignisse passieren. zb eine Taste 
gedrückt werden. Also Folge davon passiert was, zb eine LED einschalten 
oder ein Timer gestartet oder was auch immer. In anderen Zuständen 
werden Datenmanipulationen gemacht, wobei auch spezielle Ergebnisse zu 
einem Zustandswechsel führen, etc.
Das Programm ist zu jedem Zeitpunkt in einem Zustand, welcher festlegt 
was zu geschehen hat. Das kann natürlich auch sein: Nichts tun und nur 
darauf warten dass ein Eingang 1 wird (oder so) und erst dann in wieder 
einen anderen Zustand wechseln.

So etwas ist eine ganz gute Möglichkeit, wie man aufeinanderfolgende 
Abläufe in mehrere Zustände aufteilen kann, die nacheinander ablaufen

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Das Motto lautet: "Aktiv gewartet wird nicht. Auf nix und niemanden".

Kann man dann später ein wenig relativieren: wenn der Aufwand, einen
Timer aufzusetzen, größer wird, als die Zeit, die man warten will,
dann kann man schon auch mal aktiv warten.  Typischerweise handelt
es sich dabei um Zeiten im Mikrosekundenbereich (beispielsweise
Verzögerungszeiten bei der Ansteuerung von LCDs oder sowas).

Alle längeren Zeiten (also π*Daumen alles, was im Millisekundenbereich
liegt) organisiert man sich jedoch mit Timern.

Autor: Buell24 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok vielen Dank für eure zahlreichen Antworten! Habt mir wirklich sehr 
geholfen!

gruss Buell24

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

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:

> Alle längeren Zeiten (also π*Daumen alles, was im Millisekundenbereich
> liegt) organisiert man sich jedoch mit Timern.


Yep.
Und da kommt dann auch sehr oft der Timer, der beireits jetzt die 
Tastenentprellung macht, nützlich ins Spiel.
Dadurch hat man schon einen 'Programmtakt' der bei 10ms liegt. In die 
ISR kann man dann auch noch wunderbar eine Zählervariable einbauen und 
sich so einfach Zeiten abmessen

zb im Zusammenspiel mit einer Zustandsmaschein so
volatile uint16_t timeCounter;

ISR( TIMER0_OVF_vect )                            // every 10ms
{
  static uint8_t ct0, ct1, rpt;

  ...

  // Entprellkram


  if( timeCounter > 0 )
    timeCounter--;
}

In der ISR wird die Variable timeCounter heruntergezählt solange sie 
nicht 0 ist. Setze ich die ausserhalb auf zb 100, dann weiß ich das es 
100 * 10ms dauert, = 1Sekunde, bis sie wieder 0 ist.

In der Zustandsmaschine
#define DEFAULT         0
#define WAIT_FOR_TIME   1
#define CLEAR_LED       2

....


  while( 1 ) {

    switch( state ) {

      case DEFAULT:        // Grundzustand
        if( get_key_short( 1<<UP )) {    // Tastendruck?
          LED_PORT |= ( 1 << LED_RED );
          cli();
          timeCounter = 500;               // 5 Sekunden Timer starten
          sei();
          state = WAIT_FOR_TIME;
        }
        break;

      case WAIT_FOR_TIME:  // die Wartezeit abwarten
        cli();
        if( timeCounter == 0 )             // Zeit abgelaufen?
          state = CLEAR_LED;

        if( get_key_short( 1<<DOWN )) {    // Tastendruck?
          timeCounter = 0;                 // Timer stoppen
          state = CLEAR_LED;               
        }

        sei();
        break;

      case CLEAR_LED:     // Wartezeit ist vorbei
        LED_PORT &= ~( 1 << LED_RED );
        state = DEFAULT;
    }
  }
}

Das wäre dann eine Zustandsmaschine, bei der eine LED nach einem Druck 
auf die Taste UP genau 5 Sekunden leuchtet und danach wird die LED 
wieder ausgeschaltet. Die 5 Sekunden sind realisiert, ohne dass irgendwo 
aktiv gewartet wird. Wird innerhalb dieser 5 Sekunden die andere Taste 
gedrückt, so wird die LED sofort gelöscht und alles ist wieder in 
Grundstellung. Dadurch, das hier nirgends mittels _delay_xx gewartet 
wird, bleibt die volle Rechenleistung erhalten und es können scheinbar 
mehrere Dinge gleichzeitig (Zeit abwarten + Taste überwachen) gemacht 
werden.

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.