I2C-Bus Ack + Interrupt unterbrechung Im Unterforum Microcontroller - Beschreibung: Hardware - Software - Ideen - Projekte
Autor |
I2C-Bus Ack + Interrupt unterbrechung Suche nach: i2c (581) |
|
|
|
|
BID = 736808
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
|
Juhu! Mal wieder ein Assembler Problem!
Keine Angst, es handelt sich nicht um eine Fehlersuche, ich habe diesen schon erfolgreich lokalisiert, nur hab ich eine kreative Blockade bei der Lösungsfindung.
Das System an dem ich arbeite (PIC16F887 + I2C Bus (u.a. LM75) + Frequenzmessung per Interrupt) Macht folgende Zicken:
Wenn der externe Interrupt der Frequenzmessung genau dann im Programm zuschlägt wenn der PIC als Master des I2C Busses auf den Ack des Slaves wartet, so verpasst der Master diesen und der Bus bleibt kleben (State SDA High, SCL Low); Der Fehler ist nur durch einen Kaltstart zu beheben, MCLR am PIC erweckt den Bus nicht wieder zum Leben, der Slave blockt...
Ich vermute diesen Fehler aus folgenden Gründen:
-Wenn ich zwischen Slave-Adress-Write und Read vom Bus mehrere NOP Befehle einfüge, so tritt der Fehler sofort beim ersten Leseversuch auf.
-Wenn ich den Externen Frequenzzähler-Interrupt deaktiviere tritt der Fehler garnicht mehr auf.
1. Frage: Ist meine Fehlereinschätzung realistisch?
2. Frage: Lösungsvorschläge? - Eine Deaktivierung des externen Interrupts während des Lesevorganges ist ungünstig, da es die Genauigkeit meiner Frequenzmessung beeinflusst. Bei jeder Verpassten Flanke um etwa 0,4%. Dies möchte ich vermeiden und suche deshalb eine andere Lösung. Fällt euch etwas ein? |
|
BID = 736812
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
|
Nachtrag:
Ich hasse intermittierende Fehler... Der sofort auftretende Fehler beim einfügen der NOP-Befehle war zufall. Ganau 2 Schuss 2 Treffer bei etwa 5% Wahrscheinlichkeit einer Fehlfunktion.
Eine Deaktivierung des Interrupts während des Wartens auf ACK bringt keine Abhilfe - Ein generelles Deaktivieren des Interrupts jedoch schon. Ich habe alle möglichen Interrupts aktiviert - von I2C über RS232 über AD-Wandlung +Timer0 und Extern - Kann es sein, dass das einer dieser gefürchteten Stack overflows ist, von denen immer alle reden? Ich glaube aber kaum dass die Verzweigung im Worst-Case 8 Unteraufrufe tief ist.
Hmmm...
Mein Lesebefehl sieht folgendermassen aus:
Code : |
;i2c bus übernehmen
call i2c_on
;Moduladresse temperatur mit read (1) auf den bus schieben und auf ACK warten
MOVLW 0x91 ; Die adresse ist 0X91
call i2c_send
;jetzt wird gelesen
call i2c_read
;empfangenes zeichen aus dem Register holen und auf rs232 senden
MOVFW Zeicheni2c
call RS232send
call i2c_off |
|
Die Entsprechenden Stellen der Unterprogramme lauten:
Code : |
;I²C BUS ÜBERNEHMEN
i2c_on bcf PIR1, SSPIF ; SSPIF Bit löschen
bsf STATUS, RP0
bsf SSPCON2, SEN ; Bus Übernahme anweisen
bcf STATUS, RP0
i2c_t1
btfss PIR1, SSPIF ; Testen, ob Bus schon übernommen wurde
goto i2c_t1 ; nein, noch nicht
bcf PIR1, SSPIF ; ja, der Bus ist mein! nun noch SSPIF zurücksetzen
return ;weiter im Programm
;__________________________________________________________________________________________
;I²C BUS Freigeben
i2c_off
bsf STATUS, RP0
bsf SSPCON2, PEN ; Bus Freigabe anweisen
bcf STATUS, RP0
i2c_t8
btfss PIR1, SSPIF ; Bus schon freigegeben?
goto i2c_t8 ; nein, noch nicht
bcf PIR1, SSPIF ; ja, alles fertig, nun noch SSPIF zurücksetzen
return ;weiter im Programm
;__________________________________________________________________________________________
;I²C ACK senden
i2c_ack
bsf STATUS, RP0 ;Bank 1
bsf SSPCON2, ACKEN ; ACK senden
bcf STATUS, RP0 ;Bank 0
return ;weiter im Programm
;__________________________________________________________________________________________
;Das Byte in W senden
i2c_send
movwf SSPBUF ; -> zum I2C-Slave übertragen
i2c_t2
btfss PIR1, SSPIF ; ACK schon empfangen?
goto i2c_t2 ; nein, noch nicht
bcf PIR1, SSPIF ; ja, Daten sind im Slave, nun noch SSPIF zurücksetzen
return ;weiter im programm
;__________________________________________________________________________________________
;ein Byte vom Slave empfangen
i2c_read
bsf STATUS, RP0
bsf SSPCON2, RCEN ; Daten Empfang einschalten
bcf STATUS, RP0
i2c_r7
btfss PIR1, SSPIF ; Daten Empfang fertig?
goto i2c_r7
bcf PIR1, SSPIF ; ja, nun noch SSPIF zurücksetzen
movf SSPBUF, w ; empfangene Daten -> W
movwf Zeicheni2c ; empfangene Daten -> Zeicheni2c
return ;weiter im programm |
|
Hrmpf, ich will jetzt nachts nicht in meinen Keller gehen um den Logic-Analyzer anzuschliessen... ARG
[ Diese Nachricht wurde geändert von: QuirinO am 29 Dez 2010 0:06 ] |
|
BID = 736816
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Monolog-Thread, zu schnell geschossen...
Wenn ich den Analog-Digitalwandler-Interrupt deaktiviere, so läuft der I2C bus und die Frequenzmessung stabil, aber mir fallen sporadisch an PortD angeschlossenen-Kontroll-LED aus, diese werden zur Kontrolle der internen Frequenzen durch die Interrupts des Timers und des Externen Interrupts aktivier und wieder deaktiviert.
Ich tippe auf ein Problem irgendwo in der Interrupt Routine. Wenn das alles nicht immer so unübersichtlich wäre...
Ich muss da morgen mal mit dem Oszi ran. Eventuell koppelt mir ja die gemessene Frequenz in den I2c-Bus Ein. Da liegen 2 Drähte etwa 3 cm parallel.
[ Diese Nachricht wurde geändert von: QuirinO am 29 Dez 2010 0:30 ]
|
BID = 738620
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Monoglog Update:
Habe gerade alle Hardware Probleme Ausgeschlossen.
Bild 1 Monitoring der Versorgungsspannung im Fehlerfall, die 2. Kurve ist der I2C Datenbus
Bild2: Alle übertragungen mit einem Logic analyzer getestet, es wir alles Komplett abgeschlossen, inclusive der Datenübertragung zurück zum Computer per RS232. Der Absturz des Controllers findet NICHT in der I2C Routine statt, sondern NACH der erfolgreichen übersendung der Daten zum PC. Kann es doch etwas mit der Stack tiefe zu tun haben? Wie kann ich das herausfinden?
[ Diese Nachricht wurde geändert von: QuirinO am 6 Jan 2011 18:15 ]
|
BID = 738670
Racingsascha Schreibmaschine
Beiträge: 2247 Wohnort: Gundelsheim
|
In MPLAB kann man sich den aktuellen Stack bei View -> Hardwarestack ankucken. Soweit ich weiß gibt MPLAB SIM auch einen Fehler während der Simulation aus, wenn der Stack überläuft. Problem dabei: deine Hardware wird sich schwer simulieren lassen. Kannst du die Zeit zwischen letzter von außen sichtbarer bzw sichtbar machbaren Funktion wie RS232-Übertragung und Absturz messen? dann kann man vielleicht Rückschlüsse daraus ziehen, ob in diese Zeit ein return fällt oder ob durch falsch gesetzte PCLATH,3/4 der PIC in den Hyperraum springt.
_________________
Fnord ist die Quelle aller Nullbits in deinem Computer.
Fnord ist die Angst, die Erleichterung, und ist die Angst.
Fnord schläft nie.
[ Diese Nachricht wurde geändert von: Racingsascha am 6 Jan 2011 20:05 ]
[ Diese Nachricht wurde geändert von: Racingsascha am 6 Jan 2011 20:14 ]
|
BID = 738688
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Zitat :
| der PIC in den Hyperraum springt. |
Hab laut gelacht
Ja ich kann versuchen die Zeit zwischen der letzten Flanke der RS232 übertragung und der letzten Flanke der Konroll-Led zu messen, die durch den Frequenzzähler Interrupt ausgelöst wird. Wenn der Frequenzzähler Interrupt nicht ausgelöst wird, dann wird das kaum möglich sein. Interessanterweise sprint das System auch nach einem MCLR-Reset nicht wieder an. Das hatte mich zu der Vermutung geführt dass der I2C bus Klemmt, was aber offensichtlich nicht der Fall ist.
|
BID = 738705
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Da mein Pic ohne Interupts eingetlich nur in einer Warteschleife im Kreis läuft, denke ich, dass in irgend einer Konfiguration der Interrupt Flag nicht resettet wird. Ich muss nochmal genau nachlesen wo man das am besten macht.
Meine Iterrupt-Behandlungsroutine sieht im Moment so aus: (vielleicht hat ja irgendjeman lust da mal drüberzulesen
Code : |
int
;RS232-Empfänger-Interupt?
btfss PIR1,RCIF
goto intad ; Interrupt kam von wo anders
movfw RCREG ; RS232-Register auslesen
movwf Zeichen ; und in den Speicher nach 'Zeichen' schreiben
bsf DatenSindDa,0 ; Kennzeichen für gültige Daten setzen
bcf PIR1,RCIF ; interrupt-Flag löschen
goto intEnde ;der interrupt kam von hier, also brauchen wir uns um den rest nicht zu kümmern
intad
;Analog / Digitalwandler interrupt?
; btfss PIR1,ADIF
; goto intb0 ; Interrupt kam von wo anders
; MOVLW B'10000000' ;wenn der AD- Interrupt ausgelöst wird, dann die LED togglen
; XORWF PORTD,1
; MOVFW ADRESH
; CALL RS232send
; bcf PIR1,ADIF ; interrupt-Flag löschen
; goto intEnde
intb0
;PortB0 Pin Change Interrupt?
btfss INTCON,RBIF
goto intEnde ; Interrupt kam von wo anders
MOVLW B'00000001' ;wenn der RB0- Interrupt ausgelöst wird, dann die LED togglen
XORWF PORTD,1
INCF FreqCounterFeuchtZwischen1
BTFSC STATUS,Z
INCF FreqCounterFeuchtZwischen2
In den nächsten beiden Zeile Vermute ich den Fehler.
BCF INTCON,INTF ; den Interrupt Flag de Pins Löschen
BCF INTCON,RBIF ; den Interrupt Flag de Pins Löschen
intEnde ; geretteten Status wieder zcurückschreiben
swapf status_temp,w
movwf STATUS
swapf w_temp,f
swapf w_temp,w
retfie ;und imterrupt ende
|
|
|
BID = 738720
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Ich hab jetzt simuliert und gegrübelt und bin zu folgendem Schluss gekommen: Wenn bei mir ein Interrupt behandelt wird, so springt das Programm nach Abarbeiten desselben sofort zu "intende" (siehe codebeispiel)
Alle anderen Interrupts werden nicht abgearbeitet und die entsprechenden Flags bleiben gesetzt. Kann es sein, dass 2 Interruptflags gleichzeitig gesetzt werden? Blockiert dann das fehlende Löschen von z.B. RBIF die weitere Ordnungsgemässe Abarbeitung der Interrupts? Was passiert, wenn mehrere Interrupt Flags gleichzeitig gesetzt werden? Wird der Interruptvektor dann sofort wieder aufgerufen?
Ich habe diesen Sprung zu intende deshalb gemacht, um die Abarbeitung der Int-Routine möglichst kurz zu halten. Sollte ich alle Flags nacheinander prüfen und abarbeiten und nur den jeweils nicht gesetzten Flag einfach überspringen?
Bei so häufig gesetzten Interrupts wie dem Frequenzzähler könnte ich mir schon vorstellen, dass da 2 Interrupts gleichzeitig ausgelöst werden könnten...
|
BID = 738727
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Ich arbeite jetzt bei jedem gesetzten Interrupt alle Interruptflags hintereinander ab, der Fehler bleibt der gleiche. Ich suche weiter...
Das ist so frustrierend zu wissen, dass es im Enteffekt sicher nur 1 Zeile ist die falsch ist.
|
BID = 738728
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hallo,
Ich habe von PIC exakt keine Ahnung.
Ich habe mir aber mal deinen Code angesehen und wäre es nicht möglich, dass noch andere Interruptquellen aktiv sind, die du gar nicht auf „pending“ testest? Und die nach Verlassen des ISR-Handlers sofort einen neuen IRQ triggern?
Die These ist im Eimer, wenn nur explizit aktivierte IRQs auftreten. Denn dann ist bekannt, welche Interrupts auftreten können.
Weiterhin ist es natürlich möglich, dass dein PIC einfach zu lahm ist und so viele IRQs getriggert werden, dass du mit dem Abarbeiten schlicht nicht mitkommst.
Dazu würde ich zu Beginn der ISR eine LED anschalten und nach Abarbeitung abschalten (danach wird in der Background-Schleife weitergearbeitet). Somit kann man ja herausfinden, ob und wie lange der PIC in der ISR beschäftigt ist.
Statt LED wird wohl ein einfacher IO-Ausgang reichen, den du ans Oszi klemmst.
An einen Stackoverflow kann ich nicht recht glauben. Es wäre fatal, wenn ein auftretender IRQ während der Abarbeitung der ISR dafür sorgen würde, dass diese erneut gestartet wird. Denn dann läuft dir der Stack wirklich irgendwann über, wenn die IRQs schneller kommen als du sie abarbeiten kannst.
Offtopic :
| Bei AVRs ist das standardmäßig so geregelt, dass ein IRQ, der während der Abarbeitung einer ISR auftritt, blockiert wird, bis die aktuelle ISR beendet ist. Danach wird sofort das wartende Ereignis bearbeitet (jede IRQ-fähige Hardware hat ein INT-Flag und zusätzlich gibt es ein globales INT-Flag.) |
Normalerweise ist so ein Verhalten entweder explizit abgeschaltet (IRQs werden blockiert, solange die ISR läuft) und lässt sich für besondere Zwecke aktivieren (AVR können das), oder aber es ist grundsätzlich erlaubt und der Programmierer muss zu Beginn der ISR irgendein Flag löschen, damit das nicht passiert.
Da kenne ich mich aber was PIC betrifft wie gesagt nicht aus, da musst du das Datenblatt wälzen.
Außerdem besteht die Möglichkeit, dass du z.B. das Statusregister nicht richtig gesichert hast oder andere Zustände der CPU vor Beginn der ISR gesichert werden müssen. Normalerweise ließt man zu Beginn das SREG ein und schmeißt den Zustand auf den Stack. Das sehe ich bei dir nicht.
Wie gesagt, PICs sind für mich eine andere, auch merkwürdige Welt.
AVRs haben halt einen schönen Interruptvektor; man muss sich nicht erst durch alle Flags durcharbeiten .
Edit:
Zitat :
|
[...] dass 2 Interruptflags gleichzeitig gesetzt werden?
|
Mit Sicherheit kann man davon ausgehen.
Teste doch mal probeweise: statt nach einem gesetzen Flag zum Ende zu springen einfach mal den Rest prüfen (sozusagen eine Kette bilden).
Hast du denn zu Beginn mal eine Analyse angestellt, ob das überhaupt funktionieren kann? Z.B. Frequenz der regelmäßigen IRQs (ADC, Freq-Zähler) im Vergleich zur CPU-Taktung?
Wenn das schon knapp ist, kann man die IRQs komplett abschalten und alles im Hintergrund-„Prozess“ erledigen, also in der main-Schleife. Dort wird ja eh nichts gemacht, sodass man quasi in einer Kette alle IRQ-Flags abfragen kann. Dadurch erspart man sich richtig Zeit! Denn der ISR-Vektor wird nicht gestartet, keine Register-Sicherungen etc. Und einen Überlauf kann es nicht geben.
_________________
[ Diese Nachricht wurde geändert von: DonComi am 6 Jan 2011 22:34 ]
|
BID = 738729
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Danke fürs Lesen, die Tips und die Anregungen. Ich wälze wie ein Irrer
|
BID = 738731
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Beachte auch mein Edit oben!
_________________
|
BID = 738737
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
In der Main Schleife wird tatsächlich etwas gemacht, aber eben nur dann, wenn RS232 Daten eintreffen. Dann wird je nach "Befehl" eine andere Aufgabe ausgeführt - z.B. bestimmte Register zurückübertragen oder ein Sensor oder eine AD-Wandlung gestartet oder ein Register mit den erhaltenen Daten beschrieben - ein ferngesteuerter Roboter eben...
Ich habe jetzt alles der Reihe nach abgearbeitet und der Fehler tritt nur bei bestimmten "unterfunktionen" der Main schleife auf - es könnte etwas mit der Abarbeitungsgeschwindigkeit derselben zu tun haben. Ich habe noch nicht theoretisch geprüft ob die Sache überhaupt möglich ist bei dieser Taktung, ich hatte auch nie Probleme, diese sind erst aufgetreten als ich 1) das System von einem 20MHz auf ein 4MHz-System portiert habe und 2) die Frequenzmessung hinzugefügt habe. Ich werde mal grob abschätzen ob ich die Grenze des Machbaren unterschritten habe.
Bei diesem Prozessortyp dauert 1 Befehl 4 Taktzyklen (wenn mich nicht alles täuscht)
-> ich habe etwa 1 000 000 Befehle pro Sekunde zur Verfügung -> der Frequenzzähler benötigt 7000 int/sek d.H. ich habe zwischen diesen Ausführungen 142 Befehle zur Verfügung um alles abzuarbeiten. Meine gesamte Interruproutine verbraucht 45 Befehle um KOMPLETT alles auszuführen wenn ALLE interrupt-flags gesetzt sind. Bleiben noch 90 (im schlimmsten Fall) zwischendrin um alle möglichkeiten durchzuspielen welcher RS232 Befehl empfangen wurde. Dafür benötige ich im Moment 57 Befehle .> bleiben noch maximal 40 Befehle für die Abarbeitung eines einzelnen tatsächlich auszuführenden Befehls. Einer der längsten dieser Befehle ist tatsächlich der, bei dem im Moment der Fehler auftritt, er hat etwa 35 Befehle. Bleiben für Rücksprünge und Funktionsaufrufe noch etwa 5. Das ist etwas wenig. Wie krass die Abarbeitungszeit doch zusammenschmilzt wenn man das mal durchrechnet.
Was passiert denn, wenn die Zeit dann ausgeht? Im schlimmsten Fall wird das RS232 Empfangsregister nicht geleert und ein 2. RS232 Befehl kommt an. Der Pic hat aber soweit ich weiss einen Empfangsbuffer eingebaut.
Datenblatt gewälzt: Das Register kann 2 Eintreffende Befehle halten. Wenn ein dritter eintrifft, so wird ein Fehlerbit gesetzt, welches von Hand gelöscht werden muss bevor ein weiterer Befehl eintreffen kann.
Zitat :
| 12.1.2.5 Receive Overrun Error
The receive FIFO buffer can hold two characters. An
overrun error will be generated If a third character, in its
entirety, is received before the FIFO is accessed. When
this happens the OERR bit of the RCSTA register is set.
The characters already in the FIFO buffer can be read
but no additional characters will be received until the
error is cleared. The error must be cleared by either
clearing the CREN bit of the RCSTA register or by
resetting the EUSART by clearing the SPEN bit of the
RCSTA register. |
Interessant, ich forsche mal in dieser Richtung weiter.
|
BID = 738741
QuirinO Schreibmaschine
Beiträge: 2205 Wohnort: Behringersdorf
|
Zitat :
| ormalerweise ließt man zu Beginn das SREG ein und schmeißt den Zustand auf den Stack |
Meinst du mit S-Reg das Status Register? Das speichere Ich zwischen in einer Variable bei Aufruf des Interrupt-Vektors per
Code : |
movwf w_temp ; status retten
swapf STATUS,w
movwf status_temp |
|
|
BID = 738753
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Ja, SREG meint das Statusregister.
Zitat :
|
Wie krass die Abarbeitungszeit doch zusammenschmilzt wenn man das mal durchrechnet.
|
Liefert der ADC denn auch zyklisch Werte? Wenn ja, gibt es ja periodische Überschneidungen zwischen IRQs vom f-Zähler und dem ADC, sowie sporadische IRQs (UART).
Das kann also durchaus knapp werden.
Da ich deine Bedingungen nicht kenne, kann man natürlich nur sehr allgemein antworten:
· Frequenz hardwaremäßig teilen ⇒ weniger IRQs vom f-Zähler
· CPU-Takt erhöhen
Schau mal im Datenblatt nach, was in dem Fall passiert, wenn ein Interrupt auftritt, während der Prozessor gerade die ISR ausführt.
Ich vermute, dass, wenn der neue IRQ nicht abgefragt wird, danach diese Routine erneut gestartet wird. Aber danach, nicht sofort. Also zumindest kein Overflow.
Ich würde wie gesagt mal neben der Theorie praktisch nachgucken, wie stark der Professor ausgelastet ist, indem du die oben vorgeschlagene Methode anwendest (IO-PIN mit Oszi verbinden → ISR-Start: IO ← 1; ISR-Ende: IO ← 0)
_________________
|
|
Zum Ersatzteileshop
Bezeichnungen von Produkten, Abbildungen und Logos , die in diesem Forum oder im Shop verwendet werden, sind Eigentum des entsprechenden Herstellers oder Besitzers. Diese dienen lediglich zur Identifikation!
Impressum
Datenschutz
Copyright © Baldur Brock Fernsehtechnik und Versand Ersatzteile in Heilbronn Deutschland
gerechnet auf die letzten 30 Tage haben wir 19 Beiträge im Durchschnitt pro Tag heute wurden bisher 9 Beiträge verfasst © x sparkkelsputz Besucher : 182422746 Heute : 1786 Gestern : 5459 Online : 355 29.11.2024 11:48 8 Besucher in den letzten 60 Sekunden alle 7.50 Sekunden ein neuer Besucher ---- logout ----viewtopic ---- logout ----
|
xcvb
ycvb
0.075404882431
|