Forum: Mikrocontroller und Digitale Elektronik Atmega2561 Externer Interrupt


von Buell24 (Gast)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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

von Grrrr (Gast)


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

von Buell24 (Gast)


Angehängte Dateien:

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).
1
/************************************************************************/
2
/*                                                                      */
3
/*                      Debouncing 8 Keys                               */
4
/*                      Sampling 4 Times                                */
5
/*                      With Repeat Function                            */
6
/*                                                                      */
7
/*              Author: Peter Dannegger                                 */
8
/*                      danni@specs.de                                  */
9
/*                                                                      */
10
/************************************************************************/
11
 
12
#include <stdint.h>
13
#include <avr/io.h>
14
#include <avr/interrupt.h>
15
 
16
#ifndef F_CPU
17
#define F_CPU           1000000                   // processor clock frequency
18
#warning kein F_CPU definiert
19
#endif
20
 
21
#define KEY_DDR         DDRB
22
#define KEY_PORT        PORTB
23
#define KEY_PIN         PINB
24
#define KEY0            0
25
#define KEY1            1
26
#define KEY2            2
27
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
28
 
29
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
30
#define REPEAT_START    50                        // after 500ms
31
#define REPEAT_NEXT     20                        // every 200ms
32
 
33
#define LED_DDR         DDRA
34
#define LED_PORT        PORTA
35
#define LED0            0
36
#define LED1            1
37
#define LED2            2
38
 
39
volatile uint8_t key_state;                                // debounced and inverted key state:
40
                                                  // bit = 1: key pressed
41
volatile uint8_t key_press;                                // key press detect
42
 
43
volatile uint8_t key_rpt;                                  // key long press and repeat
44
 
45
 
46
ISR( TIMER0_OVF_vect )                            // every 10ms
47
{
48
  static uint8_t ct0, ct1, rpt;
49
  uint8_t i;
50
 
51
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
52
 
53
  i = key_state ^ ~KEY_PIN;                       // key changed ?
54
  ct0 = ~( ct0 & i );                             // reset or count ct0
55
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
56
  i &= ct0 & ct1;                                 // count until roll over ?
57
  key_state ^= i;                                 // then toggle debounced state
58
  key_press |= key_state & i;                     // 0->1: key press detect
59
 
60
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
61
     rpt = REPEAT_START;                          // start delay
62
  if( --rpt == 0 ){
63
    rpt = REPEAT_NEXT;                            // repeat delay
64
    key_rpt |= key_state & REPEAT_MASK;
65
  }
66
}
67
 
68
///////////////////////////////////////////////////////////////////
69
//
70
// check if a key has been pressed. Each pressed key is reported
71
// only once
72
//
73
uint8_t get_key_press( uint8_t key_mask )
74
{
75
  cli();                                          // read and clear atomic !
76
  key_mask &= key_press;                          // read key(s)
77
  key_press ^= key_mask;                          // clear key(s)
78
  sei();
79
  return key_mask;
80
}
81
 
82
///////////////////////////////////////////////////////////////////
83
//
84
// check if a key has been pressed long enough such that the
85
// key repeat functionality kicks in. After a small setup delay
86
// the key is reported beeing pressed in subsequent calls
87
// to this function. This simulates the user repeatedly
88
// pressing and releasing the key.
89
//
90
uint8_t get_key_rpt( uint8_t key_mask )
91
{
92
  cli();                                          // read and clear atomic !
93
  key_mask &= key_rpt;                            // read key(s)
94
  key_rpt ^= key_mask;                            // clear key(s)
95
  sei();
96
  return key_mask;
97
}
98
 
99
///////////////////////////////////////////////////////////////////
100
//
101
uint8_t get_key_short( uint8_t key_mask )
102
{
103
  cli();                                          // read key state and key press atomic !
104
  return get_key_press( ~key_state & key_mask );
105
}
106
 
107
///////////////////////////////////////////////////////////////////
108
//
109
uint8_t get_key_long( uint8_t key_mask )
110
{
111
  return get_key_press( get_key_rpt( key_mask ));
112
}
113
 
114
int main( void )
115
{
116
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
117
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
118
 
119
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
120
  TIMSK |= 1<<TOIE0;        // enable timer interrupt
121
 
122
  LED_PORT = 0xFF;
123
  LED_DDR = 0xFF;                     
124
 
125
  sei();
126
 
127
  while(1){
128
    if( get_key_short( 1<<KEY1 ))
129
      LED_PORT ^= 1<<LED1;
130
 
131
    if( get_key_long( 1<<KEY1 ))
132
      LED_PORT ^= 1<<LED2;
133
 
134
                                                  // single press and repeat
135
 
136
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 )){
137
      uint8_t i = LED_PORT;
138
 
139
      i = (i & 0x07) | ((i << 1) & 0xF0);
140
      if( i < 0xF0 )
141
        i |= 0x08;
142
      LED_PORT = i;      
143
    }
144
  }
145
}

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

von Karl H. (kbuchegg)


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
1
#define KEY_DDR         DDRB
2
#define KEY_PORT        PORTB
3
#define KEY_PIN         PINB
4
#define KEY0            0
5
#define KEY1            1
6
#define KEY2            2
7
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
8
 
9
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)       // repeat: key1, key2
10
#define REPEAT_START    50                        // after 500ms
11
#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.

von Karl H. (kbuchegg)


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)

von Buell24 (Gast)


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?
1
#define KEY_DDR         DDRE
2
#define KEY_PORT        PORTE
3
#define KEY_PIN         PINE
4
#define UP             6
5
#define DOWN            7
6
#define ALL_KEYS        (1<<UP | 1<<DOWN )
7
 
8
#define REPEAT_MASK     (1<<UP | 1<<DOWN)       // repeat: key1, key2
9
#define REPEAT_START    50                        // after 500ms
10
#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;
1
  TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
2
  TIMSK |= 1<<TOIE0;        // enable timer interrupt

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

von Karl H. (kbuchegg)


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;
>
1
>   TCCR0 = (1<<CS02)|(1<<CS00);      // divide by 1024
2
>   TIMSK |= 1<<TOIE0;        // enable timer interrupt
3
>

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.

von Buell24 (Gast)


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:
1
int main( void ){
2
3
  serial_initialize(57600);
4
  KEY_DDR &= ~ALL_KEYS;                // konfigure key port for input
5
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors
6
 
7
  TCCR0B = (1<<CS02)|(1<<CS00);      // divide by 1024
8
  TIMSK0 |= 1<<TOIE0;        // enable timer interrupt                    
9
 
10
  sei();
11
 
12
  while(1){
13
    if( get_key_short( 1<<UP ))
14
      printf( "UP gedrückt \n");
15
 
16
    if( get_key_long( 1<<UP ))
17
      printf( "UP lange gedrückt \n");
18
19
    if( get_key_short( 1<<LEFT ))
20
      printf( "UP gedrückt \n");
21
 
22
    if( get_key_long( 1<<LEFT ))
23
      printf( "UP lange gedrückt \n");
24
 
25
    if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN )){
26
      uint8_t i = 0;
27
 
28
      i = (i & 0x07) | ((i << 1) & 0xF0);
29
      if( i < 0xF0 )
30
        i |= 0x08;
31
      printf( "i= %d \n" ,i);      
32
    }
33
34
    // ----------------------------------------------------
35
    //                    weiterer Code
36
    // ----------------------------------------------------
37
38
  }
39
}

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?

von Karl H. (kbuchegg)


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)

1
uint8_t state;
2
3
int main()
4
{
5
  ...
6
7
8
  state = 0;
9
10
  while( 1 ) {
11
12
    switch( state ) {
13
14
      case 0:        // Grundzustand
15
        if( get_key_short( 1<<UP )) {    // Tastendruck
16
          LED_PORT |= ( 1 << LED_RED );
17
          state = 1;
18
        }
19
        break;
20
21
      case 1:
22
        time_counter = 1000;
23
        state = 2;
24
        break;
25
26
      case 2:
27
        time_counter--;
28
        if( time_counter == 0 )
29
          state = 3;
30
        break;
31
32
      case 3:
33
        LED_PORT &= ~( 1 << LED_RED );
34
        state = 0;
35
    }
36
  }
37
}

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Buell24 (Gast)


Lesenswert?

Ok vielen Dank für eure zahlreichen Antworten! Habt mir wirklich sehr 
geholfen!

gruss Buell24

von Karl H. (kbuchegg)


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
1
volatile uint16_t timeCounter;
2
3
ISR( TIMER0_OVF_vect )                            // every 10ms
4
{
5
  static uint8_t ct0, ct1, rpt;
6
7
  ...
8
9
  // Entprellkram
10
11
12
  if( timeCounter > 0 )
13
    timeCounter--;
14
}

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
1
#define DEFAULT         0
2
#define WAIT_FOR_TIME   1
3
#define CLEAR_LED       2
4
5
....
6
7
8
  while( 1 ) {
9
10
    switch( state ) {
11
12
      case DEFAULT:        // Grundzustand
13
        if( get_key_short( 1<<UP )) {    // Tastendruck?
14
          LED_PORT |= ( 1 << LED_RED );
15
          cli();
16
          timeCounter = 500;               // 5 Sekunden Timer starten
17
          sei();
18
          state = WAIT_FOR_TIME;
19
        }
20
        break;
21
22
      case WAIT_FOR_TIME:  // die Wartezeit abwarten
23
        cli();
24
        if( timeCounter == 0 )             // Zeit abgelaufen?
25
          state = CLEAR_LED;
26
27
        if( get_key_short( 1<<DOWN )) {    // Tastendruck?
28
          timeCounter = 0;                 // Timer stoppen
29
          state = CLEAR_LED;               
30
        }
31
32
        sei();
33
        break;
34
35
      case CLEAR_LED:     // Wartezeit ist vorbei
36
        LED_PORT &= ~( 1 << LED_RED );
37
        state = DEFAULT;
38
    }
39
  }
40
}

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.

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.