PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Reset nach Input-Abfrage bei PCF8574AP



Jacob2
28.06.2011, 18:18
Hallo!
Ich habe vier Taster an einem PCF8574AP angeschlossen (Taster nach GND), welcher wiederum an einem ATmega48 hängt (I2C). Nun nutze ich diese Funktionen (http://www.mikrocontroller.net/articles/Port-Expander_PCF8574#Software) (s. unten) von Mikrocontroller.net. Um die Vorgänge zu überwachen lasse ich Kurznachrichten auf einem LCD anzeigen.
Der Interruptausgang des Portexpanders löst einen Pin-Change-Interrupt aus, was auch wunderbar funktioniert:


ISR(PCINT2_vect)//PCINT20 -> Ein Taster wurde gedrückt
{
cli();
//Welche Taste?
lcd_clrscr();
lcd_puts("Vorher...");
_delay_ms(500);
uint8_t Taste = pcf8574_get_inputs(0);//Adresse = 0 (alle auf GND)
lcd_clrscr();
lcd_puts("Nachher!");
sei();
};
Das Problem ist, dass sich der Controller bei der Get-Inputs-Funktion resettet, d.h. auf dem LCD bleibt "Vorher..." stehen. Den Neustart kann ich auch mithilfe des LCD erkennen (Mitteilung am Anfang der main()-Funktion).
Mit dem Oszilloskop habe ich schon so gut es geht überprüft, dass weder die Betriebsspannung einbricht noch der Reset-Pin auf low gezogen wird...

Hier die Funktionen von Mikrocontroller.net, die ich verwende:

#include "pcf8574.h"


void pcf8574_init (void)
{
/*set bus speed*/
TWBR = 0x10;
}


unsigned char pcf8574_send_start (void)
{
/*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
/*wait, until start condition has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}


void pcf8574_send_stop (void)
{
/*writing a one to TWINT clears it, TWSTO=Stop, TWEN=TWI-enable*/
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
}


unsigned char pcf8574_send_add_rw (unsigned char address, unsigned char rw)
{
/*address can be 0 .. 8; rw=0 --> write, rw=1 --> read*/
unsigned char addr_byte = 0;
/*shift address one bit left*/
addr_byte = address << 1;
/*set RW-Bit, if necessary*/
addr_byte |= rw;
/*0b0111xxx0 --> address of Expander*/ //für PCF8574AP angepasst (0b0100xxx0 --> 0b0111xxx0)
addr_byte |= 0b01110000;
/*TWDR contains byte to send*/
TWDR = addr_byte;
/*send content of TWDR*/
TWCR = (1<<TWINT) | (1<<TWEN);
/*wait, until address has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}


unsigned char pcf8574_send_byte (unsigned char byte)
{
/*TWDR contains byte to send*/
TWDR = byte;
/*send content of TWDR*/
TWCR = (1<<TWINT) | (1<<TWEN);
/*wait, until byte has been sent --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWSR;
}


unsigned char pcf8574_read_byte (void)
{
/*send content of TWDR; TWEA = enable ACK*/
TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);
/*wait, until byte has been received --> ACK*/
while (!(TWCR & (1<<TWINT)));
return TWDR;
}


unsigned char pcf8574_get_inputs (unsigned char address)
{
pcf8574_init ();
pcf8574_send_start ();
pcf8574_send_add_rw (address, 1);
unsigned char input = pcf8574_read_byte ();
pcf8574_send_stop ();
return input;
}


void pcf8574_set_outputs (unsigned char address, unsigned char byte)
{
pcf8574_init ();
pcf8574_send_start ();
pcf8574_send_add_rw (address, 0);
pcf8574_send_byte (byte);
pcf8574_send_stop ();
}



Was könnte den Reset verursachen?
Vielen Dank im Voraus!

PS: Hier ist noch der Schaltplan:
19205

Jacob2
29.06.2011, 10:32
Okay, ich habe das Problem jetzt soweit eingrenzen können, dass der µC anscheinend nach der Interrupt-Funktion ISR(PCINT2_vect) ein Reset macht... Die I2C-Kommunikation scheint zu funktionieren, ich kann mir sogar den empfangenen Port-Status vom PCF8574 auf dem LCD anzeigen lassen, nur, danach ist immer ein Reset :-(!

Aber warum macht "er" dann ein Reset? In main() befindet sich der Controller in einer while(1); Schleife, in der er ja auch nach dem Interrupt noch sein müsste, richtig?

Ich habe auch schon versuchsweise cli() und sei() auskommentiert, an denen liegt's wohl aber auch nicht...

Richard
29.06.2011, 11:20
Ist es möglich das es zu einem Stack Überlauf kommt und Dein Programm im "Nirwana" endet?

Gruß Richard

oberallgeier
29.06.2011, 12:04
... danach ist immer ein Reset ! Aber warum macht "er" dann ein Reset? ...Bei Problemen mit undefiniertem Reset hatte ich mir mal eine extrem kurze LED-Signalroutine geschrieben, die von einer ISR angestossen wurde - z.B. drei mal kurz Blinken. Die Signalroutine wird von der ISR angestossen - sie wird nicht in der ISR ausgeführt. Dann hatte ich diese ISR Vektor für Vektor den verschiedenen Interruptvektoren zugewiesen. Irgendwann bekam ich den Auslöser identifiziert . . . .

Jacob2
29.06.2011, 12:26
@Richard Ich weiß nicht, wodurch könnte es denn zu einem Stack Überlauf kommen?
Sind z.B. cli() und sei() überhaupt sinnvoll im ISR? Kann das Tastenprellen vielleicht einen Überlauf verursachen?
Der µC hängt sich nicht auf, er startet definitiv neu, denn am Anfang von main() lasse ich eine Meldung ausgeben: lcd_puts("Starten..."); und die wird nach dem Interrupt angezeigt.
@oberallgeier Ich verstehe nicht ganz, was Du meinst - ich habe ja nur eine ISR... D.h. ich weiß, dass diese ISR bzw., das was danach geschieht, den Reset auslöst. Das Problem ist, dass ich nicht genau weiß, was nach der ISR passiert - bisher war ich davon ausgegangen, dass in die while(1)-Schleife zurückgesprungen wird...

oberallgeier
29.06.2011, 12:58
... verstehe nicht ganz, was Du meinst - ich habe ja nur eine ISR ...Ok, bevor wir die Geheimnisse der Interrupt-Vektortabelle diskutieren: Wie wärs, wenn Du SICHERSTELLST, dass nach dem lcd_puts("Vorher...") ein Reset im m48 erfolgt? Versuch doch mal zwischen lcd_clrscr(); und dem lcd_puts("Vorher...") ein Delay-1000 einzufügen - dann wäre nach einem Reset immer wieder für eine Sekunde ein gelöschtes LCD zu sehen. DANN sind wir sicher, dass ein Reset stattfindet und nicht "nur" ein Hängenbleiben des Controllers . . . . Dann könnte man weitersehen.

Richard
29.06.2011, 13:20
@Richard Ich weiß nicht, wodurch könnte es denn zu einem Stack Überlauf kommen?



Mit C habe ich leider Probleme und kann Deinen Code nicht wirklich nachvollziehen. :-( Aber solche "Neustarts" sind typisch wenn z.B. eine Rücksprung Adresse fehlt oder überschrieben wurde. Zum Testen mal den Sack Bereich vergrößern könnte helfen. Auch wenn das Programm selber "ins leere" Endet oder eine Variable zu "klein" ist( buffer overlow), (kann) das zum Neustart führen.

Gruß Richard

Searcher
29.06.2011, 15:53
Hallo,
ich verstehe C auch kaum, aber ein anderer Gedanke:

Der PCF8574 zieht INT nach LOW, wenn ein Taster gedrückt wird.
Das verursacht ein PCINT am ATMega und der springt in die ISR und der PCF wird ausgelesen.
Nach Datenblatt des PCF8574 wird dessen INT Leitung resettet, wenn Daten von seinem Datenport gelesen werden.
Das bedeutet, das am PCINT Pin der Pegel wechselt und ein neuer PCINT ansteht.
Sobald aus der ISR zurückgekehrt wird muß µC da sofort wieder rein.

Das Ganze ist davon abhängig, ob der µC das PCI Flag zu Beginn der ISR oder bei Rücksprung löscht. Das weis ich leider nicht.

SEI und CLI in der ISR macht für mich keinen Sinn, da die sowieso während der ISR geblockt sind. Statt dessen eher Verhindern von PCINTs (im Maskenregister) und vor Rücksprung sicherheitshalber Löschen des PCI Flags und Wiederzulassen von PCINTs.

Entprellung von Tasten?

Gruß
Searcher