Hallo Leute, habe mal eine Frage bezüglich dem externen Interrupt INT0. Ich habe den Interrupt so eingestellt, dass er bei einem Falling-Edge ausgelöst wird. Hierzu habe ich einen Taster an den PIND2 meines ATmega328P angeschlossen, der wiederrum mit GND verbunden ist. Zudem habe ich den internen Pull-Up-Widerstand an PIND2 aktiviert und den PIN als INPUT definiert. Nun zu meinem Problem: Sobald ich das Programm (siehe unten) starte und die Funktion Initialize_INT0 aufgerufen wurde, wird immer (ohne Ausnahme) ein Interrupt ausgelöst. Das geschieht nur 1x am Anfang! Ich rühre den Taster in diesem Zeitraum nicht an! Könnt Ihr mir sagen, was ich da flasch gemacht habe? Ich programmiere übrigens mit Atmel Studio 6 / C++. Hier mein Programm: *********************Programm******************************* #ifndef F_CPU #define F_CPU 16000000 #endif #include <avr/io.h> #include <avr/interrupt.h> void Initialize_USART () { #define BAUD 67800 #include <util/setbaud.h> UBRR0H = UBRRH_VALUE;//set Baud-Rate in UARTBaudRateRegister (UBBR) UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif UCSR0B |= (1 << RXEN0) | (1 << TXEN0);//activate Receive & Transmit } void Initialize_TIMER0 () { TCCR0A |= (1 << WGM01); //activate TIMER0 CTC-Mode TIMSK0 |= (1 << OCIE0A);//activate interrrupt if compare value is reached OCR0A |= 249; //set compare value TCCR0B |= (1 << CS01) | (1 << CS00);//start TIMER0 Prescaler 64 } void Initialize_INT0 () { EICRA = 0x02; //INT0 is triggered by falling edge EIMSK |= (1 << INT0); //activate INT0 } int main (void) { DDRD = 0xFF; //all PINS are Output PORTD = 0x00; //all Outputs are LOW DDRD &= ~(1 << PIND2); //PIND2 is Input PORTD |= (1 << PIND2); //activate internal pull-up of PIND2 (INT0) DDRB = 0xFF; //all PINS are Outputs PORTB = 0x00; //all Outputs are LOW Initialize_USART(); //initializes USART sei(); //activates global interrupts Initialize_TIMER0(); //initializes TIMER0 Initialize_INT0(); //initializes INT0 while(1) { //do something } return 0; //never reached } ISR (INT0_vect) { //do something if button was pressed //ie: toggle LED } ISR (TIMER0_COMPA_vect) { //count time and debounce button } *********************Programm_ENDE****************************** Vielen Dank für Eure Hilfe, Steve
Hat keiner eine Idee, Vorschläge, Anschiss oder ähnliches zu posten? Bin für alles dankbar, solange es mich hier weiter bringt! Greets, Steve
Und wie überprüfst du ob ein Interrupt ausgelöst wird? Da steht nichts drinn. Bitte kompletten Code posten, es könnte ja auch daran liegen. An sich seh ich keinen Fehler mit dem Interrupt Zeug. Ein einfaches Toggeln ist nicht gut, weil durch das Prellen mehrfache Interrupts ausgelöst werden und du dann evt. gar nicht siehst ob die LED toggelt oder nicht. Quick & Dirty: Mach ein delay in die ISR nach dem Toggeln der LED. Damit siehst du wenigstens was passiert. Schön ist aber anders, und ein Taster gehört eben deshalb nicht an einen Interrupt. Weil er prellt. Es kann sein dass bei Aktivierung von sei, bereits ein Interrupt Flag gesetzt war, und somit die ISR sofort angesprungen wird. Daher wird erstmal ein Interrupt ausgelöst. Funktioniert die HW? Hast du mal mit nem Multimeter am Port gemessen? Hat der Hi-Pegel im Ruhezustand und geht er auf Lo-Pegel wenn du den Taster drückst? gruß cyblord
Hallo cyblord, hier mal der Code, mit mehr Inhalt! Ich hoffe das Hilft! Ich möchte eine Stoppuhr programmieren, die auf die Millisekunde genau Reaktionszeiten misst. Also: LED:AN -> Taster:Drücken Zeit zwischen LED:AN und Taster:Drücken an PC schicken. Deshalb dachte ich mir ich lege den Taster an einen Interrupt, um die Verzögerung mit Sicherheit zu minimieren. Hab das ganze auch mit einem Oszi überprüft, der PIN am Taster geht auf LOW wenn er gedrückt wird und auf HIGH wenn ich ihn loslasse. Wenn das allerdings auch ohne Taster am Interrupt geht dann les ich mir gerne was dazu durch, wenn du einen Link hast. *********************Programm******************************* #ifndef F_CPU #define F_CPU 16000000 #endif #include <avr/io.h> #include <avr/interrupt.h> //hoffe ich habe keine globalen vars vergessen volatile uint16_t ms; volatile uint16_t ms_Button_Click; volatile uint8_t iDebounce; volatile char cDebounceFlag; volatile char cSendFlag; void Initialize_USART () { #define BAUD 67800 #include <util/setbaud.h> UBRR0H = UBRRH_VALUE; //set Baud-Rate in UART Baud Rate Register (UBBR) UBRR0L = UBRRL_VALUE; #if USE_2X UCSR0A |= (1 << U2X0); #else UCSR0A &= ~(1 << U2X0); #endif UCSR0B |= (1 << RXEN0) | (1 << TXEN0); //activate Receive & Transmit } uint8_t Receive_Character () { while (!(UCSR0A & (1 << RXC0))) //wait while data register is full {} return UDR0; } void Send_Single_Char (unsigned char cSingle) { while(!(UCSR0A & (1 << UDRE0))) //is UART register ready to receive value {} UDR0 = cSingle; } void Send_Time (char* cWord) { while(*cWord) //as long as cWord[i] is != "\0" { Send_Single_Char(*cWord); //send cWord[i] cWord++;//i++ } } void Initialize_TIMER0 () { TCCR0A |= (1 << WGM01); //activate TIMER0 CTC-Mode TIMSK0 |= (1 << OCIE0A);//activate interrrupt if compare value is reached OCR0A |= 249; //set compare value TCCR0B |= (1 << CS01) | (1 << CS00);//start TIMER0 Prescaler 64 } void Initialize_INT0 () { EICRA = 0x02; //INT0 is triggered by falling edge EIMSK |= (1 << INT0); //activate INT0 } int main (void) { char cTransmit[7]; char cStart; DDRD = 0xFF; //all PINS are Output PORTD = 0x00; //all Outputs are LOW DDRD &= ~(1 << PIND2); //PIND2 is Input PORTD |= (1 << PIND2); //activate internal pull-up of PIND2 (INT0) DDRB = 0xFF; //all PINS are Outputs PORTB = 0x00; //all Outputs are LOW Initialize_USART(); //initializes USART while(cStart != 1) { cStart = Receive_Character(); } sei(); //activates global interrupts Initialize_TIMER0(); //initializes TIMER0 Initialize_INT0(); //initializes INT0 while(cStart == 1) { if(cSendFlag == 1) { Send_Time("Taster_Click: "); Send_Time(utoa(ms_Button_Click, cTransmit, 10)); cSendFlag = 0; } } return 0; //never reached } ISR (INT0_vect) { ms_Button_Click == ms; //Send actual time Send_Time("FirstInterrupt\r\n"); //send text to hterm (quick&dirty) nur ein test ob alles funkt cSendFlag = 1; cDebounceFlag = 1; EIMSK &= ~(1 << INT0); } ISR (TIMER0_COMPA_vect) { ms++; //zählt die Zeit //Hier ist noch einiges falsch, das weiß ich bereits, aber wenn ihr denkt, das gehört komplett in den //Müll dann mach ich gerne auch was ganz anderes //debounce button if (cDebounceFlag == 1 && !(PIND & (1 << PIND2)) && iDebounce < 20) { iDebounce ++; } if (iDebounce >= 20 && (PIND & (1 << PIND2))) { iDebounce ++; if (iDebounce >= 40) { EIMSK = 0x01; //activate INT0 cDebounceFlag = 0; iDebounce = 0; } } }
Steve-o Bane schrieb: > DDRD = 0xFF; //all PINS are Output > PORTD = 0x00; //all Outputs are LOW > > DDRD &= ~(1 << PIND2); //PIND2 is Input > PORTD |= (1 << PIND2); //activate internal pull-up of PIND2 (INT0) Inputs sollte man niemals (auch nicht kurzzeitig) als Ausgang definieren. Laß sie Input bleiben. Das kann schonmal ne Flanke erzeugen, die das Interruptflag setzt. Wenn man frühere Ereignisse nicht haben will, ist es gute Praxis, vor der Freigabe das Flag zu löschen. Und die Standardantwort: Externe Interrupts nimmt man nicht für Tasten. Peter
Steve-o Bane schrieb: > Könnt Ihr mir sagen, was ich da flasch gemacht habe? Da hast eine Annahme getroffen. Nämlich die, dass Interrupt auslösende Ereignisse erst dann registriert werden, wenn du den Interrupt freigibst. Dem ist aber nicht so. Bei allen Interrups spielen immer 2 Bits zusammen. Da gibt es ein Bit welches das Ereignis an sich registriert. Und es gibt ein Bit welches die Bearbeitung dieses Ereignisses freigibt. Konkret. Im Register EIFR gibt es das Bit INTF0. Wenn immer die eingestellte Interrupt-Bedingung zutrifft, dann wird dieses Bit gesetzt. Die eingestellte Bedingung ist beim Mega328, wenn er aus dem Reset kommt: Ein Low-Level am INT0-Pin triggert dieses Bit. Dann gibt es im Register EIMSK das Bit INT0, welches regelt, ob bei Vorliegen eines 1-Bits in EIFR/INTF0 die zugehörige ISR angesprungen wird. (Natürlich nur wenn zusätzlich auch sei() gemacht wurde). Ist dieses Bit gesetzt UND ist EIFR/INTF0 auch gesetzt worden, dann wird die ISR ausgeführt und EIFR/INTF0 wieder zurückgesetzt. Aber: Bei dir IST nach dem Reset EIFR/INTF0 gesetzt! Denn: Der Default für ISC01 und ISC00 im EICRA ist: Interrupt Ereignis wird ausgelöst, wenn der physikalische Pin auf 0 Pegel ist. Und den Fall hast du nach dem Reset: Der Pin ist auf Eingang und da der Taster nichts macht bzw. der Pullup noch nicht eingeschaltet ist, hast du 0-Pegel. Der ändert sich zwar mit dem Einschalten des Pullup, das macht aber nichts. EIFR/INTF0 ist zu diesem Zeitpunkt schon lange gesetzt. Und wenn du dann im EIMSK/INT0 freigibst, dann liegt der Fall vor: sei() ist freigegeben EIFR/INTF0 ist gesetzt EIMSK/INT0 ist freigegeben und damit wird die ISR aufgerufen. Denn genau das sind die Bedingungen, unter denen dieser Aufruf erfolgt. Ereignisse die Interrupts auslösen können, werden auch dann registriert, wenn der Interrupt an sich gar nicht freigegeben ist! Sobald dann die Freigabe erfolgt, wird konsequenterweise dann auch die ISR angesprungen.
Steve-o Bane schrieb: > Deshalb dachte ich mir ich lege den Taster an einen Interrupt, um die > Verzögerung mit Sicherheit zu minimieren. * Die Verzögerung die du durch deinen Taster hast, liegt größenordungsmässig über dem Bereich der Millisekunden. * Hast du eine Vorstellung davon, wie lang eine Millisekunde für einen µC ist? Das ist für den eine halbe Ewigkeit.
Karl Heinz Buchegger schrieb: > Aber: Bei dir IST nach dem Reset EIFR/INTF0 gesetzt! Denn: Der Default > für ISC01 und ISC00 im EICRA ist: Interrupt Ereignis wird ausgelöst, > wenn der physikalische Pin auf 0 Pegel ist. Und den Fall hast du nach > dem Reset: Der Pin ist auf Eingang und da der Taster nichts macht bzw. > der Pullup noch nicht eingeschaltet ist, hast du 0-Pegel. Der ändert > sich zwar mit dem Einschalten des Pullup, das macht aber nichts. > EIFR/INTF0 ist zu diesem Zeitpunkt schon lange gesetzt. Allerdings sagt das Datenblatt:
1 | •Bit 0 – INTF0: External Interrupt Flag 0 |
2 | ... |
3 | This flag is always cleared when INT0 is configured as a level interrupt. |
Da der Level-Interrupt ein Zustands-Interrupt ist, und kein Ereignis-Interrupt, wird auch kein Flag zum Festhalten eines Ereignisses benötigt. Also ich sehe bei dem gezeigten Code nicht, wo das gesetzte Flag herkommen soll.
Nachtrag: Was ich mir vorstellen könnte, ist, dass das Umschalten der Konfiguration (ISC0x) das Flag setzen kann, so wie es ja auch beim Input-Capture-Interrupt ist.
Hallo Leute, danke schon mal für eure Hilfe. Leider haben die Vorschläge bisher nichts gebracht... Ich habe an einen Interrupt gedacht, da ich den Tastendruck unmittelbar abfangen möchte. Da ich allerdings nur auf eine ms genau jeden Tastendruck benötige, könnte ich ihn ja auch im TimerInterrupt abfragen, das eh jede ms ausgeführt wird. Denkt ihr das funkt dann? So würd ich nämlich ohne den Interrupt auskommen?! Oder gibt es noch bessere Lösungen? Bei menschlichen Reaktionszeiten um die 200ms sind zB 10 ms Verzögerung bei der Zeiterfassung schon ziemlich viel... Vielen Dank! Steve
Nachtrag: Merce, hatte Eure antworten noch nicht gesehen, das scheint es gewesen zu sein! Vielen Dank, ich werd´s mir merken! Jetzt werde ich aber trotzdem mal an einer Version ohne Interrupt INT0 arbeiten! Vielen Dank nochma, Steve
Stefan Ernst schrieb: > Karl Heinz Buchegger schrieb: >> Aber: Bei dir IST nach dem Reset EIFR/INTF0 gesetzt! Denn: Der Default >> für ISC01 und ISC00 im EICRA ist: Interrupt Ereignis wird ausgelöst, >> wenn der physikalische Pin auf 0 Pegel ist. > Also ich sehe bei dem gezeigten Code nicht, wo das gesetzte Flag > herkommen soll Da hat er ausnahmsweise mal Recht. mfg.
>Bei menschlichen Reaktionszeiten um die 200ms sind zB 10 ms Verzögerung >bei der Zeiterfassung schon ziemlich viel... Und absoluter Schwachsinn weil nicht reproduzierbar. Wenn du Zeiten im 1ms Bereich erfassen willst dann nimmst du halt keinen Taster und keinen Menschen der da draufdrückt.
Danke Peter Dannegger, damit hast du auch mein Problem gelöst! Die Externen Interrupts sind einfach zu langsam für Drehgeber. Gruß Martin
Martin schrieb: > Die Externen Interrupts sind einfach zu langsam für Drehgeber. das haett i aber gerne genauer erklaert....
Als ich für meinen Drehgeber 24imp/U einen zähler fertig hatte lief er druch die ISR sehr schnell durch aber trotz allem war der zähler in der ISR nur ~2s schnell/langsam. Also sind fast alle pulse durchgerutscht ohne sie zu zählen.. Und via INT on change ging das dann wie erwartet! Warum ist das denn so?
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.