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
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.)
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
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
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.
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)
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?
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.
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?
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
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.
Ok vielen Dank für eure zahlreichen Antworten! Habt mir wirklich sehr geholfen! gruss Buell24
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.