Forum: Mikrocontroller und Digitale Elektronik 25LC512 SPI EEPROM seltsames Verhalten / defekt?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Kai (kai_tl91)


Lesenswert?

Hallo zusammen,

ich bin seit Tagen daran ein 25LCxxx EEPROM am SPI zum laufen zu 
bekommen.

Ich bin sogar soweit, dass ich den Rest vom SPI Bus (der funktioniert) 
abgetrennt habe und bereits eine RC Terminierung mit 100Ohm und 22pC am 
SCL, sowie einen 100nF Abblockkondensator zwischen Vcc und Gnd 
nachgerüstet habe. Allerdings gibt das EEPROM immernoch zufällige Wert 
zurück, bzw. es wird irgendwas empfangen.

Der Chip ist ein AVR ATmega32A, CPU Takt: 11,0592MHz. Meine Vermutung 
ist, dass die SPI nicht richtig eingestellt ist. Aber ich habe schon 
vieles probiert und auch die Outputs des ATmega im Masterbetrieb über 
die UART ausgeben lassen. Soweit sieht alles korrekt aus. Die Befehle 
werden alle laut Datenblatt korrekt gesendet.

Irgendwas muss ich übersehen oder der EEPROM ist defekt!? Irgendwo hatte 
ich auch mal gelesen, dass beim Masterbetrieb die Reihenfolgen der SPI 
Befehle und das Setzen der Ausgänge entscheidet wäre. Allerdings 
funktioniert das Senden derzeit ohne Probleme und empfangen wird auch 
etwas - aber nichts brauchbares :(

Ich würde mich über Hilfe sehr freuen. Grüße Kai

1
#define dev25LC512_PIN_SELECT PINB4   //Chip Select PIN
2
3
#define dev25LC512_PORT      PORTB   //Portregister für Zustand (low/hi)
4
#define dev25LC512_DDR       DDRB    //Datenrichtungsregister DDRx (DDRA, DDRB, ...)
5
#define dev25LC512_PIN_CLOCK PINB7   //IC Clock
6
7
#define dev25LC512_Twc      5       //Internal write cycle time (wartezeit)
8
#define dev25LC512_READ    0x03   //CMD: Read
9
#define dev25LC512_WRITE  0x02   //CMD: Write
10
#define dev25LC512_WREN    0x06   //CMD: Write enable
11
#define dev25LC512_WRDI    0x04   //CMD: Write disable
12
#define dev25LC512_RDSR    0x05   //CMD: Read Satutsbyte
13
#define dev25LC512_WRSR    0x01   //CMD: Write Statusbyte
14
#define dev25LC512_PE    0x42   //CMD: Page erase
15
#define dev25LC512_SE    0x6C   //CMD: Sector erase
16
#define dev25LC512_CE    0xC7   //CMD: Chip erase
17
#define dev25LC512_RDID    0xAB   //CMD: READ ELECTRONIC SIGNATURE
18
#define dev25LC512_DPD    0xB9   //CMD: Deep Power Mode
19
20
#define dev25LC512_REG_WIP (1)      //Command complet Bit (0)
21
#define dev25LC512_REG_WEL (1<<1)
22
#define dev25LC512_REG_BP0 (1<<3)
23
#define dev25LC512_REG_BP1 (1<<4)
24
25
dev_25LC512_spi_onlydev(void)
26
{ 
27
  uint8_t tmp, tmp2;
28
  tmp = SREG;
29
  cli();
30
  dev_25LC512_SPI_di(); //SS Pin auf high (nicht selectiert!)
31
  dev25LC512_DDR |= (1<<PINB5); //Set MOSI output
32
  dev25LC512_DDR |= (1<<dev25LC512_PIN_CLOCK); //Set SCK output
33
  
34
  //PULL SCK (Clock) of HIGH
35
  dev25LC512_PORT |= (1<<dev25LC512_PIN_CLOCK);
36
  
37
  dev25LC512_DDR &= ~(1<<PINB6); //Set MISO input
38
  dev25LC512_DDR |= (1<<dev25LC512_PIN_SELECT); //SET SS output  
39
  
40
  
41
  SPCR = 0x00;
42
  SPCR &= ~(1<<DORD); //MSB zuerst
43
  SPCR |=  (1<<SPE) | (1<<MSTR); //Aktivieren / Master setzen
44
  SPCR &= ~(1<<CPOL); //ClockSignal - mode 0
45
  SPCR |= (1<<CPHA); //ClockSignal
46
  //SPCR |= (1<<SPR0); //F_CPU / 16
47
  //SPSR = (1<<SPI2X); //double speed
48
49
  _delay_ms(1);
50
  tmp2 = SPDR; //Dummy Readout
51
  
52
  //SREG = tmp;
53
  //uart_write("SPI Byte: 0x%2x\r\n", SPCR);
54
55
  dev_25LC512_SPI_di();
56
  
57
  dev_25LC512_byte_read(0x0000); //Dummy auslesen
58
  dev_25LC512_READ_chipid();
59
  SREG = tmp;
60
}
61
62
static void dev_25LC512_SPI_TRANS(uint8_t data)
63
{
64
  #ifdef _dev_23LCxxx_softSPI
65
  dev_25LC512_SPI_shiftbits(data);
66
  #else
67
  SPDR = data;
68
  while (!(SPSR & (1<<SPIF))); //Warten bis Byte gesendet
69
  #endif
70
}
71
72
static uint8_t dev_25LC512_SPI_REC(void)
73
{
74
  while (!(SPSR & (1<<SPIF))); //Warten bis Byte empfangen
75
  return SPDR;  
76
}
77
78
static void dev_25LC512_SPI_send(uint8_t byte) //Ein Byte übertragen
79
{
80
  dev_25LC512_SPI_en();
81
  dev_25LC512_SPI_TRANS(byte);
82
  dev_25LC512_SPI_di();
83
}
84
85
uint8_t dev_25LC512_READ_chipid(void) //Auslesen der Chip ID
86
{  uint8_t recdata;
87
  dev_25LC512_SPI_en();
88
  dev_25LC512_SPI_TRANS(dev25LC512_RDID);
89
  dev_25LC512_SPI_TRANS((uint8_t) 0x00); //Dummy Adresse HB
90
  dev_25LC512_SPI_TRANS((uint8_t) 0xA0); //Dummy Adresse LB
91
  recdata = dev_25LC512_SPI_REC();
92
  dev_25LC512_SPI_di();
93
  return recdata;
94
}

von Falk B. (falk)


Lesenswert?

Kai schrieb:
> static uint8_t dev_25LC512_SPI_REC(void)
> {
>   while (!(SPSR & (1<<SPIF))); //Warten bis Byte empfangen
>   return SPDR;
> }

Da fehlt das Senden von Daten. Das muss man auch bei Lesen tun, denn SPI 
sendet und empfängt immer gleichzeitig.
1
   SPDR = 0x00;
2
   while (!(SPSR & (1<<SPIF))); //Warten bis Byte empfangen
3
   return SPDR;

von Hannes (taurus16)


Lesenswert?

Du kannst die gleiche Funktion für senden und lesen nutzen.

Wenn du lesen willst, übergibst du halt ne 0 und wenn du nur senden 
willst ignorierst du den Rückgabewert.
1
uint8_t spi_8bit(uint8_t data){
2
   SPDR = data;
3
   while (!(SPSR & (1<<SPIF))); //Warten bis Byte empfangen
4
   return SPDR;
5
}

von Kai (kai_tl91)


Lesenswert?

Ok, verstehe! Vielen Dank!

Aber mir kommt da gerade die Frage: Wieso bekomme ich irgendwelche 
zufälligen Werte ausgegeben? Also wenn der Empfang nicht klappt sollte 
der Return doch immer NULL sein oder das letzte Byte was ich gesendet 
habe?

Ich möchte nur verstehen wie die Werte zustande kommen, um 
Hardware-seitig irgendwelche weitere Fehler auszuschließen.

von Falk B. (falk)


Lesenswert?

Kai schrieb:
> Aber mir kommt da gerade die Frage: Wieso bekomme ich irgendwelche
> zufälligen Werte ausgegeben? Also wenn der Empfang nicht klappt sollte
> der Return doch immer NULL sein oder das letzte Byte was ich gesendet
> habe?

Nein, das letzte empfangene Byte. Es gibt aber Situationen, da sendet 
der EEPROM nix beim Schreibzugriff. MISO hängt dann in der Luft und 
erzeugt Zufallsdaten, die dein AVR empfängt. Um das zu verhindern, 
sollte man MISO mit 10k Pull Up versehen. Dann empfängt man sicher nur 
0xFF.

Deine fehlerhaft Empfangsroutine macht keinen Lesezugriff sondern 
liefert das zuvor zuletzt empfangene Byte.

von Kai (kai_tl91)


Lesenswert?

By the way: Mein Code hat bei einem ATmega644P funktioniert. Auf diesen 
Chip hatte ich ihn entwickelt. Interessant, dass der ATmega32A wohl vor 
dem Empfang ein Schreiben des SPI Data Register benötigt. Der ATmega644P 
wohl nicht. Da hat das EEPROM tadellos gespielt. 😉

von Walter T. (nicolas)


Lesenswert?

Falk B. schrieb:
> Es gibt aber Situationen, da sendet
> der EEPROM nix beim Schreibzugriff.

Woran am Datenblatt machst Du das fest? Für mich sieht es so aus, als 
sei High-Z an SO nur möglich, wenn CS inaktiv ist.

von Kai (kai_tl91)


Lesenswert?

Sicher, das man an SO , MISO einen Pullup von 10kO anschließen sollte, 
wenn mehrere Slaves an dieser Leitung hängen?

von Falk B. (falk)


Lesenswert?

Kai schrieb:
> By the way: Mein Code hat bei einem ATmega644P funktioniert. Auf diesen
> Chip hatte ich ihn entwickelt. Interessant, dass der ATmega32A wohl vor
> dem Empfang ein Schreiben des SPI Data Register benötigt. Der ATmega644P
> wohl nicht.

Ganz sicher nicht. Auch der braucht das.

> Da hat das EEPROM tadellos gespielt. 😉

Klingt nach Zufall.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Walter T. schrieb:
>> Es gibt aber Situationen, da sendet
>> der EEPROM nix beim Schreibzugriff.
>
> Woran am Datenblatt machst Du das fest? Für mich sieht es so aus, als
> sei High-Z an SO nur möglich, wenn CS inaktiv ist.

Siehe Anhang.

Kai schrieb:
> Sicher, das man an SO , MISO einen Pullup von 10kO anschließen sollte,
> wenn mehrere Slaves an dieser Leitung hängen?

Muss man nicht, man muss nur wissen welche Empfangsdaten man ignorieren 
muss. Bzw. eine wirklich funktionierende SPI-Transferfunktion benutzen, 
wie sie hier gezeigt wird. Das ist Standard.

Beitrag "Re: 25LC512 SPI EEPROM seltsames Verhalten / defekt?"

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Falk B. schrieb:
> Siehe Anhang.

Danke, ich hatte in die falschen Diagramme geschaut.

von Kai (kai_tl91)


Lesenswert?

Falk B. schrieb:
>> Da hat das EEPROM tadellos gespielt. 😉
>
> Klingt nach Zufall.

Kann sein...

Aber an dieser Stelle erstmal vielen Dank für die Hilfe! Manchmal sieht 
man vor lauter programmieren, die einfachsten Fehler nicht. 😉

von Wastl (hartundweichware)


Lesenswert?

Kai schrieb:
> Manchmal sieht
> man vor lauter programmieren, die einfachsten Fehler nicht.

Meistens liegt der Fehler am Bauteil, weil es aus China kommt
und wahrscheinlich ein Fake bzw. ein schlecht nachgebautes Teil
ist. Oder der deutsche Lieferant liefert einfach "schlechte"
Ware aus. Oder der Hersteller des Bauteils liefert noch Beta-
Ware aus in der noch nicht alle Fehler ausgemerzt sind, und man
hat soeben einen solchen Fehler gefunden.

Am Layer 8 liegt es eigentlich nie, das sagt schon der Thread-
Titel (SPI EEPROM seltsames Verhalten / defekt?).

von Kai (kai_tl91)


Lesenswert?

Wastl schrieb:
> Am Layer 8 liegt es eigentlich nie, das sagt schon der Thread-
> Titel (SPI EEPROM seltsames Verhalten / defekt?).

Vielen dank für die Ironie!

von Kai (kai_tl91)


Lesenswert?

Hannes schrieb:
> Du kannst die gleiche Funktion für senden und lesen nutzen.
>
> Wenn du lesen willst, übergibst du halt ne 0 und wenn du nur senden
> willst ignorierst du den Rückgabewert.
> uint8_t spi_8bit(uint8_t data){
>    SPDR = data;
>    while (!(SPSR & (1<<SPIF))); //Warten bis Byte empfangen
>    return SPDR;
> }

Ich habe die Funktion angepasst. AAAber... leider werden immer noch 
zufällige Werte auslesen. Stimmt doch etwas mit spi_init() nicht? Jemand 
noch eine Idee?

von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Angehängte Dateien:

Lesenswert?

Kai schrieb:
> Ich habe die Funktion angepasst. AAAber... leider werden immer noch
> zufällige Werte auslesen. Stimmt doch etwas mit spi_init() nicht? Jemand
> noch eine Idee?

Deinem Code im Anfangsbeitrag nach wird das SPI-Interface mit CPHA=1 
benutzt – hast Du es schon mal mit CPHA=0 probiert? Eigentlich müsste 
man Mode=0,0 sagen, das heißt CPOL und CPHA müssten dann beide auf 0 
gesetzt sein und das wird bei den meisten ICs auch so eingestellt (im 
Anhang meine Einstellungen für MAX7219). Clock ist dann im Ruhezustand 
Low und getaktet wird mit der ersten (steigenden) Flanke, mit Deinem 
CPHA=1 nimmst Du momentan die fallende Flanke, was die falschen Werte 
erklären könnte.

___
Nur so nebenbei: man kann die ganzen Einstellungen auch in einer Zeile 
machen – so wie Du das hast, wird das Interface eingeschaltet und dann 
umgestellt. Das macht man eigentlich umgekehrt – man stellt alles ein 
und schaltet dann das Schnittstelle ein (SPE=1). Ich erwähne das wie 
gesagt nur vollständigkeitshalber wie man es richtig machen sollte bzw. 
es von Anfang an richtig lernen sollte, denn mit Deinem Fehler hat das 
nichts zu tun.

: Bearbeitet durch User
von Kai (kai_tl91)


Lesenswert?

Gregor J. schrieb:
> das SPI-Interface mit CPHA=1
> benutzt – hast Du es schon mal mit CPHA=0 probiert? Eigentlich müsste
> man Mode=0,0 sagen, das heißt CPOL und CPHA müssten dann beide auf 0
> gesetzt sein und das wird bei den meisten ICs auch so eingestellt

VIELEN DANK FÜR DIE HILFE!! => GENAU DAS war der Fehler! Mit steigender 
Flanke funktioniert es! DANKE!

von Gregor J. (Firma: Jasinski) (gregor_jasinski)


Angehängte Dateien:

Lesenswert?

Kai schrieb:
> VIELEN DANK FÜR DIE HILFE!! => GENAU DAS war der Fehler! Mit steigender
> Flanke funktioniert es! DANKE!

Sehr gerne. Wenn wir jetzt mal kurz die Sendeproblematik ausblenden, 
dann war das eine kleine Ursache (ein Bit falsch gesetzt) mit großer 
Wirkung, denn es läuft dann quasi gar nix mehr, der µC sendet und 
sampelt immer zu falschen Zeit, genau dann, wenn das EEPROM Datenwechsel 
auf der Leitung macht. Mode_1,1 müsste jetzt aber auch gehen, für diesen 
Test müssten aber diese beiden Konfigurationspins auf 1 gesetzt werden, 
Standard ist aber – wie ich bereits schrieb – Mode_0,0 und den wollen 
die meisten ICs. Solche Fehler vergisst man lebenlang nicht mehr.

Von meiner Seite auch noch Danke, denn durch Deinen Thread bin ich auf 
den Speicher gestossen, der einen Kompromiss zwischen den 24ern mit I²C, 
wovon ich kein Fan bin, und den 25ern mit SPI bildet – bei den großen 
SPI-Speichern gibt es keine einfachen Bytezugriffe mehr, man muss vorher 
immer einen kleinen Block an Bytes löschen, um anschließend z.B. ein 
Byte zu schreiben. Ich habe mir allerdings den M95256 und M95512 von ST 
ausgesucht – die aktuell hergestellten Speicher sind nämlich nicht mehr 
mit 100 Tausend, sondern mit 4 Millionen Schreibzugriffen spezifiziert, 
was meinen Algorithmus zur Lebensdauerverlängerung eines Flashspeicher 
überflüssig macht, d.h. programmtechnisch vereinfacht sich alles 
radikal, bei 64 Kilobyte kann man aber diesen natürlich immer noch 
einsetzen, um noch mehr Schreibzugriffe zu erhalten, falls das nötig 
ist. Chinesische, günstige Alternativen habe ich mir jetzt auch 
testweise besorgt – ob sie wirklich auch so gut sind, wird sich noch 
zeigen. FRAM mit 10 hoch 14 Zugriffen habe ich mir auch testweise 
bestellt (basiert auf ferroelektrischem Effekt) – für meine Roboter sind 
diese aber noch zu teuer bzw. zu teuer bei größeren Kapazitäten, was 
sich in der Zukunft hoffentlich bald nach und nach ändern wird.

: Bearbeitet durch User
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.