Guten Tag,
erstmal Sorry für den ungenauen Thread-Titel, aber es ist etwas schwer
zu schildern, was man wohl auch an der Textlänge erkenne kann...
Ich hab hier ein Projekt mit einem PCA9564 I²C Host Controller an einem
Motorola MC68VZ328 µC.
Es hängt etliches an I²C Peripherie dran, auch ein paar EPROMs. Der Bus
wird mit einem PCA9548 eines noch in weitere "Sub-Busse" aufgeteilt.
Ich habe das Projekt von einem Kollegen übernommen, der jetzt leider
nicht mehr in der Firma ist. Schaltung und Software sind nicht von mir,
was es für mich nicht einfacher macht mich reinzudenken.
Nun habe ich eine Erweiterung vor bei der ich die bisherige Ansteuerung
der EPROMs verändern muss. Daten werden bisher nur Byte-weise
ausgelesen. Das frisst jede Menge Zeit wenn die komplette Kommunikation
jedes mal neu aufgebaut und neu adressiert wird.
Die EPROMs können sequentiell ausgelesen werden, das funktioniert aber
nicht zu 100% so wie es soll.
Will ich 3 Bytes ab Adresse 0 auslesen erhalte ich zurück Bytes 1 2 und
3. Ein anschliessendes Auslesen fördert dann die Bytes 5 6 und 7 zutage.
Am Oszi hgabe ich auch beobachtet dass die Bytes am Bus erscheinen, aber
wohl nicht von der Software ausgelesen werden.
Um's komplett kompliziert zu machen: Das Auslesen eines einzelnen bytes
funktioniert einwandfrei.
Dann habe ich in der do-Schleife den Aufruf von ResetSI() hinter das
auslesen der Daten aus dem PCA9564-Register (*data =
ReadI2CHostRegister(HostData) & 0xff;) geschoben.
Nun erhlate ich die Bytes 0, 1 und 3. 2 wird "übersprungen".
Anschliessend kommt dann 4, 5 und 7.
Dem Code entnehme ich, dass Aktivität auf der Taktleitung nur ausgelöst
wird wenn das SI-bit zurückgesetzt ist. Dafür habe ich im Datenblatt
aber noch keine Bestätigung gefunden :/
Irgendwas sorgt aber vorher schon dafür dass auf SCL Takte ausgegeben
werden.
Deswegen ist auch schon ein Byte vorhanden wenn ich in die Schleife
springe.
Ich hab das bis jetzt so verstanden:
Irgend ein Befehl lässt SCL tackern bevor die do-Schleife erreicht wird,
und auch vor dem letzten auszulesenden Byte werden Takte ausgegeben ohne
dass das Register ausgelesen wird.
Im Moment bin ich wohl etwas betriebsblind und weiss nicht mehr wo
weitersuchen...
Vielleicht hat ja hier jemand eine Idee wo ich noch draufschauen
könnte...
Bin für jeden Hinweis dankbar!
hier die Auslese-Funktion (schon so geändert wie beschriebe, dass 0 1
und 3 ausgelesen werden)
Ich wuerd mal aufzeichen welche Sequenz da sein sollte, und dann mit dem
Analyzer nachmessen, ob das auch so ist.
Und das sequentiell fuer jedes Bauteil am Bus.
Analyzer hab ich keinen, leider!
Aber das was ich am Oszi sehe ist eigentlich korrekt, bis auf die
Tatsache dass ein Byte zu viel auf dem Bus erscheint.
Das vorletzte Byte wird aber von der Software nicht erfasst.
Bob A. schrieb:> Aber das was ich am Oszi sehe ist eigentlich korrekt, bis auf die> Tatsache dass ein Byte zu viel auf dem Bus erscheint.
Und das Byte erscheint weil Takte kommen die du nicht ausgelöst hast?
>Dann habe ich in der do-Schleife den Aufruf von ResetSI() hinter das>auslesen der Daten aus dem PCA9564-Register (*data =>ReadI2CHostRegister(HostData) & 0xff;) geschoben.
Warum? Die I2CRead() Funktion wird doch auch erfolgreich zum lesen
anderer Bausteine verwendet, oder nur beim EEPROM?
Und was ist mit I2CWrite()? Wenn du 3 Byte lesen willst, musst du doch
erst mal 1 Byte schreiben (Speicheradresse). Anschliessend (mit Repeated
Start oder kompletter neustart) musst du 3 Byte lesen.
Jörg S. schrieb:> Bob A. schrieb:>> Aber das was ich am Oszi sehe ist eigentlich korrekt, bis auf die>> Tatsache dass ein Byte zu viel auf dem Bus erscheint.> Und das Byte erscheint weil Takte kommen die du nicht ausgelöst hast?
Genau so stellt sich das im Moment für mich dar. D.h. irgendwo werden
die Takte wohl ausgelöst werden, ich sehe nur gerade nicht wo :/
>>Dann habe ich in der do-Schleife den Aufruf von ResetSI() hinter das>>auslesen der Daten aus dem PCA9564-Register (*data =>>ReadI2CHostRegister(HostData) & 0xff;) geschoben.> Warum? Die I2CRead() Funktion wird doch auch erfolgreich zum lesen> anderer Bausteine verwendet, oder nur beim EEPROM?
Die Software war eben so gestrickt, dass immer nur ein einzelnes Byte
gelesen wurde. Dann Bus Stopp, neu adressiert und nächstes Byte.
Ziemlich ineffektiv bei mehreren kByte... deswegen möchte ich das ja
ändern.
> Und was ist mit I2CWrite()? Wenn du 3 Byte lesen willst, musst du doch> erst mal 1 Byte schreiben (Speicheradresse). Anschliessend (mit Repeated> Start oder kompletter neustart) musst du 3 Byte lesen.
Stimmt, das funktioniert scheinbar, aber auch hier werden immer nur
einzelne Bytes geschrieben. Weiter getestet hab ich das noch nicht, ich
möchte zuerst mal das Lesen korrekt hinkriegen, danach schau ich mir das
Speichern an. Sollte hier auch ein Problem sein, hoffe ich dass die
gleiche Lösung greift...
Bob A. schrieb:> Die Software war eben so gestrickt, dass immer nur ein einzelnes Byte> gelesen wurde. Dann Bus Stopp, neu adressiert und nächstes Byte.> Ziemlich ineffektiv bei mehreren kByte...
Ja, aber wurde denn bei ALLEN I2C Bausteinen immer nur 1 Byte gelesen?
>> Und was ist mit I2CWrite()? Wenn du 3 Byte lesen willst, musst du doch>> erst mal 1 Byte schreiben (Speicheradresse). Anschliessend (mit Repeated>> Start oder kompletter neustart) musst du 3 Byte lesen.> Stimmt, das funktioniert scheinbar, aber auch hier werden immer nur> einzelne Bytes geschrieben.
Na ja, für die Chip-Adresse und die nachfolgende Speicheradresse sind es
schon mal 2 Bytes auf dem Bus.
> Weiter getestet hab ich das noch nicht, ich> möchte zuerst mal das Lesen korrekt hinkriegen, danach schau ich mir das> Speichern an.
Also wenn ich das richtig verstehe sendest du erst mal keine
Speicheradresse, sondern willst nur 3 Byte lesen? Oben schreibst du doch
aber das du ab Adresse 0 was lesen willst?
Hmm, ja hab mich unklar ausgedrückt, sorry.
Beim schreiben sind es in der Tat zwei Bytes, sogar 3 wenn man es genau
nimmt, denn es sind beim EEPROM 16-bit Adressen.
Die Software ist komplett so gehalten dass immer nur 1 Byte ausgelesen
wurde. Vielleicht hatte der Entwickler damals das gleiche Problem und
hat es damit umgangen, dass er immer alle Bytes einzeln eingelesen hat,
wer weiss das schon??
Ich sende schon die Adresse, danach lese ich immer 3 Bytes. Nach der
Adressierung sind das die Bytes an Adresse 0, 1 und 3. Da die Software
wohl eins überspringt.
Danach steht der Adress-pointer auf Speicherzelle 4, ich lese dann 4, 5
und 7 u.s.w.
Ich bin nicht mehr im Büro, deswegen kann ich jetzt nicht nachprüfen wie
es beim Senden ausschaut.
Montag versuch ich das mal genauer zu inspizieren!
Vielen Dank erstmal an und schönes WE
Bei sowas bin ich ja immer etwas paranoid:
if ((Daten < Anzahl_Daten - 1)
if ((Daten == Anzahl_Daten - 1)
Lieber mal ein paar Klammern zu viel als zu wenig:
if ((Daten < (Anzahl_Daten - 1))
if ((Daten == (Anzahl_Daten - 1))
Nachtrag: Da bleibt aber immer noch ein übler Nachgeschmack.
In deinem Code ist nicht ersichtlich wer wann die Datenübertragung
auslöst. EnableAck() oder DisableAck() sollte man nicht
während des Transfers machen, sondern vorher.
holger schrieb:> EnableAck() oder DisableAck() sollte man nicht> während des Transfers machen, sondern vorher.
Die beiden hab ich mittlerweile auch im Verdacht... aber wie kann ich
dann gewährleisten dass das letzte gelesene Byte nicht acknowledged
wird?
Oder ist das nicht zwingend notwendig? Bisher hab ich das immer so in
den Datenblättern gelesen.
P.S. da ein Kollege gerade das Oszi braucht ist erstmal Pause mit
Debugging :/
Bob A. schrieb:> Ich sende schon die Adresse, danach lese ich immer 3 Bytes. Nach der> Adressierung sind das die Bytes an Adresse 0, 1 und 3. Da die Software> wohl eins überspringt.> Danach steht der Adress-pointer auf Speicherzelle 4, ich lese dann 4, 5> und 7 u.s.w.
Leider wird dadurch nicht klarer was du machst.
Wieso holst du 2x 3 Byte, warum nicht auf einen Schlag 6 Byte?
Gibt es zwischen schreiben der Adresse und lesen der Bytes ein Stop oder
ein Repeated Start?
Gibt es zwischen dem lesen der ersten 3 Bytes und den letzten 3 Bytes
ein Repeated Start?
Das übliche (schnellste) vorgehen wäre ja
- Start,
- Chip Adresse mit Write Bit senden
- 2 Byte Speicheradresse senden
- Repeated Start
- Chip Adresse mit Read Bit senden
- 6 Byte aus EEPROM lesen (beim letzten Byte NACK senden)
- Stop
Stimmt schon, dass es wenig Sinn mach die Daten stückchenweise
auszulesen.
Die 2 x 3 Byte sind ein simpler Test um zu verstehen was da eigentlich
passiert.
Ich hatte das Problem dass beim Lesen mehrere Byte in Folge als erstes
Byte immer (Adresse + 1) ausgelesen wurde.
Durch Ändern der Reihenfolge von
1
*data++=ReadI2CHostRegister(HostData)&0xff;
und
1
ResetSI();
war dieses Problem beseitigt.
Dafür hatte ich mir eine kleine Testschleife gebaut die ohne erneute
Adressierung immer 3 Byte am Stück ausliest.
Hierbei ist mir dann aufgefallen, dass am Schluss auch noch mal Takte
generiert werden, und dann am Ende ein Byte verschluckt wird.
Das ist ja unabhängig davon ob ich 6 am Stück oder 2 mal 3 Byte lese.
(Eigentlich lese ich unendlich viele 3-Byte Pakete... Das Ganze ist, wie
gesagt, nur ein Testaufbau.)
Ich hab das jetzt mal in Anlehnung an holgers Vorschlag umgebaut...
leider keine Veränderung.
Kann es sein dass das Ein-/Ausschalten vom Acknowledge Takte generiert?
Im Datenblatt habe ich dazu nichts gelesen...
Hat hier schon jemand den PCA9564 benutzt und weiss was genaueres?
Gruß und Dank,
Bob
Ohne jetzt alle Beiträge gelesen zu haben: Das ist 100% ein Problem mit
dem ACK. Nach dem Commando fürs Lesen wird das ACK vom Slave gesendet.
Die nachfolgenden Bytes, die du liest, musst du (sprich der Master) mit
einem ACK quittieren. Nur das letzte Byte erhält KEINEN ACK.
Bob A. schrieb:> WriteI2CHostRegister (HostControl, 0xff50);> // senden> ResetSI ();> // Status abfragen> *I2CSTAT = ReadI2CHostRegister(HostStatus) & 0xff;
Ohne zu wissen, was diese magischen Funktionen mit den magischen
Parametern konkret machen, kann man dazu nichts sagen.
Das ist nur Stochern im Nebel.
Man bräuchte die komplette I2C-API-Beschreibung.
Einfacher wird es, wenn man sich Macros schreibt, die wie die benötigten
I2C-Funktionen heißen:
send_start()
send_byte_read_ack()
read_byte_with_ack()
read_byte_with_nack()
send_stop()
Damit kann man viel besser das Programm lesen und nachvollziehen.
Peter
Bob A. schrieb:> // x-tes Datenbyte empfangen?> if ((Daten < Anzahl_Daten - 1) && (*I2CSTAT != 0x50))> {
...
> if ((Daten == Anzahl_Daten - 1) && (*I2CSTAT != 0x58))> {> // ja, Bus Stop
Müsste das nicht jeweils "*I2CSTAT ==" sein ?
Du erwartest ja eigentlich 0x50 (aka. received and acked), bzw 0x58
(aka. received and not acked) beim letzten byte. Prüfen tust Du aber auf
UNGLEICH.
In Deinem Code sieht das so aus, als ob Deine Routine dort einfach
weiter läuft.
Grüße
Andreas
@Andreas:
Nee, das ist schon richtig so,
wenn der Status ungleich ist geht's in die Schleife und dann wird
abgebrochen. In der übergeordneten Funktion kommt dann die
Fehlerbehnadlung.
@Peter:
die magischen Funktionen bedienen den PCA9564.
Mit so spezifische Funktionen kann ich leider nicht dienen. Ob ACK oder
nicth z.Bsp. ist eine Konfigurationssache beim PCA9564. Vor dem Lesen
des letzten Bytes wird das entsprechende Bit im PCA9564 gesetzt.
Den magischen Source-Code kann ich leider erst Morgen liefern...
Vielen Dank für die Hilfe und ganzen Anregungen hier!!
Bob
Ich würde einfach erst mal nur 2 Byte vom EEPROM lesen, ohne Write
zugriff vorher. Damit schliesst man schon mal Fehler an dem Teil aus.
Und dann mal ganz genau mit dem Oszi schauen was da so auf dem Bus genau
passiert. Evt. dazu einen Pin vom µC als Debug Pin konfigurieren und zum
triggern des Oszi benutzen um zu sehen was wann passiert.
@Jörg:
aber genau das mache ich ja, deswegen kam ja erst diese Verwirrung mit
wieso nicht 6 anstatt 2 mal 3. Ich lese halt in einer Schleife immer
fortlaufend...
Bleiben wir mal bei den 3 Bytes die ich auslese:
Start condition ist OK
dann kommt die Adresse vom EEPROM und ACK von eben diesem
dann 2 Bytes mit ACK vom PCA9564
das dritte und eigenltich letzte Byte kriegt dann wieder ein ACK obwohl
es nicht sollte
das 4. Byte bekommt dann aber kein ACK
Stop condition
Die eingelesenen Daten zeigen mir dann dass das dritte Byte
"verschluckt" wurde und ich habe den Inhalt von Byte 1 2 und 4.
Sieht am Oszi eigentlich alles so aus wie es sich gehört, auffällig ist
bloss, dass das 4 Byte zeitlich deutlich schneller hinter dem 3. kommt
als dies der Fall bei den anderen Transfers ist. Zwischen Adresse und
Daten und auch zwischen den Daten habe ich grob die gleiche Zeit.
Das gilt auch bei mehr als 3 Byte, das letzte kommt immer deutlich
kürzer hinter dem Vorletzten als alle vorherigen.
Bei meinem barometer (BMP085) gibt's ein ähnliches Problem.
Ich lese 2 Bytes für die Temperatur aus und anschließend 3 für den
Druck.
Die Bytes speicher ich einfach auf eine SD-Karte im csv.Format.
So sollte es sein:
MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, XLSB-Druck, \n
MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, XLSB-Druck, \n
MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, XLSB-Druck, \n
MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, XLSB-Druck, \n
MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, XLSB-Druck, \n
...
Und so ist es:
MSB-Temp, LSB-Temp, 0, MSB-Druck, LSB-Druck, \n
XLSB-Temp, MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, \n
XLSB-Temp, MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, \n
XLSB-Temp, MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, \n
XLSB-Temp, MSB-Temp, LSB-Temp, MSB-Druck, LSB-Druck, \n
...
Ein einziges mal wird diese Null mitgesendet. Wenn ich am Code etwas
rumstelle kann auch statt der 0 eine 165 an Stelle vom XLSB-Temp stehen.
Deswegen einmal @Bob: Vielleicht verlierst du garnicht dein Byte,
sondern liest drei Bytes aus nachdem du die Adresse schreibst und eins
ist fehlerhaft oder einfach 0 und wird dadurch in deiner Datei oder wo
du es auch immer abspeichert nicht abgespeichert.
@Alle: Woher kommt diese einmalige 0???
@Wolfgang:
nee das Byte ist schon da ich sehe es ja am Oszi, dort kommen nach der
Adressierung ganz klar 4 mal Takte und der Inhalt der 4 Speicherzellen.
Woher deine 0 kommt? Kein Plan! Pollst du oder ist die I²C Routine
interrupt-gesteuert. Beim Pollen könnte es eventuell ein timeout sein...
Immer hilfreich: Oszi oder Logic-analyzer dranhängen und kucken was da
wirklich abgeht!
Viel Erfolg bei der Suche,
Bob
@Bob:
Hier ist die Lösung für mein Problem. Vielleicht liegt es bei dir ja an
etwas ähnlichem.
Der Fehler lag im Auslesen des Multireads.
So hatte ich es:
write()
wait()
start_read()
MSB = get_value()
LSB = get_value()
stop_read()
So soll es sein:
write()
wait()
start_read()
MSB = get_value()
stop_read()
LSB = get_value()
Nach dem "Stop-Generieren" wird vom Slave also vom Barometer noch ein
Byte gesendet. Deswegen muss der Stop vor dem letzten Byte, das man
auslesen möchte, gesendet werden.
Grüße