Hallo
ich habe einen 24LC32A EEPROM, den ich mit einem 16f876a Microkontroller
beschreiben und auslesen will, allerdings will das einfach nicht
funktionieren.
hier ist der Code den ich bei jetzt habe:
1
staticunsignedcharSchreibwert,Lesewert;
2
staticbitack=0;
3
staticinti=0;
4
5
Schreibwert=0;
6
InitI2C();
7
8
while(1){
9
LcdClr();
10
LcdPos(0,0);
11
LcdChar("Schreibwert:",12);
12
LcdNumber(Schreibwert);
13
__delay_ms(1000);
14
15
i2c_WriteTo(0xA8);
16
i2c_SendByte(0x00);//Adresse Highbyte
17
i2c_SendByte(0x00);//Adresse Lowbyte
18
i2c_SendByte(Schreibwert);
19
i2c_Stop();
20
21
__delay_ms(50);
22
23
i2c_WriteTo(0xA8);
24
i2c_SendByte(0x00);//Adresse Highbyte
25
i2c_SendByte(0x00);//Adresse Lowbyte
26
i2c_ReadFrom(0xA8);
27
Lesewert=i2c_GetByte(I2C_LAST);
28
i2c_Stop();
29
30
LcdPos(0,1);
31
LcdChar("Lesewert:",9);
32
LcdNumber(Lesewert);
33
__delay_ms(500);
34
35
Schreibwert++;
36
}
37
}
und als Initialisierung nutze ich:
1
voidInitI2C(void){
2
SSPADD=9;
3
SSPCON=0b00101000;
4
SSPSTAT=0b10100000;
5
SSPCON2=0;
6
}
Auf dem Display wird als Lesewert immer 255 ausgegeben.
Ich hoffe ihr könnt mir helfen.
Tom
Tom R. schrieb:> i2c_WriteTo(0xA8);> i2c_ReadFrom(0xA8);
Senden diese Funktionen das Startbit mit?
Setzt die i2c_ReadFrom(0xA8) das LSb der I2C-Adresse automatisch auf
'1'?
Auf welchen Pegeln sind die Pins A<2:0>?
Der Code
i2c_WriteTo(address)
i2c_WriteTo(address)
stammt von der HI-TECH compiler Software, wo dies als sample zu finden
ist
an sich macht das folgendes:
1
i2c_Start();
2
i2c_SendByte(address|(rw?1:0))//0 für write, 1 für read, adresse = 0xA8
3
if(i2c_ReadAcknowledge())
4
returnTRUE;
5
returnFALSE;
Also Startbit wird gesendet und das Lsb wird entsprechend gesetzt.
und PORTA wird ziemlich weit am Anfang auf 0 gesetzt.
aber was hat das mit dem i2c-Bus zu tun?
Dir fehlt vermutlich eine Stop condition:
i2c_WriteTo(0xA8);
i2c_SendByte(0x00); //Adresse Highbyte
i2c_SendByte(0x00); //Adresse Lowbyte
i2c_Stop(); // <<<--- diese hier
__delay_ms(50); // <<<--- zur Sicherheit
i2c_ReadFrom(0xA8); // <<<- ReadFrom erzeugt vermutlich als erstes
eine Start-Condition bevor die Addresse gesendet wird
Lesewert = i2c_GetByte(I2C_LAST);
i2c_Stop();
Ansonsten würde ich empfehlen dir bei i2c_WriteTo() und i2c_ReadFrom()
anzugucken, ob das EEProm jeweils ACKed.
Vergiss das, was ich eben schrieb. Du brauchts hier keine stop
condition.
Mir fallen 3 Gründe ein:
a) Adresse ist falsch (siehst du daran, dass schon die Adresse bei
deinem Schreibzugriff nicht ge-ackt wird).
b) EEProm ist schreibgeschützt (WP pin)
c) Die Zeit zwischen Schreiben und Lesen ist zu kurz bemessen.
Eigentlich solltest du hier ein polling implementieren. Solange das
EEProm seine Adresse nicht acked, ist es noch mit dem Wegschreiben
deiner Daten beschäftigt.
Testweise kannst du hier ja einfach mal länger warten (1/2 Sekunde z.B)
Kleiner Tip:
Organisier Dir einen Logic-Analyzer und schau damit mal was auf der
"LowLevel" vom und zum EEPROM an Daten läuft.
Ein Teil, das ich auch in der Firma einsetzte ist der hier
"www.saleae.com/ -> LOGIC8" (Kostet ca. 150..200 Euro)
Damit hab ich schon einige Probleme mit Schnittstellen I2C, SPI,...
analysiert und festgestellt, wo ich in meinem Code Murks gebaut hab,
oder ob mir da irgendwas anderes in die Suppe spuckt.
Hab mir auch schon ein eigenes Plugin für eine etwas unhandliche
Schnittstelle geschrieben. Das Teil ist was den Preis und die Funktionen
angeht echt spitze.
Ein Oszi geht zwar auch, aber das raus-picken der Bits ist sehr mühsam.
Einige Probleme bei I2C können sein:
1.
Adresse des Slaves, bzw, wie die angegeben wird. Da ich aber weder deine
Schaltung kenne, noch die Doku zu den verwendeten Funktionen, kann ich
da keine konkretere Aussage machen. Manche I2C Funktionen erwarten die
Slave-Adresse als 1:1 Wert, wie bei deinem Codebeispiel als 0xA8. Andere
würde das ein Bit nach rechts geschoben erwarten, also 0x54 (als 7-Bit
Adresse, bei der das R/W-Bit nicht einbezogen ist).
2.
Hardwareprobleme: Bus-Idle Pegel nicht vorhanden - Pullups fehlen,
zu viel Kapazität auf den Busleitungen für die Geschwindigkeit
(sieht man nicht im Logic-Analyzer, aber mit einem Oszi)
Leider weiss ich nicht, ob die Hi-Tech Routinen Bit-Banging oder
I2C-Hardware verwenden. Allerdings gehe ich in so einem Fall immer so
vor, dass ich erst die Hardware prüfe (Oszi -> Stimmen die Bus-Pegel und
tut sich was, LogikAnalyzer -> Was laufen für Daten). Im Analyzer sieht
man dann recht schnell, ob man z.B. die korrekte Adresse hat und der
SLave antwortet.
Wenn die Obige I2C-Read-Routine z.B. eine Bit-Banging Routine wäre, dann
kann da schon 0xFF rauskommen, wenn der Slave nicht antwortet.
Dummerweise ist auch der Lieferzustand der EEPROM-Zellen 0xFF, so dass
man hier keine Unterscheidung machen kann.
Wenn die Funktionen einen Rückgabewert haben, z.B. ob vom SLave ein ACK
kam, oder nicht, dann empfehle ich Dir den auch auszuwerten!
Ich hoffe das hilft ein wenig weiter.
Misst noch was vergessen:
@Easylife: Laut Datenblatt dauert ein WriteCycle 5ms.
Er hat 50 drin. Wenn das EEPROM nach seinen 50ms noch nicht fertig ist,
dann wäre es reif für die Tonne ;-)
Außerdem sollte ein Faktor 2, also max. 10ms völlig ausreichen.
Mir ist bisher auch noch kein I2C-EEPROM untergekommen, da nicht nach
spätesten 10ms mit schreiben fertig ist (laut Datenblatt).
Danke erstmal für die Ideen.
@ Matthias ... ich bevorzuge lieber das was ich schon habe zu nutzen,
ein gutes altes Speicheroszi muss dafür also reichen.
Also erstmal zum Schaltungsaufbau was: Der uC ist auf dem Board aus dem
Buch "Mikrocontroller für Einsteiger" verbaut. Schaltungstechnisch mache
ich mir also keine Sorgen. In dem Buch gibt es zwar zahlreiche Beispiele
zu dem Board, allerdings sträube ich mich dagegen etwas mit Assembler zu
programmieren. (Ich habe also bereits ein funktionierendes Programm zum
Lesen und Schreiben des EEPROM's, allerdings in Assembler - Anhang
~Messwertspeicherung).
Die Adresse für den Speicher habe ich aus diesem Programm entnommen.
ABER
Ich bin mal mit nen Oszi dran gehangen und da ist bei meinem Programm
gar nichts auf SDA oder SDL zu sehen.
Also scheint das Problem schon beim "InitI2C()" zu liegen?!
@Easylife
wie gesagt ... Schaltungstechnisch mache ich mir keine Sorgen. (gar
nichts sollte High-Pegel heißen).
Inzwischen funktioniert das Ganze sogar (aber mit einem komplett anderen
/ neuen Code) nur brauche ich nochmal für eine scheinbar simple Sache
eure Hilfe:
Ich möchte, wenn ich mehrere Bytes auf einmal auslese ein Array von
einer Funktion zurückbekommen. Theoretisch scheint mir das recht
einfach, aber praktisch will der Kompiler das nicht so wie ich will.
und die Fehlermeldung lautet:
"Error [202] ... 45.51 nur 'lvalues' darf etwas zugewiesen oder dürfen
geändert werden"
hat jemand eine Idee wie es besser geht?
1. Du hast in der Funktion readEEPROMchar() ein array data[] ohne
Größenangabe deklariert.
2. Der Rückgabewert ist ein Pointer auf dieses Array (auf Element 0).
Da das Array aber lokal in der Funktion existiert, ist der Pointer nach
Beendigung von readEEPROMchar() nicht mehr gültig.
Du könntest data[] entweder global deklarieren (unschön), oder in der
aufrufenden Funktion, und einen Pointer auf dieses Array an
readEEPROMchar() übergeben (so macht man das üblicherweise in C).
Also in etwa so:
int readEEPROMchar (unsigned char *data, char adr, int sadr, int amount)
{
// (...)
for (int i = 0; i < amount; i++) {
data[i] = I2CRead(); // store byte in buffer
if (i == (amount-1)) I2CAck();
else I2CNak();
}
// (...)
return 1; // bzw eben eine Information, ob der Read-Zugriff
erfolgreich war
}
#define MAX_I2C_READ_SIZE (100) // an die individuellen Gegebenheiten
anpassen
main()
{
unsigned char i2c_read_buf[MAX_I2C_READ_SIZE];
int result;
result = readEEPROMchar (i2c_read_buf, 0xA8, 0x0000, 4);
}
Sollte dein Compiler die Zeile
data[i] = I2CRead(); // store byte in buffer
nicht fressen, versuche es mit stattdessen mit den folgenden 2 Zeilen:
*data = I2CRead(); // store byte in buffer
data++; // increment pointer
Das 1. Argument (i2c_read_buf) beim Aufruf von readEEPROMchar ist also
in Wirklichkeit die Adresse des ersten Elementes des Arrays.
Kann man auch so schreiben:
&i2c_read_buf[0]