Hallo Eigentlich wollte ich ein kleines OLED-Display mit SSD1306 Controller über den I2C-Bus ansteuern. Da ich aber die I2C Adresse des Displays nicht kenne habe ich es erstmal mit einem I2C EEPROM (24C32)versucht. Dazu aktiviere ich den Bus, sende danach vier Byte an das EEPROM (Adresse mit Schreibwunsch, Adresse im EEPROM high, Adresse im EEPROM low, und zum Schluss ein Datenbyte welches in das EEPROM geschrieben werden soll. Danach gebe ich den Bus wieder frei, lege eine kleine Pause ein und wiederhole das ganze endlos, um es mit dem Oszilloskop kontrollieren zu können. Man sieht deutlich die vier zu übertragenden Byte auf SDA und den Takt auf SCL. Das fatale ist nun, das das Bild genauso aussieht, wenn ich eine falsche EEPROM Adresse sende oder das EEPROM sogar ganz entferne, der I2C-Bus also "offen" ist. Ich vermute das es an meiner Auswertung des Acknowledge liegt, weis aber nich weiter.
Hallo Hier mal eine Routine für das EEPROM 24C02. I2C1_Wr(0xA2) ' send byte via I2C (device address + W) I2C1_Wr(2) ' send byte (address of EEPROM location) I2C1_Wr(0xAA) ' send data (data to be written) I2C1_Stop() ' issue I2C stop signal Delay_100ms() I2C1_Start() ' issue I2C start signal I2C1_Wr(0xA2) ' send byte via I2C (device address + W) I2C1_Wr(2) ' send byte (data address) I2C1_Repeated_Start() ' issue I2C signal repeated start I2C1_Wr(0xA3) ' send byte (device address + R) LATB = I2C1_Rd(0) ' Read the data (NO acknowledge) I2C1_Stop() ' issue I2C stop signal Peter.
Hallo Peter Erstmal Danke für die schnelle Reaktion. Ich habe Deinen Code verstanden. Das ist jedoch nur der Ablauf zum schreiben oder lesen des EEPROMs. Mein Problem ist jedoch warscheinlich die Auswertung des Acknowledge Signals, um zu sehen das der Slave auf dem BUS überhaupt (unter dieser Adresse) existiert. Wie gesagt kann ich eine richtige oder auch eine falsche Adresse senden. Das Oszillogramm sieht immer gleich aus. Wenn ich aber kein Acknowledge vom EEPROM empfange, weil die Adresse falsch ist, müsste das Programm in "I2C_warte" hängen bleiben. Ich werte das Interrupt Flag des I2C Modules des PIC aus. void I2C_warte (void) { while (!SSP1IF) continue; // Warten bis fertig SSP1IF = 0; // Interrupt Flag wieder löschen __delay_us (10); // Test } Wo ist der Wurm drin?
Ohne deinen Quellcode und die Information wie du den SSP1-Interrupt konfiguriert hast, wird dir niemand helfen können zu beurteilen wieso sich das SSP1-InterruptFlag SSP1IF verhält ...
Hallo Test (Gast) Der Interrupt für das I2C-Modul wird garicht konfiguriert und es wird auch kein Interupt ausgelöst bzw abgearbeitet. Es wird nur das Flag ausgewertet.
:
Bearbeitet durch User
So wie ich die I2C Implementation beim Pic verstanden habe, wird das SSP1IF Flag immer gesetzt, wenn das Byte versendet und das Ack eingelesen wurde - unabhängig vom Zustand des ACK-Bits. Ob eine positves Ack wmpfangen wurde, sieht man dann im Zustand des ACKSTAT- Bits im SSP1CON2-Register.
Manfred F. schrieb: > Der Interrupt für das I2C-Modul wird garicht konfiguriert PEIE im INTCON sollte gesetzt sein. Anbei meine seit langem funktionierenden I2C-Routinen für den PIC16F1825, zwar in ASM, aber wohl verständlich. Da ASM, müssen ggf. BANK-Befehle gesetzt werden, das macht C ja automatisch.
1 | ;*********************************************************************** |
2 | ;*********************************************************************** |
3 | ; Routinen fuer I2C |
4 | ; Bus uebernehmen i2c_on |
5 | ; W senden i2c_tx |
6 | ; Byte empfangen i2c_rx (nach w und d_byte) |
7 | ; Bus freigeben i2c_off |
8 | ;*********************************************************************** |
9 | |
10 | ; schreiben W nach Adresse adr_l/adr_h im I2C-Device adr_e |
11 | |
12 | ; *** Spezialversion fuer TSIC-Logger |
13 | ; *** schreibt 2 Daten HI/LO, dann 2 Stop-Bytes FF FF (= Kennung, dass letzter Datensatz) |
14 | |
15 | write_i2c_2 |
16 | call i2c_on ; Bus start |
17 | call i2c_set ; setze Adress-Pointer auf 1. Speicherzelle des aktuellen Satzes |
18 | |
19 | movfw te0_hi ; HI Datenbyte von TSIC #0 nach W |
20 | call i2c_tx ; schreibe W nach Speicherzelle |
21 | movfw te0_lo ; LO Datenbyte von TSIC #0 nach W |
22 | call i2c_tx ; schreibe W nach Speicherzelle +1 |
23 | |
24 | ; call i2c_off ; Bus stop |
25 | |
26 | ; naechste Adresse EEPROM |
27 | |
28 | movlw 0x02 ; uebernaechste interne Adresse LO |
29 | addwf adr_l, f |
30 | bc $+2 ; Carry abfragen |
31 | goto ffff |
32 | incfsz adr_h, f ; naechste interne Adresse HI |
33 | goto ffff |
34 | |
35 | goto ende ; EEPROM voll -> Schluss mit allem |
36 | |
37 | ffff |
38 | ; movfw eewait ; eewait msec warten auf EEPROM |
39 | ; call waitw |
40 | |
41 | ; call i2c_on ; Bus start |
42 | ; call i2c_set ; setze Adress-Pointer auf 1. Speicherstelle des naechsten Satzes |
43 | |
44 | movlw 0xFF ; FF = Endebedingung |
45 | call i2c_tx ; schreibe FF nach Speicherzelle +2 |
46 | movlw 0xFF ; FF = Endebedingung |
47 | call i2c_tx ; schreibe FF nach Speicherzelle +3 |
48 | |
49 | call i2c_off ; Bus stop |
50 | return |
51 | |
52 | ;*********************************************************************** |
53 | |
54 | ; lesen aktuelle Adresse im I2C-Device adr_e nach W |
55 | |
56 | read_i2c_a |
57 | call i2c_on ; Bus start |
58 | goto read22 |
59 | |
60 | ; lesen Adresse adr_l/adr_h im I2C-Device adr_e nach W |
61 | |
62 | read_i2c |
63 | call i2c_on ; Bus start |
64 | call i2c_set ; setze Adress-Pointer |
65 | call i2c_ron ; Bus repeated start |
66 | |
67 | read22 |
68 | movfw adr_e ; 1010 xxx1 I2C-Adresse fuer lesen |
69 | iorlw b'00000001' ; letztes Bit 1 = read Data |
70 | call i2c_tx ; sende I2C-Adresse |
71 | call i2c_rx ; lesen Speicherzelle nach W |
72 | ; call i2c_ack |
73 | |
74 | call i2c_off ; Bus stop |
75 | return |
76 | |
77 | ;*********************************************************************** |
78 | |
79 | ; setze Adresspointer auf adr_l/adr_h im I2C-Device adr_e |
80 | |
81 | i2c_set |
82 | movfw adr_e ; 1010 xxx0 I2C-Adresse fuer schreiben |
83 | andlw b'11111110' ; letztes Bit 0 = write Data |
84 | call i2c_tx ; sende I2C-Adresse |
85 | movfw adr_h |
86 | call i2c_tx ; sende interne Adresse HI |
87 | movfw adr_l |
88 | call i2c_tx ; sende interne Adresse LO |
89 | return |
90 | |
91 | ;*********************************************************************** |
92 | |
93 | ; I2C-Bus im Master-Mode uebernehmen - START |
94 | |
95 | i2c_on |
96 | bank0 |
97 | bcf PIR1, SSP1IF ; SSP1IF Bit zuruecksetzen |
98 | bank4 |
99 | bsf SSP1CON2, SEN ; start condition |
100 | bank0 |
101 | btfss PIR1, SSP1IF ; Bus schon frei ? |
102 | goto $-1 ; |
103 | bcf PIR1, SSP1IF ; ja, Bus ist uebernommen |
104 | return |
105 | |
106 | ;*********************************************************************** |
107 | |
108 | ; I2C-Bus im Master-Mode uebernehmen - repeated START |
109 | |
110 | i2c_ron |
111 | bank0 |
112 | bcf PIR1, SSP1IF ; SSP1IF Bit zuruecksetzen |
113 | bank4 |
114 | bsf SSP1CON2, RSEN ; repeated start condition |
115 | bank0 |
116 | btfss PIR1, SSP1IF ; Bus schon frei ? |
117 | goto $-1 ; |
118 | bcf PIR1, SSP1IF ; ja, Bus ist uebernommen |
119 | return |
120 | |
121 | ;*********************************************************************** |
122 | |
123 | ; I2C-Bus wieder freigeben - STOP |
124 | |
125 | i2c_off |
126 | bank0 |
127 | bcf PIR1, SSP1IF ; SSP1IF Bit zuruecksetzen |
128 | bank4 |
129 | bsf SSP1CON2, PEN ; Bus Freigabe anweisen |
130 | bank0 |
131 | btfss PIR1, SSP1IF ; Bus schon frei ? |
132 | goto $-1 ; |
133 | bcf PIR1, SSP1IF ; ja, Bus frei |
134 | return |
135 | |
136 | ;*********************************************************************** |
137 | |
138 | ; ein Byte aus W auf I2C senden |
139 | |
140 | i2c_tx |
141 | bank4 |
142 | movwf SSP1BUF ; W auf I2C-Bus senden |
143 | bank0 |
144 | btfss PIR1, SSP1IF ; ACK schon empfangen ? |
145 | goto $-1 ; |
146 | bcf PIR1, SSP1IF ; ja |
147 | return |
148 | |
149 | ;*********************************************************************** |
150 | |
151 | ; ein Byte vom I2C empfangen |
152 | |
153 | i2c_rx |
154 | bank4 |
155 | bsf SSP1CON2, RCEN ; Daten Empfang einschalten |
156 | bank0 |
157 | btfss PIR1, SSP1IF ; Daten empfangen ? |
158 | goto $-1 ; |
159 | bcf PIR1, SSP1IF ; ja |
160 | bank4 |
161 | movf SSP1BUF, W ; empfangene Daten -> W |
162 | bank0 |
163 | return |
164 | |
165 | ;*********************************************************************** |
166 | |
167 | ; ACK bei naechsten Byte |
168 | |
169 | i2c_ack |
170 | bank4 |
171 | bsf SSP1CON2, ACKEN ; ACK bei naechsten Byte |
172 | bank0 |
173 | return |
Manfred F. schrieb: > Ich vermute das es an meiner Auswertung des Acknowledge liegt, weis aber > nich weiter. Hast du mal überprüft, ob dein Code mit dem Inhalt des DB übereinstimmt?
1 | 25.6.6.4 Typical Transmit Sequence |
2 | 25.6.7.4 Typical Receive Sequence |
Manfred F. schrieb: > Ich vermute das es an meiner Auswertung des Acknowledge liegt, weis aber > nich weiter. WO wertest Du das ACk den aus? Das Result des I2C_write_byte() ignorierst Du ja fleissig. /regards P.S: Bevor Du PICs probierst wäre es evtl sinnvoll erst mal zu lernen, wie man Src-code postet. Ein .png ist da selten sinnvoll, insbesondere wenn es komplizierter wird.
Vielleicht habe ich das Problem nicht ganz verstanden, aber wenn du bei offenem Bus (also ohne EEPROM) das geiche Timing siehst, dann bedeutet dass, das der EEPROM gar kein Acknowledge erzeugt (und der PIC also auch keines auswerten kann). Auf dem Oszi-Bild sieht man Bursts von 9 Takten, der 9. ist dabei der Ack-Impuls, in diesem Takt muss der Slave das SDA-Signal auf Low ziehen. Das ist im Bild nicht so eindeutig zu erkennen, ob das der Fall ist. Triggere mal nur auf einen einzelnen Bust und schaue genau hin, was beim 9. Takt passiert. Btw, die beiden unteren Anschlüsse der Widerstände in deinem Foto berühren sich nicht zufälligerweise?
Andreas H. schrieb: > WO wertest Du das ACk den aus? > Das Result des I2C_write_byte() ignorierst Du ja fleissig. Ach ja. Hatte ich übersehen (dawn .png^^): Deine I2C_write_byte() ist ja auch noch buggy. Nimm lieber Diese, geklaut von https://aticleworld.com/interfacing-eeprom-with-pic-microcontroller-i2c-based/:
1 | // Function Purpose: I2C_Write_Byte transfers one byte
|
2 | bit I2C_Write_Byte(unsigned char Byte) |
3 | {
|
4 | SSPBUF = Byte; // Send Byte value |
5 | while(!SSPIF); // Wait for it to complete |
6 | SSPIF = 0; // Clear the flag bit |
7 | |
8 | return ACKSTAT; // Return ACK/NACK from slave |
9 | }
|
Dann bekommst du Du das ACK Bit als Ergebnis zurück. /regards
Manfred F. schrieb: > Wenn ich aber kein Acknowledge vom EEPROM empfange, weil die Adresse > falsch ist, müsste das Programm in "I2C_warte" hängen bleiben. Das wäre ein schlechter Bus, wenn er hängen bleiben könnte. Er liest ein NACK ein, welches Du auswerten solltest.
Manfred F. schrieb: > I2C_Aufbau.JPG Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen.
reminder schrieb: > Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Aber im Internet steht zu lesen dass man Abblock-Kondensatoren gar nicht braucht weil es auch ohne geht. Und weil die Kondensatoren soviel Geld kosten und soviel mehr Arbeit machen.
Hallo Erstmal Danke an Alle die geholfen haben. McMix schrieb: > So wie ich die I2C Implementation beim Pic verstanden habe, wird das > SSP1IF Flag immer gesetzt, wenn das Byte versendet und das Ack > eingelesen wurde - > unabhängig vom Zustand des ACK-Bits. > Ob eine positves Ack wmpfangen wurde, sieht man dann im Zustand des > ACKSTAT- Bits im SSP1CON2-Register. Ich habe nochmal das DB des PIC studiert. Du hast Recht, Der Empfang des ACK-Bits wird in ACKSTAT abgelegt. Ich habe mein Prog dementsprechend geändert, aber nun hängt es immer wenn ich ACKSTAT auf low überprüfe. Als wenn von Slave (EEPROM) kein Acknowledge gesendet würde. uxdx schrieb: > PEIE im INTCON sollte gesetzt sein. ok, habe ich gemacht, ändert aber nix. > Anbei meine seit langem funktionierenden I2C-Routinen für den > PIC16F1825, zwar in ASM, aber wohl verständlich. Da ASM, müssen ggf. > BANK-Befehle gesetzt werden, das macht C ja automatisch. Danke dafür. Dies entspricht genau meinen C-Routinen. Allerdings kann ich nicht erkennen wo Du das ACK auswertest. genau wie bei mir wertest Du bei jeder Aktion das Interrupt Flag SSPIF aus. Wenn Du mal Dein EEPROM aus der Fassung ziehst müsste das Programm trotzdem weiter laufen, da das SSPIF Flag vom PIC gesetzt wird und Acknowledge nicht geprüft wird. mh schrieb: > Hast du mal überprüft, ob dein Code mit dem Inhalt des DB > übereinstimmt?25.6.6.4 Typical Transmit Sequence ok, habe ich gemacht. EEPROM schreiben entspricht meiner Meinung nach Punkt 25.6.6.4. Andreas H. schrieb: > WO wertest Du das ACk den aus? > Das Result des I2C_write_byte() ignorierst Du ja fleissig. ok, Eine Auswertung des ACK habe ich nun implementiert. Nun hängt das Programm genau dort. > P.S: Bevor Du PICs probierst wäre es evtl sinnvoll erst mal zu lernen, > wie man Src-code postet. Ein .png ist da selten sinnvoll, insbesondere > wenn es komplizierter wird. Ich will mich bessern ;-) Aber wie geht das? reminder schrieb: > Ich muss bei jedem IC ein Abblock-C von VCC nach GND anschliessen. Vorne am Steckbrett sitzt ein 10uF Tantal und ein 100nF Keramikkondi. (Sieht man auf dem Foto nicht) Ich habe nun trotzdem nochmal je IC einen 100nF Keramikkondensator parallel zur Stromversorgung geschaltet (man waren die teuer). Also lange Rede kurzer Sinn: Die Auswertung des Inetrruptflgs klappt einwandfrei. Dann kann ich jedoch das EEPROM aus der Fassung ziehen und es läuft totzdem. Versuche ich ACK auszuwerten bleibt das Prog dort hängen. :-(((
:
Bearbeitet durch User
Manfred F. schrieb: > und ein 100nF Keramikkondi. (Sieht man auf dem Foto nicht) Der gehört ja auch ans IC und nicht an den Arsch des Steckbretts.
Manfred F. schrieb: > ok, Eine Auswertung des ACK habe ich nun implementiert. Nun hängt das > Programm genau dort. Warum willst Du es denn dort hängen lassen? Nach einem NACK sendest Du noch ein STOP und meldest der aufrufenden Instanz, daß die Aktion fehlgeschlagen ist. Nach einem Schreiben ist der EEPROM für ~5ms nicht mehr ansprechbar, d.h. dann ist ein NACK korrekt.
Manfred F. schrieb: > Andreas H. schrieb: >> WO wertest Du das ACk den aus? >> Das Result des I2C_write_byte() ignorierst Du ja fleissig. > > ok, Eine Auswertung des ACK habe ich nun implementiert. Nun hängt das > Programm genau dort. > Naja, ohne Code ist das mühseliges Rumraten. Nur falls das noch nicht aufgefallen ist: Das ACK ist **low-activ**, d.h. ein gültiges ACK ist 0V, ein NACK ist 5V (oder was auch immer die benutzte I2C Spannung ist, also da wo die PullUps dranhängen) Manfred F. schrieb: > Andreas H. schrieb: >> P.S: Bevor Du PICs probierst wäre es evtl sinnvoll erst mal zu lernen, >> wie man Src-code postet. Ein .png ist da selten sinnvoll, insbesondere >> wenn es komplizierter wird. > > Ich will mich bessern ;-) > Aber wie geht das? Entweder die Files als Anhang an die Mail dranhängen (da gibts einen Button. Suchen hilft ... ;) oder den Code mit den entsprechenden Tags direkt einfügen. Die Tags findest Du unter https://www.mikrocontroller.net/articles/Formatierung_im_Forum. Peter D. schrieb: > Warum willst Du es denn dort hängen lassen? > Nach einem NACK sendest Du noch ein STOP und meldest der aufrufenden > Instanz, daß die Aktion fehlgeschlagen ist. > Nach einem Schreiben ist der EEPROM für ~5ms nicht mehr ansprechbar, > d.h. dann ist ein NACK korrekt. Das habe ich auch noch nicht verstanden ;) /regards
Hallo, ich habe mir den Code auch mal angesehen. Ich arbeite mit dem XC8. Ich musste an der Stelle vor jedem Write ein I2CIdle(); einbauen, dann lief es. Ich kann Dir meine LIB für den PIC18F46K80 zusenden. Bitte schreibe mir eine PN dann schicke ich Sie Dir zu. Ingo
Ingo S. schrieb: > Hallo, > > ich habe mir den Code auch mal angesehen. Ich arbeite mit dem XC8. > Ich musste an der Stelle vor jedem Write ein I2CIdle(); einbauen, dann > lief es. Ich kann Dir meine LIB für den PIC18F46K80 zusenden. Bitte > schreibe mir eine PN dann schicke ich Sie Dir zu. > > Ingo Hallo Ingo Datei ist angekommen Danke schon mal im Voraus. Habe Heute leider kaum Zeit und schau Morgen mal rein. Schöne Grüße und Dankeschön.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.