25LC512 SPI EEPROM seltsames Verhalten / defekt?

OP #7742685
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
}
#7742695
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;
#7742701
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
}
OP #7742811
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.

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

OP #7742816
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. 😉

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

#7742844
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?"

Angehängte Dateien:
#7743170
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?).

OP #7743645
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?

(Firma: Jasinski) #7743652
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.

Angehängte Dateien:
OP #7743973
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!

(Firma: Jasinski) #7747190
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.

Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren