Forum: Mikrocontroller und Digitale Elektronik PIC16F1825 - I2C Acknowledge Problem?


von Manfred F. (dermanfred)


Angehängte Dateien:

Lesenswert?

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.

von Peter K. (Firma: www.pic-microcontroller.de) (peter_k)


Lesenswert?

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.

von Manfred F. (dermanfred)


Lesenswert?

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?

von Test (Gast)


Lesenswert?

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 ...

von Manfred F. (dermanfred)


Lesenswert?

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
von McMix (Gast)


Lesenswert?

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.

von uxdx (Gast)


Lesenswert?

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

von mh (Gast)


Lesenswert?

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

von Andreas H. (ahz)


Lesenswert?

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.

von Vancouver (Gast)


Lesenswert?

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?

von Andreas H. (ahz)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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.

von reminder (Gast)


Lesenswert?

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.

von reminder (Gast)


Lesenswert?

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.

von Manfred F. (dermanfred)


Lesenswert?

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
von reminder (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Andreas H. (ahz)


Lesenswert?

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

von Ingo S. (schicki)


Lesenswert?

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

von Manfred F. (dermanfred)


Lesenswert?

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
Noch kein Account? Hier anmelden.