PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Merkwürdiges Problem mit PCINT und UART



Marten83
30.05.2009, 10:37
Hallo Zusammen,

wie oben schon steht habe ich ein ganz komisches Problem mit folgendem Code

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "uart.h"


#define UART_BAUD_RATE 9600

ISR(PCINT_vect)
{
uart_puts("A pin change interrupt occurred!");
}

int main(void)
{
DDRB = 0x00; //set pins as output
PORTB |= (1<<PB0) | (1<<PB1); //activate pullup
PCMSK |= (1<<PCINT0) | (1<<PCINT1); //mask port
GIMSK |= (1<<PCIE); //activate PCinterrupts

uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
sei();
while(1)
{
uart_puts("Noch alles in Ordnung?\r\n");
_delay_ms(1000);
}
return 0;
}
Es ist so, dass wenn ich die ISR auskommentiere das Programm problemlos läuft. Sobald es aber mit kompiliert wird ist nach dem uart_init(..); und sei(); schluss und die int main(void); wird von vorne durchlaufen.
D.h. der µC steckt dann in einer Endlosschleife fest.

Ich programmiere mit AVRStudio mit WinAVR plugin.

Ach ja, die UART-Routinen sind von Peter Fleury und der µC ist ein ATTiny2313.

Kann mir jemand einen Hinweis geben woran das liegen könnte?

Vielen Dank!

MfG, Marten83

sternst
30.05.2009, 11:42
Du hast dir da einen netten Deadlock reinprogrammiert. Die Fleury-Lib versendet die Zeichen per Interrupt, die ja aber innerhalb eines anderen Interrupts deaktiviert sind. Wenn du die Zeichenkette kürzer machst (weniger als 32 Zeichen) wird es funktionieren, aber nur solange die Interrupts nicht zu schnell aufeinander folgen.

Marten83
30.05.2009, 13:22
Ja, ok.
Darüber habe ich noch nicht nachgedacht.
Aber, müsste, solange kein pin change interrupt kommt, die Routine nicht "Noch alles in Ordnung?" senden?
Nach der Simuation in AVRStudio kommt der Compiler noch nichtmal an die Stelle an der mit "sei();" die Interrupts aktiviert werden.
Und das verstehe ich nicht.

Marten83

sternst
30.05.2009, 15:04
Du solltest das Pin-Change-Interrupt-Flag nach der Initialisierung löschen. Wahrscheinlich ist das Flag durch einen floatenden Eingang bereits gesetzt, und dann wird nach dem sei sofort der Interrupt ausgelöst.

Marten83
30.05.2009, 16:08
Habe das ganze jetzt mal um folgende Zeile erwitert:

EIFR &= ~(1<<PCIF); //clear interrupt flag

Leider hatte auch das nicht den gewünschten Erfolg!

McJenso
30.05.2009, 17:27
Hallo,

diese Flags werden eigentlich immer durch das Schreiben einer 1 gelöscht.

Gruß

Jens

Marten83
30.05.2009, 20:47
Oh, da hätte ich wohl bis zum Ende lesen sollen.

Da stellt sich aber gleich noch eine Frage:

Warum????

Ich meine im Datenblatt steht, dass wenn ein Interrupt kommt, das PCIF gesetzt also "eins" wird und dadurch dann die entsprechende ISR ausgeführt werden kann. Ich finde es unlogisch dann eine "eins" zum löschen des bits zu schreiben.

Naja, ich probiers erstmal!

Danke!

Marten83
30.05.2009, 20:55
So, habe es ausprobiert....
Leider geht es nur halb.
Solange ich keinen Interrupt von aussen auslöse funktioniert die Hauptroutine. Nach einem Interrupt bleibt der µC wieder hängen und führt noch nicht einmal den Code der ISR aus.
Das PCIF-flag wird doch bei Ausführung der ISR wieder gelöscht, oder?
Zumindest steht es so im Datenblatt.

sternst
30.05.2009, 21:38
Wie sieht der Code jetzt aus?

Marten83
31.05.2009, 09:08
So sieht er jetzt aus:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>
#include "uart.h"
#include "owi.h"


#define UART_BAUD_RATE 9600


ISR(PCINT_vect)
{
//EIFR |= (1<<PCIF); //<--hat auch nicht geklappt
uart_puts("A pin change interrupt occurred!");
}

int main(void)
{
DDRB = 0x00; //set pins as output
PORTB |= (1<<PB0) | (1<<PB1); //activate pullup
PCMSK |= (1<<PCINT0) | (1<<PCINT1); //mask port
GIMSK |= (1<<PCIE); //activate PCinterrupts
EIFR |= (1<<PCIF); //clear interrupt flag

uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
sei();
signed int temperatur;
char s[20];
char temp[5];
while(1)
{
OWReset();
OWWriteByte(0xcc); //skip rom
OWWriteByte(0x44); //convert T
_delay_ms(500);
OWReset();
OWWriteByte(0xcc);
OWWriteByte(0xbe);
temperatur = OWReadByte();
temperatur |= (OWReadByte() << 8);
temperatur = temperatur / 2;
strcpy(s,"$PMBPS,");
itoa(temperatur,temp,10);
strcat(s,temp);
uart_puts(s);
uart_puts("\r\n");
_delay_ms(500);

}
return 0;
}

sternst
31.05.2009, 09:13
Solange ich keinen Interrupt von aussen auslöse funktioniert die Hauptroutine. Nach einem Interrupt bleibt der µC wieder hängen und führt noch nicht einmal den Code der ISR aus.

ISR(PCINT_vect)
{
//EIFR |= (1<<PCIF); //<--hat auch nicht geklappt
uart_puts("A pin change interrupt occurred!");
}
Wozu habe ich eigentlich meinen ersten Post geschrieben?

Marten83
02.06.2009, 11:48
Hallo sternst,

das habe ich mir schon zu Herzen genommen.
Aber ich habe auch nochmal nachgeguckt, in der lib wird nur am UDRIE im UCSRB Register rumgefummelt.
Nach meinem Verständnis löst doch ein Pegelwechsel am PortB einen Interrupt aus der dann die ISR ausführt. Darin wird der Puffer der uart lib gefüllt und der "Puffer leer" Interrupt aktiviert. So, wenn der leer ist berechnet er den Puffer neu. Zwischendurch ist die ISR auch schon fertig geworden und wartet auf den nächsten Interrupt.
Sollte doch so klappen.
Dazu muss ich sagen, dass mir klar ist dass wenn ich ungünstig den Interrupt nochmal auslöse der Puffer durcheinander kommen kann. Ich löse ihn aber nur einmal aus. (Hall-Schalter mit integr. Schmitt-Trigger)
Also sollte es doch eigentlich fonktionieren, oder?

MfG, Marten83

sternst
02.06.2009, 18:03
Nein.
Die Funktion uart_puts schreibt die Daten in den Sendepuffer, aber natürlich nur solange dort auch noch entsprechend Platz ist. Ist kein Platz mehr, wartet die Funktion so lange, bis wieder Platz ist. Die Defaultgröße des Puffers ist 32. Du versuchst aber mehr als 32 Zeichen zu senden, also wartet die Funktion, bis wieder Platz ist, um auch den Rest noch unterbringen zu können. Aber wie soll wieder Platz frei werden, wenn das Senden aus dem Puffer per Interrupt funktioniert, der Interrupt aber nicht aufgerufen wird, weil du gerade in einem anderen Interrupt bist? Also wartet die Funktion bis in alle Ewigkeit.