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
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
@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.
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()
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
@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 :)
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
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)
1 | ; lesen Adresse adr_l/adr_h im I2C-Device adr_e nach W |
2 | |
3 | read_i2c |
4 | call i2c_on ; Bus start |
5 | call i2c_set ; setze Adress-Pointer |
6 | call i2c_ron ; Bus repeated start |
7 | |
8 | movfw adr_e ; 1010 xxx1 I2C-Adresse für lesen |
9 | iorlw b'00000001' ; letztes Bit 1 = read Data |
10 | call i2c_tx ; sende I2C-Adresse |
11 | call i2c_rx ; lesen Speicherzelle nach W |
12 | |
13 | call i2c_off ; Bus stop |
14 | return |
15 | |
16 | ; setze Adresspointer auf adr_l/adr_h im I2C-Device adr_e |
17 | |
18 | i2c_set |
19 | |
20 | movfw adr_e ; 1010 xxx0 I2C-Adresse für schreiben |
21 | andlw b'11111110' ; letztes Bit 0 = write Data |
22 | call i2c_tx ; sende I2C-Adresse |
23 | movfw adr_h |
24 | call i2c_tx ; sende interne Adresse HI |
25 | movfw adr_l |
26 | call i2c_tx ; sende interne Adresse LO |
27 | return |
28 | |
29 | ; I2C-Bus im Master-Mode übernehmen - START |
30 | |
31 | i2c_on |
32 | bcf PIR1, SSPIF ; SSPIF Bit zurücksetzen |
33 | bank1 |
34 | bsf SSPCON2, SEN ; start condition |
35 | bank0 |
36 | btfss PIR1, SSPIF ; Bus schon frei ? |
37 | goto $-1 ; |
38 | bcf PIR1, SSPIF ; ja, Bus ist übernommen |
39 | return |
40 | |
41 | ; I2C-Bus im Master-Mode übernehmen - repeated START |
42 | |
43 | i2c_ron |
44 | bcf PIR1, SSPIF ; SSPIF Bit zurücksetzen |
45 | bank1 |
46 | bsf SSPCON2, RSEN ; repeated start condition |
47 | bank0 |
48 | btfss PIR1, SSPIF ; Bus schon frei ? |
49 | goto $-1 ; |
50 | bcf PIR1, SSPIF ; ja, Bus ist übernommen |
51 | return |
52 | |
53 | ; I2C-Bus wieder freigeben - STOP |
54 | |
55 | i2c_off |
56 | bank1 |
57 | bcf PIR1, SSPIF ; SSPIF Bit zurücksetzen |
58 | bsf SSPCON2, PEN ; Bus Freigabe anweisen |
59 | bank0 |
60 | btfss PIR1, SSPIF ; Bus schon frei ? |
61 | goto $-1 ; |
62 | bcf PIR1, SSPIF ; ja, Bus frei |
63 | return |
64 | |
65 | ; ein Byte aus W auf I2C senden |
66 | |
67 | i2c_tx |
68 | movwf SSPBUF ; W auf I2C-Bus senden |
69 | btfss PIR1, SSPIF ; ACK schon empfangen ? |
70 | goto $-1 ; |
71 | bcf PIR1, SSPIF ; ja |
72 | return |
73 | |
74 | ; ein Byte vom I2C empfangen |
75 | |
76 | i2c_rx |
77 | bank1 |
78 | bsf SSPCON2, RCEN ; Daten Empfang einschalten |
79 | bank0 |
80 | btfss PIR1, SSPIF ; Daten empfangen ? |
81 | goto $-1 ; |
82 | bcf PIR1, SSPIF ; ja |
83 | movf SSPBUF, W ; empfangene Daten -> W |
84 | return |
@ 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:
1 | 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
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.
Ich danke dir herzlich, für deine Bemühungen :) Hat mir sehr viel gebracht, jetzt verstehe ich es endlich ;)
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.