www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik I2C Programmierungsproblem


Autor: Christoph M. (chrischu91)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe ein problem: ich bekomme beim ausgang von der I2C schnitstelle 
vom PIC16F887 beim SDL ausgang, kein Signal über, wenn ich es mit dem 
Oszilloskop messe...
Ich weiss nicht was ich noch einstellen muss, oder ob ich etwas falsch 
mache oder etwas nicht beachte...
Der SDL ausgang sollte doch immer ein Clock geben oder bin ich da falsch 
informiert? (sofern ich das programm darauf geladen habe das im Anhang 
ist.)
Ich benutze den CCSC Compiler und das MPLAB IDE v8.56
Die RS232 funktioniert ohne Probleme, und es muss mit RS232 laufen!

Könnte mir jemand einen tipp geben oder mir helfen, ich danke schon 
jetzt!

Christoph

Autor: XXX (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

int  read_i2c(int adresse1, int adresse2)
{
  int  daten;
  disable_interrupts(GLOBAL);
  i2c_start();
  i2c_write(0xA1);
  i2c_write(adresse1);
  i2c_write(adresse2);

Fehlt hier i2c_stop(); ??????

  i2c_start();
  i2c_write(0xA0);
  daten = i2c_read(0);
  i2c_stop();
  return(daten);
  enable_interrupts(GLOBAL);
  delay_us(1);
}


Kann es sein, das dort ein stop-Befehl fehlt??

Gruß
Joachim

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was hängt denn an dem I2C dran ?

Autor: Christoph M. (chrischu91)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Joachim: ich muss ja da ein restart machen und die Adresse, zu der er 
hingeht, ist im vorherigen teil, das stopbit setze ich doch erst am 
ende, wenn ich alles fertig geschrieben habe nicht?

@Stefan: es hängt ein EEPROM 24WC32 daran.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kenn den aufruf etwas anders.
Vielleicht hilft es dir ja:

I2C1_Start()           ' issue I2C start signal
    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


    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)
    PORTB = I2C1_Rd(0)     ' Read the data (NO acknowledge)
    I2C1_Stop()

Autor: Bastler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Pull-Up Widerstände angeschlossen?

Autor: Nilix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du ein 24WC32 dran hast, dann mußt du nach dem Sender der 
Slave-Adresse zuerst das High-Byte und dann das Low-Byte der Adresse 
senden, auf welche Speicherstelle du zugreifen willst. -> see datasheet

Autor: Christoph M. (chrischu91)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan: hmm vielleicht liegt es wirklich daran, dass ich ein
         I2C1_Repeated_Start()
einfügen sollte, ich muss mir das noch einmal genau anschauen.

@Bastler: Ja, an das habe ich gedacht.

@Nilix: Achso danke, ja stimmt, da habe ich etwas falsch verstanden!

ich glaub nach zusätzlichen herumfragen in meiner Firma, bei 
verschiedensten personen, dass ich wohl etwas zu komplex war, und der 
RS232 Port gar nicht nachmag mit der programmierung, oder der fehler, 
der Nilix beschrieben hat, ich werde es morgen in der Firma dann checken 
können :)

Autor: Christoph M. (chrischu91)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es funktioniert auch nicht, aber ich versuche nun das Debugging menu zum 
laufen zu bringen, doch dies scheint viel schwieriger zu sein, als ich 
am anfang dachte :s
Ich soll ein Debugging bit setzen, da ich aber PicKit3 verwende scheint 
dies viel schwieriger zu sein...
kann mir da jemand helfen? ich sollte es mindestens mal debuggen können!
Weil die Funktion
  i2c_start();
führt es noch aus, aber bei
  i2c_write(0xA1);
bleibt es hängen.
Wenn man es mit dem oszilloskop misst, kommt bei einer Tastatureingabe 
auch kein Signal! Ich müsste also auch noch wissen wie man das tris_c 
register setzen sollte, damit ich das SCL und SDA richtig in betrieb 
nehmen kann, denn ich habe immer ein Low Pegel statt ein High Pegel, nur 
bei einem Reset geht es kurz auf den High Pegel!

Ich hoffe jemand kann mir helfen.
Werde jetzt aber für eine Woche in den Ferien sein, und es mir nachher 
anschauen!

Danke schon jetzt!
Gruss Christoph

Autor: Stephan S. (uxdx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du brauchst beim Lesen über I2C defintiv ein "repeated start", sonst 
kannst Du ja keinen Lesebefehl absetzen.

In Assembler sieht das beim 16F887 so aus (Teil einer Routine, die ich 
immer wieder verwende, z.B. für EEPROMs)

; lesen Adresse adr_l/adr_h im I2C-Device adr_e nach W

read_i2c
    call   i2c_on                 ; Bus start
    call   i2c_set                ; setze Adress-Pointer
    call   i2c_ron                ; Bus repeated start

    movfw   adr_e                 ; 1010 xxx1   I2C-Adresse für lesen
    iorlw   b'00000001'           ; letztes Bit 1 = read Data
    call    i2c_tx                ; sende I2C-Adresse
    call    i2c_rx                ; lesen Speicherzelle nach W

    call    i2c_off               ; Bus stop
    return

; setze Adresspointer auf adr_l/adr_h im I2C-Device adr_e

i2c_set

    movfw   adr_e                 ; 1010 xxx0   I2C-Adresse für schreiben
    andlw   b'11111110'           ; letztes Bit 0 = write Data
    call    i2c_tx                ; sende I2C-Adresse
    movfw   adr_h
    call    i2c_tx                ; sende interne Adresse HI
    movfw   adr_l
    call    i2c_tx                ; sende interne Adresse LO
    return

; I2C-Bus im Master-Mode übernehmen - START

i2c_on
    bcf     PIR1, SSPIF           ; SSPIF Bit zurücksetzen
    bank1
    bsf     SSPCON2, SEN          ; start condition
    bank0
    btfss   PIR1, SSPIF           ; Bus schon frei ?
    goto    $-1                   ; 
    bcf     PIR1, SSPIF           ; ja, Bus ist übernommen
    return

; I2C-Bus im Master-Mode übernehmen - repeated START

i2c_ron
    bcf     PIR1, SSPIF           ; SSPIF Bit zurücksetzen
    bank1
    bsf     SSPCON2, RSEN         ; repeated start condition
    bank0
    btfss   PIR1, SSPIF           ; Bus schon frei ?
    goto    $-1                   ; 
    bcf     PIR1, SSPIF           ; ja, Bus ist übernommen
    return

; I2C-Bus wieder freigeben - STOP

i2c_off
    bank1
    bcf     PIR1, SSPIF           ; SSPIF Bit zurücksetzen
    bsf     SSPCON2, PEN          ; Bus Freigabe anweisen
    bank0
    btfss   PIR1, SSPIF           ; Bus schon frei ?
    goto    $-1                   ; 
    bcf     PIR1, SSPIF           ; ja, Bus frei
    return
  
; ein Byte aus W auf I2C senden

i2c_tx
    movwf   SSPBUF                ; W auf I2C-Bus senden
    btfss   PIR1, SSPIF           ; ACK schon empfangen ?
    goto    $-1                   ; 
    bcf     PIR1, SSPIF           ; ja
    return

; ein Byte vom I2C empfangen

i2c_rx
    bank1
    bsf     SSPCON2, RCEN        ; Daten Empfang einschalten
    bank0
    btfss   PIR1, SSPIF          ; Daten empfangen ?
    goto    $-1                  ; 
    bcf     PIR1, SSPIF          ; ja
    movf    SSPBUF, W            ; empfangene Daten -> W
    return


Autor: Christoph M. (chrischu91)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Stefan: Danke für deine Rückmeldung, der Code geht trotzdem habe ich 
festgestellt ;), mit dem CCS C-Compiler ist dies möglich, dies habe ich 
nachdem ich den dofen Fehler festgestellt hatte trotzdem noch 
ausprobiert, und es funktionierte einwandfrei :D

Habe mir in den Ferien viel überlegt und auch ein paar mal den Code 
angeschaut, nun habe ich festgestellt, dass ich die 2 x I/O:
set_tris_c(0x98);  //  Eingang=1, Ausgang=0 (SCL & SDA sind auf dem C3 und C4 Register und müssen auf 1 gesetzt sein! so wie hier die korrigierte Version)

falsch gesetzt hatte, darum funktionierte auch nichts, ein dummer 
fehler, der mich viel Zeit gekostet hatte, aber ich danke euch trotzdem.

------

andere Frage:

jetzt habe ich aber noch so ein komischer Fehler festgestellt: Wenn ich 
jetzt den Code so anwende, kommt bei mir im Terminal das heraus, dass es 
immer nur jeder 4. Register im EEPROM beschreibt, wenn ich aber zwischen 
jedem Vorgang ein delay von 50ms hineinsetze, schreibt es jedes 
register. Dies trifft bei jeder Baudrate egal ob 115200 oder 9600 auf...
Könnte mir das jemand erklären? Ich verstehe das irgendwie nicht...

Danke
Gruss Christoph

Autor: Unbekannt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das liegt daran, weil du wahrscheinlich keine ausreichende 
Fehlerbehandlung in deinen Routinen implementiert hast.

I2C-EEPROMs kannst du byteweise oder auch bis zur nächsten Pagegrenze 
hin in einem Rutsch beschreiben. Ein I2C-EEPROM hat dafür einen internen 
Puffer, der genauso groß ist, wie die Pagegröße. Diese steigt mit der 
EEPROM-Speichergröße an.

WIchtig ist hier aber, dass (siehe Datenblatt!) ein I2C-EEPROM 
(typischerweise) bis maximal 10ms für den internen Schreibvorgang 
braucht.
Das sieht dann SO aus, dass nach der Stop-Sequenz das EEPROM anfängt, 
die Daten aus dem Puffer in die EEPROM-Zellen zu schreiben.

Wichtig: In diesem Zustand meldet sich das EEPROM auf keine Zugriffe 
mehr. Es scheint, als wäre es nicht am Bus! Man kann daher nicht sagen, 
ob überhaupt kein EEPROM am Bus ist oder die falsche I2C-Slave-Adresse 
genutzt wurde oder das EEPROM gerade während des internen 
Schreibvorgangs ist.

Nutzt man ausreichende Fehlerrückgabewerte in seinen Routinen, die die 
entsprechenden Register des I2C-Moduls abfragen, dann wirst du 
wahrscheinlich feststellen, dass du das erste Byte fehlerfrei sendest, 
Bytes 2 und 3 aber mit einem Fehler quittiert werden = EEPROM nicht 
vorhanden/erreichbar. Byte 4 wird wieder einwandfrei geschrieben, da das 
EEPROM in der Zwischenzeit mit dem Schreiben fertig war.

Schreibst du z.B. eine ganze Page auf einmal, ist diese Zeit nach dem 
Stop-Kommando noch länger, als bei einem Byte.

Jetzt gibt es zwei allgemeine Ansätze, das in der Software zu 
bewerkstelligen:

1.) Nach einem Stop-Kommando triggert man das EEPROM einfach nur mit 
seiner Slave-Adresse an, bis es sich wieder meldet, sendet dann noch ein 
STOP hinterher und beendet dann die Schreibroutine. Das EEPROM ist dann 
auf jeden Fall beim nächsten Zugriff wieder bereit. Nachteil: Man wartet 
aktiv, was viel Zeit kostet (bis zu 10ms!). Hier muss auf jeden Fall ein 
Software-Timer genutzt werden, der dich aus der Schleife rettet, falls 
z.B. gar kein EEPROM verbaut ist oder das EEPROM defekt ist!

2.) Man schiebt den Timeout beim Schreiben hinein. Man versucht also 
z.B. mindestens 10ms das EEPROM zu erreichen, bis man (Software-Timer!) 
einen Fehler á la "EEPROM nicht erreichbar!" ausgibt. Vorteil: Man 
musste siehe 1. vorher nicht warten. Außerdem wird man durch 
anderweitige Softwareroutinen sowieso höchstwahrscheinlich zwischen den 
Schreibzugriffen unterbrochen. Dann ist die Gesamtperformance in dieser 
Reihenfolge besser. Bei 1. muss ich halt immer x ms warten, bei 2. kann 
es ja schon sein, dass ich durch meine eigenen Routinen "auch mal was 
anderes zu tun habe". Bei 2. warte ich also statistisch immer weniger, 
als bei 1.

Vorteilhaft ist hier ein Oszilloskop mit I2C-Analyser. Ich habe selbst 
mal stundenlang nach einem Fehler gesucht. Den habe ich dann mit dem 
passenden Oszilloskop in sehr kurzer Zeit gefunden. Ein 
Speicheroszilloskop tuts auch, ist aber unkomfortabler.

Autor: Christoph M. (chrischu91)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich danke dir herzlich, für deine Bemühungen :)
Hat mir sehr viel gebracht, jetzt verstehe ich es endlich ;)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.