PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Externe Interrupts an ATMega 32 auf RN-Control... *help*



corone
14.04.2007, 18:51
Hallo ale miteinander. Seit gestern nachmittag versuche ich verzweifelt auf meinem RN-Control ein kleines Programm zum Laufen zu bringen. Es soll in der Hauptschleife die LED am PortC.4 blinken lassen (tut es auch) und wenn ich den Interrupt 0 auslöse die LED am PortC.0 blinken lassen (tut es nicht).

Ich habe jetzt viele Stunden dieses und etliche andere Foren gewälzt, Tutorials, Datenblätter und Google gequält, aber ich finde die Lösung einfach nicht... Könnt ihr bitte mal kurz in meinen Code schauen und mir sagen, wo der Fehler liegt?



Quellcode:

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


int main(void)
{

/*###Initialisierungsphase###*/

//Pins bzw. Ports als Ein-/Ausgänge konfigurieren
DDRC |= 0x11; //10001000 -> PORTC.4 ist blinkende LED im Hauptprogramm, PORTC.0 ist Anzeige des Interrupts
DDRD |= 0x00; //00000000 -> PORTD.2 ist Interrupt0 (Der ausgelöst werden soll)

//Variablen
uint8_t counter;

//Interrupts initialisieren - ist der Kram hier richtig?
MCUCR = ~(1<<ISC01);
MCUCR = (1<<ISC00);
GICR = (1<<INT0);
sei();

//Interruptroutine, die ausgelöst werden soll - aber nicht wird
ISR(INT0_vect)
{
for(uint8_t i=0; i<5; i++)
{
PORTC |= (1<<0);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<0);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}
}
}

while(1)
{
PORTC |= (1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}
{

}
}


return 1;
}




Compilerausgabe:

> "make.exe" all

-------- begin --------
avr-gcc (GCC) 3.4.6
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Size before:
interrupts.elf :
section size addr
.text 228 0
.data 0 8388704
.bss 0 8388704
.noinit 0 8388704
.eeprom 0 8454144
.stab 1980 0
.stabstr 1866 0
Total 4074




Converting to AVR Extended COFF: interrupts.cof
avr-objcopy --debugging --change-section-address .data-0x800000 --change-section-address .bss-0x800000 --change-section-address .noinit-0x800000 --change-section-address .eeprom-0x810000 -O coff-ext-avr interrupts.elf interrupts.cof
Warning: file C:/DOCUME~1/EWEDDI~1/LOCALS~1/Temp/cc0Eeaaa.s not found in symbol table, ignoring
Warning: ignoring function __vectors() outside any compilation unit
Warning: ignoring function __bad_interrupt() outside any compilation unit

Size after:
interrupts.elf :
section size addr
.text 228 0
.data 0 8388704
.bss 0 8388704
.noinit 0 8388704
.eeprom 0 8454144
.stab 1980 0
.stabstr 1866 0
Total 4074



Errors: none
-------- end --------


> Process Exit Code: 0
> Time Taken: 00:01




Ich weiß, dass sich dieser Effekt über Timer oder sonstwas viel einfacher erreichen ließe, aber ich will verstehen, wie ich externe Interrupts auslöse. Bin für jede Hilfe sehr dankbar!


Gruß
Corone

Hubert.G
14.04.2007, 20:40
Im Simulator funktioniert es so:

#include <avr/interrupt.h>
#define F_CPU 10000000UL
#include <util/delay.h>
uint8_t i;
uint8_t counter;


//Interruptroutine, die ausgelöst werden soll - aber nicht wird
ISR(INT0_vect){

// for( i=0; i<5; i++)
{
PORTC |= (1<<0);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<0);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}
}
}
int main(void)
{

/*###Initialisierungsphase###*/

//Pins bzw. Ports als Ein-/Ausgänge konfigurieren
DDRC |= 0x11; //10001000 -> PORTC.4 ist blinkende LED im Hauptprogramm, PORTC.0 ist Anzeige des Interrupts
DDRD |= 0x00; //00000000 -> PORTD.2 ist Interrupt0 (Der ausgelöst werden soll)

//Variablen


//Interrupts initialisieren - ist der Kram hier richtig?
MCUCR = ~(1<<ISC01);
MCUCR = (1<<ISC00);
GICR = (1<<INT0);
sei();

for(;;){

PORTC |= (1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

}
}


Einige kleine Änderungen, Taktfrequenz noch richtig eintragen.

Hubert

corone
15.04.2007, 11:42
Mhhh.... vielen Dank für deine Hilfe. Supi, dass sich so schnell jemand gemeldet hat! Leider bekomm ich's trotzdem noch nicht hin...

Klar... ich Ochse hab irgendwie die Interruptroutine in der main-Schleife gehabt... warum auch immer *Kopp auf Tisch hau*. Aber das war nicht der alleinige Fehler... Leider funktionierte auch dein Programm nicht besser als meines - LED4 blinkt, die 0 rührt sich nicht :-(

Habe da gleich mal noch ein paar Fragen: Warum enthält dein Programm in der Main keine Return-Anweisung? Sicher, die wird nie erreicht... aber bei mir meckert der Compiler, wenn ich keine habe.

Warum hast du in meiner Interruptroutine die For-Schleife auskommentiert?

Ach ja... das Setzen dieser Register (MCUCR und GICR) habe ich mir nach Datenblättern und Tuts frei zusammengeschustert - stimmt das überhaupt, was ich da geschrieben habe?

Zum Auslösen des Interrupts verbinde ich immer +5V kurzzeitig mit dem Pin D.2... ist das überhaupt so möglich, oder kann ich damit gar keinen Interrupt auslösen?

So... zu guter letzt noch mal die aktuelle Version meines Quelltextes. Da nach Programmstart die LED0 immer mit rumfunzelt (Ausgang is eben high) will ich sie per Interrupt nur noch auf Low schalten -> Programm verkürzt sich weiter. Könnt ihr bitte noch mal schauen, was ich falsch mache?




#include <avr/delay.h>
#include <avr/interrupt.h>


//Interruptroutine, die ausgelöst werden soll - aber nicht wird
ISR(INT0_vect)
{
PORTC &= ~(1<<0);
}



int main(void)
{

/*###Initialisierungsphase###*/

//Variablen
uint8_t counter;

//Pins bzw. Ports als Ein-/Ausgänge konfigurieren
DDRC |= 0x11; //10001000 -> PORTC.4 ist blinkende LED im Hauptprogramm, PORTC.0 ist Anzeige des Interrupts
DDRD |= 0x00; //00000000 -> PORTD.2 ist Interrupt0 (Der ausgelöst werden soll)


//Interrupts initialisieren - ist der Kram hier richtig?
MCUCR = ~(1<<ISC01);
MCUCR = (1<<ISC00);
GICR = (1<<INT0);
sei();


while(1)
{
PORTC |= (1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}
}


return 1;
}



Vielen Dank für deine Hilfe und alle, die sich hoffentlich noch melden!!

Viele Grüße
Corone

izaseba
15.04.2007, 12:14
Hallo,


MCUCR = ~(1<<ISC01);
MCUCR = (1<<ISC00);

Erste Zeile hat hier wohl nicht viel zu sagen, weil Du in der zweiten Zeile den ganzen Register sowieso überschreibst...
Lass die erste Zeile für das Beispielprogramm ganz weg.
Du stellst Interrupt bei logischem Weschsel an Int 0 ein, ist es denn Hardwaremäßig so gegeben ?
Welchen Level hat der INT0 ?
Ist da ein Pulldown dran ?
selbst wenn ja, machst Du hier 2 Interrupts schnell hintereinander, also:
Du gehst mit Deinen +5V Draht an den Pin dran -> interrupt, machst den Draht weg -> interrupt
Mach es z.B so:


//Pins bzw. Ports als Ein-/Ausgänge konfigurieren
DDRC |= 0x11; //10001000 -> PORTC.4 ist blinkende LED im Hauptprogramm, PORTC.0 ist Anzeige des Interrupts
DDRD |= 0x00; //00000000 -> PORTD.2 ist Interrupt0 (Der ausgelöst werden soll)
PORTD = (1<<PD2); /*internen Pullup einschalten*/
MCUCR = (1<<ISC01); /*Interrupt bei fallender Flanke*/
GICR = (1<<INT0);
sei();

Jetzt kannst Du Deinen Draht zwischen GND und PD2 halten,
so müßte es funktionieren

Gruß Sebastian

Hubert.G
15.04.2007, 15:06
Hallo
Das mit dem Interrupt auslösen hat dir "izaseba" schon geschrieben. Die for-schleife habe ich nur zum testen auskommentiert. In der Simulation blinkt bei mir auch die Led 0. Ich weis nicht ob du das AVR-Studio benutzt, aber da kann man zumindest so einfache Sachen sehr leicht simulieren.
Hubert

corone
15.04.2007, 15:47
Ah, vielen Dank für eure Hilfe. Hab's gerad eben auch hinbekommen... nur eine Frage zu dem MCUCR hab ich noch: Warum überschreibt der Zweite das Erste? Laut Datenblatt sind ISC01 und ISC00 unabhängig voneinander - je nachdem, welche der insgesamt 4 Kombinationsmöglichkeiten ich wähle, habe ich die 4 Modi: (vorne: ISC01 / hinten: ISC00) "00 - Interrupt bei Low-Level auslösen" "01 - bei irgend einer Pegeländerung" "10 - bei fallender Flanke" "11 - bei steigender Flanke"


Ist dem nicht so? Wenn nein - wie kann ich dann die 4 verschiedenen Modi aussuchen?




Hier mal noch mein kompletter Code... hilft vielleicht mal irgend einem Anfänger weiter, der vor dem selben Rätsel wie ich steht:





#include <avr/delay.h>
#include <avr/interrupt.h>

//Variablen
uint8_t counter;
uint8_t i;

//Interruptroutine, die ausgelöst werden soll
ISR(INT0_vect)
{
for(i=0; i<2; i++)
{
PORTC |= (1<<0);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<0);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}
}
}



int main(void)
{

/*###Initialisierungsphase###*/

//Pins bzw. Ports als Ein-/Ausgänge konfigurieren
DDRC |= 0x11; //10001000 -> PORTC.4 ist blinkende LED im Hauptprogramm, PORTC.0 ist Anzeige des Interrupts
DDRD |= 0x03; //00000011 -> PORTD.2 ist Interrupt0 (Der ausgelöst werden soll)


//Interrupts initialisieren
MCUCR &= ~(1<<ISC01); //stellen den Modus ein, wie der Interrupt auslöst - je nach Art des Sensorsignals muss ein anderer Modus gewählt werden
MCUCR |= (1<<ISC00); //00 bei Low-Level //01 bei Änderung des Pegels (egal, welche) //10 bei fallender Flanke (high zu low) //11 bei steigender Flanke (low zu high)
GICR |= (1<<INT0); //aktiviert den Interrupt an sich
sei(); //aktiviert das globale Interrupt Enable-Flag (I im SREG-Register)


while(1)
{
PORTC |= (1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}

PORTC &= ~(1<<4);

//wait 16*65536
counter = 0;
while(counter < 16)
{
counter++;
_delay_loop_2(65535);
}
}


return 1;
}




Vielen Dank für eure Hilfe!! Schaut mal bitte noch wegen dem MCUCR-Register, das verwirrt mich noch. Ansonsten - alles gelernt, was auf dem Plan stand. Danke!!!


Grüßle
Corone

izaseba
15.04.2007, 17:02
Warum überschreibt der Zweite das Erste?


:-k
MCUCR = ~(1<<ISC01);
löscht ISC01 bit und läßt den rest unverändert...

MCUCR = (1<<ISC00);

interessieren die restlichen Bits nicht, es wird nur ISC00 gesetzt, der Rest wird mit 0 beschrieben.
Solltest Du z.B im MCUCr irgendwas gesetzt haben, wird nach der zweiten Operation alles gelöscht sein, alles klar?
Deswegen hab ich geschrieben , daß die erste Zeile in diesem Zusammenhang unnötig ist.

Gruß Sebastian

corone
15.04.2007, 18:54
ahhh... gut, danke. aber wenn ich beide auf 1 setzen will, muss ich

MCUCR = (1<<ISC01);
MCUCR = (1<<ISC00);


nehmen, oder?

izaseba
15.04.2007, 19:14
nein, Entweder


MCUCR = (1<<ISC01);
MCUCR |= (1<<ISC00);

oder


MCUCR = (1<<ISC01)|(1<<ISC00);

was bei eingeschalteter Optimierung den gleichen Code geben dürfte.


MCUCR |= (1<<ISC01)|(1<<ISC00);

setzt die Bits, ohne den alten Zustand von MCUCR zu verändern


MCUCR &= ~(1<<ISC01)|(1<<ISC00);

Schaltet sie ab, der Rest bleibt unberührt, das sind aber C Grundlagen :wink:
Ich hoffe Licht ins dunkle gebracht zu haben :-)

Gruß Sebastian