Hi Leute,
ich versuche gerade ein OLED-Display per I2C anzusteuern. Das Display
funktionierte in einer anderen Schaltung, ist also in Ordnung. Pull-ups
sind angeschlossen. Es klappt leider nicht mit dem Code - vermute ich,
weil beim debuggen 80% aller Programmstopps im Interrupt landen. Könnte
jemand mal meinen Code ansehen? Von alleine finde ich den Fehler nicht.
Andere Beispiele wie z.B. der von Driller und offizielle ST-Doku habe
ich mir schon durchgeschaut, will die aber nicht guttenbergen. Hier ist
das Programm:
Viktor B. schrieb:> I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
... und wo ist dein Interrupt-Handler dafür?
Viktor B. schrieb:> // enable alternate functions (why?);
Wenn du nicht weisst warum, warum machst du es dann?
Viktor B. schrieb:> // EmBitz does not recognize CR1_STOP_Set> // send start condition; EmBitz doesnt recognize it also
Was hat EmBitz damit zu tun? Gar nichts. Der Programmierer
ist dafür verantwortlich dass er dem Compiler die Symbol-
definitionen bekannt gibt.
Viktor B. schrieb:> // No devise with that adress
<device> schreiben ist die Devise
Damit wir hier ein bisschen konkreter auf die Bedürfnisse des Displays
eingehen können, solltest du es benennen (bzw. dessen Controller).
An Deiner Stelle würde ich die Kommunikation mit einem Logik-Analyzer
untersuchen. Stimmt sie mit dem Quelltext überein? Wenn nicht, hast du
wohl die I²C Schnittstelle falsch benutzt. Wenn sie jedoch überein
stimmt, ist etwas an der Initialisierungs-Sequenz falsch.
Falls es wie ich vermute ein SSD1306 sein sollte, darfst du gerne von
meinem Code abgucken.
http://stefanfrings.de/esp8266/WIFI-Kit-8-Test2.zip
Meine Initialisierungssequenz ist etwas anders als deine. Sie beruht auf
Informationen aus zwei unterschiedlichen Datenblättern, plus diversen
Tipps die ich im Internet gefunden habe. Ich sehe da einige Unterschiede
(abgesehen von vermutlich harmlosen Abweichungen bei der Reihenfolge der
Kommandos).
Falls es kein SSD1306 ist, könnte das Display eventuell Pausen zwischen
bestimmten Kommandos benötigen.
Noch was: Eventuell magst du zuerst mal die Bit-Banging Methode aus
meinem Code mit deiner Initialisierungssequenz versuchen. Oder du
versuchst es mit der Polling Methode wie hier umgesetzt:
http://stefanfrings.de/stm32/index.html#i2c Beide kann man tadellos
debuggen.
So kannst du wenigstens herausfinden, ob der Fehler in deiner
Initialisierungssequenz steckt, oder woanders.
Rätsel Rater schrieb:> Viktor B. schrieb:>> I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);>> ... und wo ist dein Interrupt-Handler dafür?
Es gibt nur 2 Interruptvektoren - Event und Error. Quelle: Reference
Manual für STM32F10x Controller, Seite 772, Abbildung 278, I2C interrupt
mapping diagram.
Rätsel Rater schrieb:> Viktor B. schrieb:>> // enable alternate functions (why?);>> Wenn du nicht weisst warum, warum machst du es dann?
Weil es in einem der Beispiele so stand. War mir nicht sicher, was es
sollte, deswegen hab ich es im ursprünglichem Zustand gelassen.
Rätsel Rater schrieb:> Viktor B. schrieb:>> // EmBitz does not recognize CR1_STOP_Set>> // send start condition; EmBitz doesnt recognize it also>> Was hat EmBitz damit zu tun? Gar nichts. Der Programmierer> ist dafür verantwortlich dass er dem Compiler die Symbol-> definitionen bekannt gibt.Rätsel Rater schrieb:> Viktor B. schrieb:>> // EmBitz does not recognize CR1_STOP_Set>> Merke: EmBitz ist kein Compiler.
Ich habe auch nicht gesagt, EmBitz wäre ein Compiler. Und ja, der
Compiler hinter EmBitz erkennt zwar andere Definitionen aus der selben
Datei, aber genau die eine irgendwie nicht. Was soll man da bloß machen?
Rätsel Rater schrieb:> Viktor B. schrieb:>> // No devise with that adress>> <device> schreiben ist die Devise
Oh bitte, war das nötig?
Stefanus F. schrieb:> An Deiner Stelle würde ich die Kommunikation mit einem Logik-Analyzer> untersuchen.
EInen Logik Analyzer habe ich mit vor einigen Tagen bestellt, der ist
leider noch nicht angekommen. Den I2C-Ausgang werde ich so schnell wie
möglich damit testen.
Das Display ist - richig geschätzt - ein SSD1306. Die
Initialisierungssequenz ist aus einem anderen Projekt, wo es zuverlässig
funktioniert, d.h. an der Sequenz wird es erstmal nicht liegen. Ich bin
mir sicher, ich hätte den I2C-Code vermurkst, aber wie?.. Interrupt
flags sollen sich durch Hardware beim Lesen der Status-Register
rücksetzen. Den I2C-takt habe ich eingeschaltet. Welche weit
verbreiteten Fehler gibt es noch?
Viktor B. schrieb:> Ich habe auch nicht gesagt, EmBitz wäre ein Compiler.
Sehr wohl hast du das (indirekt) gesagt, denn du hast EmBitz
die Schuld zugeschrieben dass er es nicht findet. Dabei ist
es eben der Compiler der es nicht findet. Und genau deswegen
weil du ihm nicht gesagt hast in welcher Datei er es finden
soll. Dumm wäre es jetzt stm32f10x_i2c.c zu inkludieren.
Der Grund ist vermutlich eine gewisse Absicht von den SPL-Machern,
da sie die entsprechenden Defines "privat" gemacht haben.
Du solltest I2C_GenerateSTOP (..) aus stm32f10x_i2c.c verwenden.
Gleiches gilt für I2C_GenerateSTART (..)
Viktor B. schrieb:> Es gibt nur 2 Interruptvektoren - Event und Error.
Ok, das war mein Wissensdefizit.
Dann solltest du in den Interrupt-Handlern noch entsprechend
gesetzte Flags mit I2C_ClearITPendingBit (..) löschen.
Rätsel Rater schrieb:> Dumm wäre es jetzt stm32f10x_i2c.c zu inkludieren.
Sollte schon mit conf.h drin sein.
Rätsel Rater schrieb:> Du solltest I2C_GenerateSTOP (..) aus stm32f10x_i2c.c verwenden.> Gleiches gilt für I2C_GenerateSTART (..)
Die Methode I2C_GenerateStart und die andere sind dasselbe wie I2C1->CR1
|= I2C_CR1_START, nur halt mit einem assert, damit ich bloß nicht sowas
wie TIM1->OCRA |= I2C_CR1_START compiliere (klappen würde es ja, wäre
trotzdem eine schlechte Idee.) Für einen kleineren Projekt fügt das nur
Abstraktionslayer hinzu.
Rätsel Rater schrieb:> ann solltest du in den Interrupt-Handlern noch entsprechend> gesetzte Flags mit I2C_ClearITPendingBit (..) löschen.
Diese werden von der Hardware gelöscht, wenn man die Register SR1 und
SR2 ausliest. Außerdem sind die Bits als "read-only" im Reference Manual
markiert.
Viel Debugging später hat sich gezeigt, dass der Programmfluss im
Interrupt gestört wird bzw. dort der Fehler liegt. Irgendwie springt es
von der Zeile
1
if(I2C_num_bytes){// in case its available
bei I2C_num_bytes = 0 anstatt zur nächsten schließenden geschweiften
Klammmer einfach völlig aus dem Interrupt. Ich bin ehrlich gesagt
ratlos.
UPD.: Außerdem enthält das Register I2C1->SR1 auch nach dem Senden des
ersten Datenbytes nur den bit ADDR gesetzt. Wieso? sollte es nicht
automatisch resettet werden und bei der nächsten Übertragung das TXE-bit
gesetzt werden?
Viktor B. schrieb:> Irgendwie springt es> von der Zeile if(I2C_num_bytes){ // in case> its available> bei I2C_num_bytes = 0 anstatt zur nächsten schließenden geschweiften> Klammmer einfach völlig aus dem Interrupt.
Wieso? Ist doch richtig: In dem Interrupt ist ja auch nichts mehr zu
tun!
Geschweifte Klammern sind keine Sprungmarken, sondern kennzeichnen nur
logische Programmblöcke. Ob und wie der Compiler dafür Code erzeugt,
bleibt ihm überlassen.
Thomas E. schrieb:> In dem Interrupt ist ja auch nichts mehr zu> tun!
Ich hätte erwartet, dass das Programm den IF-Block, der in die Klammern
eingeschlossen ist, überspringt und mit
Viktor B. schrieb:> Compiler hinter EmBitz erkennt zwar andere Definitionen aus der selben> Datei, aber genau die eine irgendwie nicht. Was soll man da bloß machen?
Sie ohne Tippfehler zu schreiben wär spontan mein erster Gedanke.
Viktor B. schrieb:> Thomas E. schrieb:>> In dem Interrupt ist ja auch nichts mehr zu>> tun!>> Ich hätte erwartet, dass das Programm den IF-Block, der in die Klammern> eingeschlossen ist, überspringt und mit>
1
elseif((I2C1->SR1&I2C_SR1_BTF))
> weitermacht?..
Dafür solltest du deine geschweiften Klammern mal sortieren.
Du hast hier zwei verschachtelte If-Blöcke...
Mein Logic Analyser ist endlich da. Bringt nur mäßig Licht ins Dunkle.
Der µC weiß nichts mit der Clock anzufangen?...
Noch interessanter: Ich hab den Code umsortiert, nun sieht die Routine
so aus:
> Bringt nur mäßig Licht ins Dunkle.
Das sehe ich aber ganz anders. Wir können hier sehen, dass ein top
sauberes Timing vorliegt, dass der Master die Zahl 0x78 sendet (= 7bit
Adresse 0x3C), und dass der Slave diese mit ACK bestätigt. Dass heisst,
der Slave hat reagiert, er fühlt sich angesprochen.
Aber die nachfolgenden Bytes und das abschließende das Stop Signal
(roter Punkt) fehlen. Sieht für mich 100% nach einem Softwarefehler auf
dem Master aus.
Bau mal in deine vier if/else Abschnitte Trace Meldungen
(http://stefanfrings.de/stm32/index.html#traceswo) ein um zu sehen,
welche davon in welcher Reihenfolge ausgeführt werden.
Stefanus F. schrieb:> Bau mal in deine vier if/else Abschnitte Trace Meldungen> (http://stefanfrings.de/stm32/index.html#traceswo) ein um zu sehen,> welche davon in welcher Reihenfolge ausgeführt werden.
Ich hab so schnell keine Möglichkeit gefunden, Trace Meldungen in EmBitz
zu lesen, deswegen musste das Debugging per LED (und Logic Analyser)
erfolgen. Der Interrupt schien sich sinnlos zu wiederholen. Beim
Studieren des Datenblattes ist mir dann eine Zeile aufgefallen:
> As soon as the address byte is sent,the ADDR bit is set by hardware> and an interrupt is generated if the ITEVFEN bit is set.> Then the master waits for a read of the SR1 register> *followed by a read of the SR2 register*
Als ein Experiment wurde also die ISR abgeändert. Folgende Zeile wurde
hinzugefügt:
1
elseif(((I2C1->SR1&I2C_SR1_ADDR)||(I2C1->SR1&I2C_SR1_TXE))&&(I2C_pointer->I2C_num_bytes>0))// if not, then send data
2
{
3
if(I2C1->SR2&I2C_SR2_BUSY)//seems crucial
4
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
5
I2C1->DR=*(I2C_pointer->I2C_reading_pointer);
6
I2C_pointer->I2C_reading_pointer++;
7
I2C_pointer->I2C_num_bytes--;
8
}
Und siehe da, die ganze Initialisierungssequenz wird übertragen. Zwar
hab ich komischerweise Schnee im Display, aber es ist besser als gar
nichts. LA sagt, die zweite Übertragung endet mit einer Stop Condition
genau nach der Start Condition. Da muss ich wohl nach anderen Fehlern
suchen.
Update: der zweite Schreibvorgang (foo) klappt bestens, wenn ich das
Programm mit dem Debugger Schritt für Schritt durchgehe. Dafür, wenn ich
das Programm laufen lasse, wird nur ein Start und gleich darauf ein Stop
gesendet..? WTF?
Lama schrieb:> Schaue dir doch mal die Bits im SR mal ganz genau an :-)
Welches SR von der beiden?
Im Datenblatt oder während der Programmausführung?
Welche Bits müssten denn Ihrer Meinung nach genau angeschaut werden?