Forum: Mikrocontroller und Digitale Elektronik [STM32] CMD0 liefert immer 0xFF zurück


von bbernhard1 (Gast)


Lesenswert?

Hallo,

ich versuche gerade eine SD-Karte mit CMD0 zu initialisieren, aber 
irgendwie klappt das nicht so ganz. So wie ich das verstanden habe, 
sollte die SD-Karte auf CMD0 mit 0x01 antworten, doch ich bekomme immer 
0xFF zurück.

Mein Sourcecode sieht so aus:
1
void spi_init(){
2
3
   //CS = PB12 (General Purpose Output)
4
   //CLK = PB13 (Alternative Function, Push Pull)
5
   //SDI(MISO) = PB15 (Input Pull-Up)
6
   //SDO(MOSI) = PB14 (Alternative Function, Push Pull)
7
8
   RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //GPIOB clock enable
9
   GPIOB->CRH &= ~0xFFFF0000;    //clear GPIOB configuration bits
10
   GPIOB->CRH |= 0x8BB30000; //set GPIOB configuration
11
12
   RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;   //SPI2 clock enable
13
   SPI2->CR1 &= ~SPI_CR1_SPE;  //SPI2 disable
14
   SPI2->CR1 |= SPI_CR1_CRCEN; //enable hardware CRC calculation
15
   SPI2->CR1 &= ~SPI_CR1_BIDIMODE; //fullduplex data mode enable
16
   SPI2->CR1 &= ~SPI_CR1_DFF; //data frame format = 8bit
17
   SPI2->CR1 &= ~SPI_CR1_SSM; //disable software slave management
18
   
19
   SPI2->CR1 |= 0x0038;  //fpclk /256
20
   SPI2->CR1 &= ~SPI_CR1_LSBFIRST; //MSB transmitted first
21
22
   SPI2->CR1 |= SPI_CR1_MSTR; //configure as master
23
   SPI2->CR1 &= ~SPI_CR1_CPOL; //CPOL = 0
24
   SPI2->CR1 &= ~SPI_CR1_CPHA; //CPHA = 0
25
26
   GPIOB->BSRR |= GPIO_BSRR_BS12; //set pin PB12
27
28
   SPI2->CR1 |= SPI_CR1_SPE;  //SPI2 enable
29
30
}
1
void spi_send(char data){
2
  SPI2->DR = data;
3
  while (!(SPI2->SR & SPI_SR_TXE)); //SPI2 Buffer empty?
4
}
1
unsigned char spi_receive(){
2
  char data = 0x00;
3
  spi_send(0xFF);
4
  data =  SPI2->DR; //get data 
5
  while((SPI2->SR & SPI_SR_RXNE) == 0); 
6
  return data;
7
}
1
int main(void){
2
   int data = 0;
3
   int i = 0;
4
5
   usart2_init(9600);
6
   USART_TARGET = 2;
7
   spi_init();
8
  
9
   GPIOB->BSRR |= GPIO_BSRR_BS12; //set pin PB12
10
   for(i=0; i < 10; i++){
11
    spi_send(0xFF);   //send 8*10 = 80 clock pulses
12
   }
13
14
   //send CMDO
15
   for(i = 0; i < 500; i++) {
16
       GPIOB->BSRR &= GPIO_BSRR_BR12; //reset pin PB12
17
       spi_send(0x40);
18
       spi_send(0x00);
19
       spi_send(0x00);
20
       spi_send(0x00);
21
       spi_send(0x00);
22
       spi_send(0x95);
23
       data = spi_receive();
24
       printf("Data = %d\n", data);
25
       GPIOB->BSRR |= GPIO_BSRR_BS12; //set pin PB12
26
       if (data==0x01) break;  
27
   }
28
29
  for(;;){
30
  }
31
}

Hat jemand einen Tipp für mich? Langsam gehen mir die Ideen aus...

von Ulf (Gast)


Lesenswert?

>while (!(SPI2->SR & SPI_SR_TXE)); //SPI2 Buffer empty?

Wartet nicht bis das letzte Byte gesendet wurde, zeigt nur an, das der 
Buffer wieder beschrieben werden kann. Dadurch kommt Deine CS 
Deaktivierung

mit GPIOB->BSRR |= GPIO_BSRR_BS12; //set pin PB12

möglicherweise zu früh.

Benutze mal das BSY Flag,

// Warten bis Byte vollständig gesendet ist
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);

von bbernhard1 (Gast)


Lesenswert?

Hi,

hab die Senderoutine folgendermaßen geändert:
1
void spi_send(char data){
2
  SPI2->DR = data;
3
  while ((SPI2->SR & SPI_SR_BSY) == 1);
4
}

Leider empfange ich immer noch nur 0xFF

von Lutz (Gast)


Lesenswert?

Also kompilierbar ist Dein Code so wahrscheinlich nicht; es kann also 
niemand das Ganze mal im Simulator testen. Btw.: Du auch nicht?

von bbernhard1 (Gast)


Angehängte Dateien:

Lesenswert?

Kompilieren kann ich meinen Code. Ich hab hier nur die relevanten 
Codeschnipsel rauskopiert. Im Anhang befinden sich alle 3 Sourcefiles 
die notwendig sind.

von Lutz (Gast)


Lesenswert?

bbernhard1 schrieb:
> data =  SPI2->DR; //get data
>   while((SPI2->SR & SPI_SR_RXNE) == 0);

Wenn ich mich im Datenblatt (übrigens: Ich habe in das des 103 
geschaut...) nicht auf die Schnelle verlesen habe, wird RXNE durch das 
Lesen von DR in der Zeile davor auf Null gesetzt. Du VERwhileST danach, 
so lange es Null ist ???
Ehrlich gesagt habe ich aber von den dicken Dingern praktisch noch keine 
Ahnung und dümpel so mit den ARVs rum, also kann es auch Quatsch sein, 
was ich schreibe. Denn sollte der Endlosloop irgendwann mal beendet 
werden, müßte Dein
> printf("Data = %d\n", data);
eigentlich "0" ausgeben ...

von Lutz (Gast)


Lesenswert?

Lutz schrieb:
> Denn sollte der Endlosloop irgendwann mal beendet
> werden, müßte Dein
>> printf("Data = %d\n", data);
> eigentlich "0" ausgeben ...

Vielleicht doch plausibel: Kannst Du mal ausprobieren, wenn Du in 
spi_receive statt spi_send(0xFF) mal spi_send(0xEE) schreibt, Du auch 
immer "EE" zurückbekommst? Dann würdest Du nämlich den vorher selbst ins 
DR geschriebenen Wert zurückbekommen.

von Lutz (Gast)


Lesenswert?

Nach ein bischen planschen im kühlen naß:

bbernhard1 schrieb:
> unsigned char spi_receive(){
>   char data = 0x00;
>   spi_send(0xFF);
>   data =  SPI2->DR; //get data
>   while((SPI2->SR & SPI_SR_RXNE) == 0);
>   return data;
> }
- Wenn die Funktion unsigned char zurückliefern soll, sollte data auf so 
definiert werden.
- Empfohlene Vorgehensweise bei Fullduplex:
1. Enable the SPI by setting the SPE bit to 1.
2.Write the first data item to be transmitted into the SPI_DR register 
(this clears the TXE flag).
3. Wait until TXE=1 and write the second data item to be transmitted. 
Then wait until RXNE=1 and read the SPI_DR to get the first received 
data item (this clears the RXNE bit). Repeat this operation for each 
data item to be transmitted/received until the n–1 received data.
4. Wait until RXNE=1 and read the last received data.
5. Wait until TXE=1 and then wait until BSY=0 before disabling the SPI.

Wobei ich noch nicht verstanden habe, ob der STM32 den CS jetzt 
automatisch steuert oder nicht. Du willst ihn in der for-Schleife selber 
steuern, aber nur dort.
bbernhard1 schrieb:
> GPIOB->BSRR |= GPIO_BSRR_BS12; //set pin PB12
Wenn ich mich nicht vertue, solle es GPIOB->BSRR = GPIO_BSRR_BS12; 
heißen, wenn ganz sicher nur PB12 beeinflußt werden soll. Gilt überall, 
wo Du BSRR benutzt hast. Auch das "&" ist hier überflüssig. Sonst nimmst 
Du jede vorherige Nutzung (Wert) des BSRR-Registers mit!

von Max (Gast)


Lesenswert?

Nimm an besten die LIB von STM. Da sind auch Beispiele drin.
http://www.st.com/mcu/download2.php?file=stm32f10x_stdperiph_lib.zip&info=STM32F103RB%20Firmware%20&url=http://www.st.com/stonline/products/support/micro/files/stm32f10x_stdperiph_lib.zip

Init Bsp. für SPI1 (nur Output) PORT-Einstellungen
1
  GPIO_DeInit(GPIOA);                                           // Port-Reset
2
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);         // GPIO Takt freigeben
3
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,  ENABLE);         // SPI1 Takt freigeben
4
5
  // SPI1 an Port A (SCK, MOSI)
6
  GPIO_InitStructure.GPIO_Pin   = (GPIO_Pin_5 | GPIO_Pin_7);    // PA5=SCK, PA7=MOSI
7
  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;              // Pin-Mode (Alternativ-Funktion)
8
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;             // Pin-Taktung
9
  GPIO_Init(GPIOA, &GPIO_InitStructure);                        // Port-Einstellung
10
11
  // SPI1 CS-Pin
12
  GPIO_InitStructure.GPIO_Pin   =  GPIO_Pin_4;                  // PA4=CS
13
  GPIO_InitStructure.GPIO_Mode  =  GPIO_Mode_Out_PP;            // Pin-Mode 
14
  GPIO_InitStructure.GPIO_Speed =  GPIO_Speed_50MHz;            // Pin Taktung
15
  GPIO_Init(GPIOA, &GPIO_InitStructure);                        // Port-Einstellung
16
....
1
// ***********************************************************************
2
// SPI Initialisierung
3
// ***********************************************************************
4
void init_spi (void) {
5
  SPI_InitTypeDef  SPI_InitStructure;                                   // SPI Struktur
6
7
  // SPI1 Konfiguration
8
  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;             // Nur senden
9
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                         // SPI Master-Mode
10
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                     // 8 Datenbits
11
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                           // CPOL High=Ruhezustand
12
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                          // CPHA 2. Flanke (L/H) gibt Daten aus
13
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                             // CS Chipselect per Software
14
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;   // Taktfrequenz 72MHz/32 = 2,2 MHz
15
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                    // MSB (höherwertiges Bit) zuerst senden
16
  SPI_InitStructure.SPI_CRCPolynomial = 7;                              // kein CRC Byte
17
  SPI_Init(SPI1, &SPI_InitStructure);                                   // SPI initialisieren
18
  SPI_Cmd(SPI1, ENABLE);                                                // SPI freigeben
19
}
20
21
void sendbyte_spi (unsigned char byte) {
22
  SPI_I2S_SendData(SPI1, byte);                                     // Byte per SPI senden
23
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);     // Warten bis Byte vollständig gesendet ist
24
  // TXE geht auch als Auswertung wenn mehrere Byte hintereinander, aber vor CS Deaktivierung (nach letzen Byte) BSY abfragen und warten
25
}

CS Steuerung musst Du im Bsp selber machen, geht aber auch von der SPI 
Unit her automatisch (NSS)

von bbernhard1 (Gast)


Lesenswert?

Lutz schrieb:
> Vielleicht doch plausibel: Kannst Du mal ausprobieren, wenn Du in
> spi_receive statt spi_send(0xFF) mal spi_send(0xEE) schreibt, Du auch
> immer "EE" zurückbekommst? Dann würdest Du nämlich den vorher selbst ins
> DR geschriebenen Wert zurückbekommen.

Wenn ich spi_send(0xEE) schreibe, dann bekomme ich immer noch 0xFF 
zurück.

Lutz schrieb:
> - Wenn die Funktion unsigned char zurückliefern soll, sollte data auf so
> definiert werden.

Stimmt, habs ausgebessert.

Lutz schrieb:
> - Empfohlene Vorgehensweise bei Fullduplex:
> 1. Enable the SPI by setting the SPE bit to 1.
> 2.Write the first data item to be transmitted into the SPI_DR register
> (this clears the TXE flag).
> 3. Wait until TXE=1 and write the second data item to be transmitted.
> Then wait until RXNE=1 and read the SPI_DR to get the first received
> data item (this clears the RXNE bit). Repeat this operation for each
> data item to be transmitted/received until the n–1 received data.
> 4. Wait until RXNE=1 and read the last received data.
> 5. Wait until TXE=1 and then wait until BSY=0 before disabling the SPI.

Kanns sein, dass diese Vorgehensweise nur dann gilt wenn ich 16bit 
übertragen möchte?

@Max: Die Lib von STM möchte ich wenns geht vermeiden, da ich die 
ehrlich gesagt nicht so gut finde. Es ist zwar ohne Lib zeitintensiver, 
aber so hab ich wenigstens die Chance die ganzen Register und deren 
Funktion kennenzulernen.


Hab gerade etwas zum RXNE Flag im Reference Manual gefunden:
When set, this flag indicates that there are valid received data in the 
Rx Buffer. It is reset when SPI Data register is read.

Das heißt, ich sollte meine Receive Funktion besser so umschreiben:
1
unsigned char spi_receive(){
2
  unsigned char data = 0x00;
3
  spi_send(0xFF);
4
  data =  SPI2->DR; //get data 
5
  while((SPI2->SR & SPI_SR_RXNE) != 0); 
6
  return data;
7
}

Ändert aber leider auch nichts dran...ich bekomme wieder nur 0xFF 
zurück.

von holger (Gast)


Lesenswert?

spi_send(0xFF);

Ok, du musst was senden um ein Byte zurück zu bekommen.

  data =  SPI2->DR; //get data

Du holst dir das Byte, auch ok.

Was soll das hier? Ist doch alles schon vorbei;)

  while((SPI2->SR & SPI_SR_RXNE) != 0);

Oder gehört das ganz woanders hin?

von bbernhard1 (Gast)


Lesenswert?

Du hattest Recht, da hab ich etwas verdreht. Hab mir jetzt nochmal das 
Reference Manual und die STM32 Sourcen angesehen und meine 
Sende/Empfangsroutine korrigiert:
1
unsigned char spi_receive(){
2
  unsigned char data = 0x00;
3
  spi_send(0xFF);
4
  while((SPI2->SR & SPI_SR_RXNE) == 0); 
5
  data =  SPI2->DR; //get data 
6
  return data;
7
}
1
void spi_send(char data){
2
  while ((SPI2->SR & SPI_SR_TXE) == 0);
3
  SPI2->DR = data;
4
}


Leider noch immer das Problem mit 0xFF

von Michael (Gast)


Lesenswert?

hast du beachtet, dass NSS im Master Mode beim Senden nicht automaisch 
zurückgesetzt wird? Das ist nämlich etwas "buggy" beim STM32.

von Ulf (Gast)


Lesenswert?

Gibt Deine SPI überhaupt was aus? Hast Du mal den Oszi dran gehalten.

Ansonsten sind in der STM LIB und zu den EVAL Boards einige Beispiele.

Gegen die Nutzung der STM-LIB spricht eigentlich nichts. Läuft seit der 
Version 3 recht stabil.

Errata Sheet mal durchgesehen? Div. Abhängikeiten ua. der SPI bei 
Nutzung anderer Units.

von bbernhard1 (Gast)


Lesenswert?

Im Errata Sheet konnte ich keine Abhängigkeiten zwischen dem USART2 und 
dem SPI2 finden.

Hab mir nochmals das Reference Manual hergenommen und meinen Sourcecode 
überarbeitet.
Erstmal @Lutz: Du hattest mit dieser Aussage Recht:
Lutz schrieb:
> Wenn ich mich nicht vertue, solle es GPIOB->BSRR = GPIO_BSRR_BS12;
> heißen, wenn ganz sicher nur PB12 beeinflußt werden soll. Gilt überall,
> wo Du BSRR benutzt hast. Auch das "&" ist hier überflüssig.

Hab jetzt nochmals ein paar Messungen mit dem Oszi durchgeführt:
-) Der Clock wird generiert
-) Es werden Daten gesendet

Nach unzähligen Testversuchen hab ichs zwei Mal geschafft, 0x01 
zurückzubekommen. Ich bin mir aber nicht sicher ob das ein glücklicher 
"Zufall" war, oder ob die SD Karte ein bisschen zickt.

Mein derzeitiger Code:
1
void spi_init(){
2
3
   //CS = PB12 (General Purpose Output)
4
   //CLK = PB13 (Alternative Function, Push Pull)
5
   //SDI(MISO) = PB14 (Input Pull-Up)
6
   //SDO(MOSI) = PB15 (Alternative Function, Push Pull)
7
8
   RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //GPIOB clock enable
9
   GPIOB->CRH &= ~0xFFFF0000;    //clear GPIOB configuration bits
10
   GPIOB->CRH |= 0xB8B30000; //set GPIOB configuration
11
12
   RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;   //SPI2 clock enable
13
   SPI2->CR1 &= ~SPI_CR1_SPE;  //SPI2 disable
14
   SPI2->CR1 |= SPI_CR1_CRCEN; //enable hardware CRC calculation
15
   SPI2->CR1 &= ~SPI_CR1_BIDIMODE; //fullduplex data mode enable
16
   SPI2->CR1 &= ~SPI_CR1_DFF; //data frame format = 8bit
17
   SPI2->CR1 &= ~SPI_CR1_SSM; //disable software slave management
18
   SPI2->CR2 |= SPI_CR2_SSOE; //slave select output is enabled in master mode
19
20
   SPI2->CR1 |= 0x0038;  //fpclk /256
21
   SPI2->CR1 &= ~SPI_CR1_LSBFIRST; //MSB transmitted first
22
23
   SPI2->CR1 |= SPI_CR1_MSTR; //configure as master
24
   SPI2->CR1 &= ~SPI_CR1_CPOL; //CPOL = 0
25
   SPI2->CR1 &= ~SPI_CR1_CPHA; //CPHA = 0
26
27
   GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
28
29
   SPI2->CR1 |= SPI_CR1_SPE;  //SPI2 enable
30
}
1
void spi_send(char data){
2
  GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
3
  while ((SPI2->SR & SPI_SR_TXE) == 0);
4
  SPI2->DR = data;
5
  GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
6
}
1
unsigned char spi_receive(){
2
  unsigned char data = 0x00;
3
  spi_send(0xFF);
4
  while((SPI2->SR & SPI_SR_RXNE) == 0); 
5
  data =  SPI2->DR; //get data 
6
  return data;
7
}
1
int main(void){
2
   int data = 0;
3
   int i = 0;
4
   usart2_init(9600);
5
   USART_TARGET = 2;
6
   spi_init();
7
8
   GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
9
   for(i=0; i < 10; i++){
10
    spi_send(0xFF);   //send 8*10 = 80 clock pulses
11
   }
12
   
13
   GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
14
   //send CMDO
15
   for(i = 0; i < 500; i++) {
16
       GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
17
       spi_send(0x40);
18
       spi_send(0x00);
19
       spi_send(0x00);
20
       spi_send(0x00);
21
       spi_send(0x00);
22
       spi_send(0x95);
23
       data = spi_receive();
24
       printf("Data = %d\n", data);
25
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
26
       if (data==0x01) break;  
27
   }
28
29
  for(;;){
30
  }  
31
}

von 78787 (Gast)


Lesenswert?

hast du während dieser init den takt mal runtergesetzt ?
in 90% der beispiele ist der takt hier bei < 1Mhz

erst nach der init wird dann auf  max möglicher takt geschwenkt

von Bernhard B. (schluchti)


Lesenswert?

Ich hab bereits den größten Taktteiler (fpclk/256) eingestellt.

von holger (Gast)


Lesenswert?

Mach hinter dem

       spi_send(0x95);

mal mehrere

       data = spi_receive();
       data = spi_receive();

und schau dir jeweils an wie data aussieht.

von Bernhard B. (schluchti)


Lesenswert?

Auch hier bekomm ich immer nur 0xFF zurück.

von Lutz (Gast)


Lesenswert?

bbernhard1 schrieb:
> Hab jetzt nochmals ein paar Messungen mit dem Oszi durchgeführt:
> -) Der Clock wird generiert
> -) Es werden Daten gesendet
>
> Nach unzähligen Testversuchen hab ichs zwei Mal geschafft, 0x01
> zurückzubekommen. Ich bin mir aber nicht sicher ob das ein glücklicher
> "Zufall" war, oder ob die SD Karte ein bisschen zickt.

Mit diesen beiden Aussagen tippe ich mal auf die Karte. Zumindest hat 
sie dann einen großen Anteil am Problem. Denn wenn die selbe Software 
mal ein Ergebnis liefert und mal nicht, muß etwas anderes das Problem 
verursachen oder zumindest stark mitwirken. Bleibt also nur die 
Hardware. Wenn möglich, einfach mal zum Testen eine andere Karte nehmen. 
Die Kosten ja zum Glück nichts mehr.

von Bernhard B. (schluchti)


Lesenswert?

Ich hab vorhin mal eine andere SD Karte getestet, die hat auch nicht 
funktioniert. Aber der Test ist vermutlich nicht sehr aussagekräftig 
gewesen, da die neue Karte vom gleichen Hersteller wie die alte Karte 
(nämlich Traveler SD-Karte) ist. Lediglich die Speichergröße variiert.

Mal schauen ob ich noch eine Karte von einem anderen Hersteller finde

von Bernhard B. (schluchti)


Lesenswert?

Ok, jetzt wirds wirklich strange. Ich hab keine SD-Karte im Slot stecken 
und bekomme trotzdem 0xFF zurück! Was empfängt der da bitte? Die Daten 
die ich wegschicke könnens auch nicht sein, denn wenn ich in meiner 
Empfangsroutine
folgendes ändere:
1
unsigned char spi_receive(){
2
  unsigned char data = 0x00;
3
  //spi_send(0xFF); wird geaendert zu
4
  spi_send(0x40);
5
  while((SPI2->SR & SPI_SR_RXNE) == 0); 
6
  data =  SPI2->DR; //get data 
7
  return data;
8
}

dann empfange ich immer noch 0xFF

Der Schaltplan von meinem Entwicklungsboard: 
http://www.olimex.com/dev/images/ARM/ST/STM32-P103-sch.gif

Ich checks nicht...

von hdd (Gast)


Lesenswert?

Sind die Pullups in der MISO-Leitung aktiviert?
Wenn ja, dann mal deaktivieren und nen Pulldown nach Masse bei fehlender 
Karte an die Leitung hängen, dann solltest du auf jeden Fall 0x00 zurück 
bekommen wenn der Fehler nicht in der Software liegt.

von Bernhard B. (schluchti)


Lesenswert?

Ich hab den MISO Pin jetzt mal auf Floating Input geschalten, das heißt 
dann sollte ja nur der Pullup am Entwicklungsboard wirken?
Ändert leider auch nichts dran.

von Matthias K. (matthiask)


Lesenswert?

>SPI2->CR1 &= ~SPI_CR1_CPOL; //CPOL = 0
>SPI2->CR1 &= ~SPI_CR1_CPHA; //CPHA = 0

Ist das überhaupt so Ok? Sieht das Timing auf dem Oszi geanus so aus wie 
im Datenblatt der SD?

Oft Fehler, da bei falscher Einstellung, die Daten zur falschen Zeit 
(H/L, L/H Flanke) übernommen werden.

von STM32 (Gast)


Lesenswert?

btw, der spi hat so eine art fifo wenn ich mich nicht täusche, der füllt 
sich auch mit den sendedaten, also nach jedem senden nochmal das 
datenregister auslesen..
Dann sollte das funktionieren.


char spi_send(char data){

  GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12

  while ((SPI2->SR & SPI_SR_TXE) == 0);
  SPI2->DR = data;

  while((SPI2->SR & SPI_SR_RXNE) == 0);
  data =  SPI2->DR; //get data

  GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12

  return data;
}

irgendwie so, hoffe das ist richtig, hab nicht ins datenblatt wegen den 
statusregistern geschaut.

spi_send(xy);                // schreiben
data = spi_send(0xFF);       // lesen

von Matthias K. (matthiask)


Lesenswert?

Kuck mal in die aktuelle LIB. Dort ist ein Bsp. für die SD-Karten drin.

STM32F10x_StdPeriph_Lib_V3.3.0\Utilities\STM32_EVAL\Common\stm32_eval_sp 
i_sd.c

Zumindest kannst Dir dort abkucken, wie es geht, auch wenn Du die LIB 
nicht einsetzen willst (Warum eigentlich nicht?). Die Quellen der 
SPI-LIB im SRC-Ordner verraten sicher auch einiges.

von Bernhard B. (schluchti)


Lesenswert?

@STM32: Ich hab das mal in meine Senderoutine übernommen. Hab das auch 
in der von Matthias K. angesprochenen Library so gefunden. Leider bekomm 
ich noch immer nur 0xFF zurück.

@Matthias K.
Ich hab mir das von dir angesprochene Sourcecodefile aus der ST Library 
angesehen und darin auch die CMD0 Initialisierung gefunden. Soweit ich 
das beurteilen kann, unterscheidet sich der CMD0 Initialisierungsprozess 
nicht wirklich von meinem. Ein paar kleine Unterschiede konnte ich 
feststellen (höherer Takt, Verwendung der internen CRC Calculation 
Unit...), aber wie gesagt kein wesentlicher Unterschied.

Eine andere SD Karte (Kingston 1GB) hab ich auch schon versucht, liefert 
auch nur 0xFF zurück.

von Bernhard B. (schluchti)


Lesenswert?

Hat noch jemand nen Tipp?

btw: vielen Dank an alle die sich bis jetzt beteiligt haben!

von Lutz (Gast)


Lesenswert?

Oh weh, ich habe gerade mal versucht, den Kram der ST-LIB 
nachzuvollziehen und zu überfliegen. Nach mehreren includes und defines 
und hin- und hergespringe bin ich dann abgestürzt. Entweder haben die es 
echt drauf, etwas zu verkomplizieren oder es ist doch viel komplexer 
als ich dachte. Wenn auch nur 10 % des Codes in der LIB wirklich 
erforderlich sind, dann dürften Deine paar Zeilen sicherlich einiges 
ausgelassen haben ...

Du schriebst, daß Du mit einem Oszi Takt und Daten messen konntest. Hat 
das auch alles gestimmt, also Bits, Sequenz, Frequenz etc., und vor 
allem in beide Richtungen? Witzig ist ja auch, daß ohne SPI-Karte 0xFF 
zurückkommt.

von Matthias K. (matthiask)


Lesenswert?

Die LIB ist einfacher zu beherschen, als es im ersten Moment aussieht. 
Es ist ein Template für die verschiedenen IDE/Compiler enthalten. Die 
ganze Ordnerstruktur für den Anfang erstmal beibehalten.

von Bernhard B. (schluchti)


Lesenswert?

Das ist genau der Grund warum ich die Library nicht wirklich mag. Da 
sind so viele defines drinnen um ja alle STM32 Devices abzudecken. Was 
auch ziemlich lustig ist: Oftmals werden dort Namen definiert, die so 
nicht mal im Reference Manual zu finden sind. Da muss man dann erst 
wieder auf Registerebene runter und schauen welche Register tatsächlich 
verwendet werden.

Wenn ich mich nicht verschaut habe, dann sollte meine CMD0 
Initialisierung mit der aus der Standard-Lib übereinstimmen. Wie du 
bereits gesagt hast, ist das ein ziemliches Rumgehopse in den 
Sourcefiles. Ich hoff ich hab nichts übersehen.

Ich werd mir das morgen nochmals mit dem Oszi anschauen. Damals hab ich 
nur geschaut ob ein Takt generiert wird und auf der MOSI Leitung etwas 
gesendet wird. Das sollte ich mir mit dem Oszi echt noch genauer 
untersuchen.

von Matthias K. (matthiask)


Lesenswert?

>SPI2->CR1 &= ~SPI_CR1_CPOL; //CPOL = 0
>SPI2->CR1 &= ~SPI_CR1_CPHA; //CPHA = 0

Hatten wir weiter oben schon, dachte Du hast das gecheckt?

von Bernhard B. (schluchti)


Lesenswert?

Ich weiß, dass sich meine CPOL/CPHA Konfiguration von der Konfiguration 
der STM32 Lib unterscheidet. Wenn ich das richtig verstanden habe, dann 
sollte das auch mit CPOL = 0 und CPHA = 0 funktionieren.
Siehe elm-chan:
"For SDC, the 'SPI mode 0' is defined for its SPI mode. For MMC, it is 
not the SPI timing, both latch and shift actions are defined with rising 
edge of the SCLK, but it seems work in SPI mode 0 in SPI mode. Thus the 
SPI Mode 0 (CPHA=0, CPOL=0) is the proper setting for MMC/SDC interface, 
but SPI mode 3 also works as well in most case."
http://elm-chan.org/docs/mmc/mmc_e.html

Ich habs aber auch schon so probiert, wie ST das in der Library macht - 
es bleibt aber dabei, ich empfange nur 0xFF

von STM32 (Gast)


Lesenswert?

Du könntest dir mal ein Array erstellen und solange lesen bis der 
empfangsbuffer bzw das datenregister leer ist, das array schaust du dir 
danach mal an, evtl ist doch noch etwas versetzt und somit solltest du 
die zu erwartenden Daten auch irgendwo im array sehen sofern der rest 
richtig ist.

von Bernhard B. (schluchti)


Angehängte Dateien:

Lesenswert?

So, ich hab heute die Messungen mit dem Oszi durchgeführt.

Der Sendevorgang auf der MOSI Leitung sieht ganz gut aus (siehe 
Screenshot).
Um meine Senderoutine zu testen, hab ich in einer Endlosschleife 0x40 
gesendet. Die CPOL/CPHA Konfiguration passt auch, die Daten werden mit 
der steigenden Flanke angelegt.

Der Takt beträgt, wie in der Initialisierungsroutine konfiguriert, 
125kHz.

Die Empfangsroutine macht mir Sorgen, die MISO Leitung ist durchgehend 
auf High...

von holger (Gast)


Lesenswert?

>Die Empfangsroutine macht mir Sorgen, die MISO Leitung ist durchgehend
>auf High...

Was ist mit der CS Leitung? Wenn die dauernd high ist kommt
auch nichts raus aus der SD.

von holger (Gast)


Lesenswert?

GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
   GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12

Sieht für mich irgendwie gleich aus;)

von holger (Gast)


Lesenswert?

>GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
>GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12

>Sieht für mich irgendwie gleich aus;)

Nee, doch nicht:( Hab doch glatt das BR12/BS12 übersehen.

von Lutz (Gast)


Lesenswert?

Aber nur irgendwie: Unterschied R und S :-)

von Matze (Gast)


Lesenswert?

Ist schon lang her aber ich kann mich noch blaß daran erinnern...
Hatte damals nen Mp3-Player gebastelt, der lief super mit MMC Karten, SD 
funkionierten nur manchmal.
Ich weiß die Lösung nicht mehr ganz genau, nur daß ich im init der Karte 
irgendwo ne Wartezeit bißchen verlängert hab. Und siehe da, 
funktionierte Tadellos

von Michael (Gast)


Lesenswert?

trotzdem irgendwie komisch... ich mach das mit:
1
GPIOB->BRR = GPIO_Pin_12;
bzw.
1
GPIOB->BSRR = GPIO_Pin_12;

von Lutz (Gast)


Lesenswert?

Michael schrieb:
> trotzdem irgendwie komisch... ich mach das mit:
> GPIOB->BRR = GPIO_Pin_12;      bzw.GPIOB->BSRR = GPIO_Pin_12;

Komisch nicht, aber das Ergebnis eines der viiiiiielen #defines in der 
LIB bzw. der stm32f10x.h, quasi eine Geschmacksfrage: Im jeweiligen BSRR 
sind die unteren 16 bit für das Setzen und die oberen 16 bit für das 
Rücksetzen des jeweiligen bits zuständig. Also kann man alles mit dem 
BSRR machen und braucht das BRR gar nicht. Die Unterscheidung erfolgt 
über die defines
1
#define GPIO_BSRR_BS12  ((uint32_t)0x00001000)   /*!< Port x Set bit 12 */ 
2
#define GPIO_BSRR_BR12  ((uint32_t)0x10000000)   /*!< Port x Reset bit 12 */

von Bernhard B. (schluchti)


Angehängte Dateien:

Lesenswert?

holger schrieb:
>>Die Empfangsroutine macht mir Sorgen, die MISO Leitung ist durchgehend
>>auf High...
>
> Was ist mit der CS Leitung? Wenn die dauernd high ist kommt
> auch nichts raus aus der SD.

Ich ziehe die CS Leitung auf Low, schicke ein Datenbyte, lese das 
Empfangsregister um die Antwort der SD Karte zu bekommen und setze CS 
wieder auf High (siehe Screenshot)

gelb = CS
blau = MOSI

Um das mit dem Oszi gut aufnehmen zu können, schicke ich wieder 0x40 in 
der Endlosschleife. MISO bleibt, wie schon vorhin erwähnt, immer auf 
High.

von Michael (Gast)


Lesenswert?

was macht deine Clock Leitung während dessen? Clockt die vllt nicht? :)

von Bernhard B. (schluchti)


Lesenswert?

SCK clockt (siehe auch den 1.Oszi-Screenshot weiter oben), hab aber 
leider nur ein Oszi mit 2 Kanälen.

von Lutz (Gast)


Lesenswert?

Bernhard B. schrieb:
> Ich ziehe die CS Leitung auf Low, schicke ein Datenbyte, lese das
> Empfangsregister um die Antwort der SD Karte zu bekommen und setze CS
> wieder auf High (siehe Screenshot)

Hm. Ich habe mir jetzt das Timing nicht durchgerechnet, aber die 
High-Phase Deines CS-Signals sieht sehr kurz aus. Vielleicht zu kurz, um 
von der Karte erkannt zu werden. Außerdem macht es den Eindruck, als 
wenn nur 1 Bit und nicht ein ganzes Byte übertragen wird. Wenn das 
Timing aber stimmt und hier 2 Bits aus dem Byte dargestellt sind, dürfte 
CS dazwischen nicht High werden.
Schau Dir z.B. mal Figure 239 auf Seite 660 des Manuals an. Da sieht 
eine SPI-Sequenz doch anders aus.

von Bernhard B. (schluchti)


Lesenswert?

Auf welches Manual beziehst du dich? Ich hab hier das Reference Manual 
RM0008 vom STM32F103RBT6 vor mir liegen und da ist auf Seite 660 der 
Watchdog.

Wenn ich keinen groben Denkfehler hab, dann sollte das Timing meiner 
Meinung nach schon stimmen. Ich schicke, wie gesagt, in einer 
Endlosschleife 0x40.

0x40 = 01000000b

Die CS Leitung zieht auf Low, dann wird ausgehend vom MSB das Byte 
übertragen. Nach der Übertragung wird CS wieder auf High gezogen.

Ich werd mal die High-Phase der CS Leitung mit einem delay etwas 
vergrößern.
Vielleicht bringts ja etwas...

von Bernhard B. (schluchti)


Lesenswert?

Ok, ich warte jetzt zwischen jedem Befehl 2ms - bringt aber leider auch 
nichts. Ich hab echt keinen blassen Dunst was da falsch sein könnte...

von Lutz (Gast)


Lesenswert?

Bernhard B. schrieb:
> Auf welches Manual beziehst du dich? Ich hab hier das Reference Manual
> RM0008 vom STM32F103RBT6 vor mir liegen und da ist auf Seite 660 der
> Watchdog.

Ich meine auch das RM0008, Doc ID 13902 Rev 11 vom April 2010. Sollte 
eigentlich das Aktuellste sein. Sicher, daß Du nicht bei 460 
nachgeschaut hast?

Bernhard B. schrieb:
> Die CS Leitung zieht auf Low, dann wird ausgehend vom MSB das Byte
> übertragen. Nach der Übertragung wird CS wieder auf High gezogen.

Ich interpretiere den Screenshot so:
CS kommt low ins Bild und wird 20 µs später für ca. 100 ns high, um 
danach wieder low weiterzulaufen. Ca. 15 µs später wird MOSI, der von 
Anfang an low war, für ca. 10 µs high, um dann low weiterzulaufen. Das 
ganze wiederholt sich ca. 70 µs später noch einmal, außer das CS jetzt 
für ca. 200 ns high wird.
=> Ich kann da kein ganzes Byte durchlaufen sehen, sondern nur max. 2 
Bit. Und bei denen wird zwischenzeitlich CS kurz high. Aber ich habe 
auch jetzt nicht das Timing durchgerechnet. Oder sind die beiden 
MOSI-Peaks in Wirklichkeit 2 ganze Bytes (0x04), man erkennt nur wegen 
des gestauchten Zeitstrahls nicht die einzelnen 8 bits da drin?

Oh, Oh, jetzt habe ich aber einen Termin ....

von gggg (Gast)


Lesenswert?

hast du es mal mit einer fertigen Lib versucht ?

ich hab zB die Elm Chan lib benutzt .
diese lief auch nach einigen anläufen erst richtig ..

gerade das CS timing sollte man bei STM oder höhrern µC nicht dem HW SPI 
überlassen
zumindest haben hier die meisten fertigen Libs ihre schwierigkeiten

wenn da mal ein CS high zu früh kommt wars das mit den 0xFF oder 0xFE ( 
token)

von Lutz (Gast)


Lesenswert?

Bitte post mal Deinen aktuellen Code; mit den ganzen Änderungen wirds 
langsam unübersichtlich.

von Bernhard B. (schluchti)


Lesenswert?

Lutz schrieb:
> Ich meine auch das RM0008, Doc ID 13902 Rev 11 vom April 2010. Sollte
> eigentlich das Aktuellste sein. Sicher, daß Du nicht bei 460
> nachgeschaut hast?

Ich hatte noch ein ältere Version vom RM0008 Manual. Hab gar nicht 
gewusst, dass es schon ein neueres gibt. Danke für den Hinweis!

Lutz schrieb:
> CS kommt low ins Bild und wird 20 µs später für ca. 100 ns high, um
> danach wieder low weiterzulaufen. Ca. 15 µs später wird MOSI, der von
> Anfang an low war, für ca. 10 µs high, um dann low weiterzulaufen. Das
> ganze wiederholt sich ca. 70 µs später noch einmal, außer das CS jetzt
> für ca. 200 ns high wird.
> => Ich kann da kein ganzes Byte durchlaufen sehen, sondern nur max. 2
> Bit. Und bei denen wird zwischenzeitlich CS kurz high. Aber ich habe
> auch jetzt nicht das Timing durchgerechnet. Oder sind die beiden
> MOSI-Peaks in Wirklichkeit 2 ganze Bytes (0x04), man erkennt nur wegen
> des gestauchten Zeitstrahls nicht die einzelnen 8 bits da drin?

Ich werd da morgen nochmals messen, vielleicht bekomme ich den 
Oszi-Screenshot etwas besser hin.

gggg schrieb:
> gerade das CS timing sollte man bei STM oder höhrern µC nicht dem HW SPI
> überlassen
Stimmt, das hab ich mittlerweile auch schon mitbekommen. Die 
Hardware-NSS-Steuerung beim STM32 funktioniert ja mal gar nicht so wie 
gewünscht.
Mittlerweile kontrolliere ich CS komplett per Software, da gibts 
wenigstens keine bösartigen Überraschungen :)

gggg schrieb:
> ich hab zB die Elm Chan lib benutzt .
> diese lief auch nach einigen anläufen erst richtig ..
Mit einer fertigen Lib hab ichs noch nicht versucht.
Im Moment bin ich mir überhaupt nicht sicher wo ich den Fehler vermuten 
soll. Bei der Konfiguration der SPI Schnittstelle, beim Senden der 
Daten, beim Empfangen der Daten oder irgendwo dazwischen? Das ist leider 
der erste Versuch mit der SPI Schnittstelle auf dem STM32, deshalb kann 
ich nicht wirklich etwas ausschließen...

Der aktuelle Code:
1
void spi_init(){
2
3
   //CS = PB12 (General Purpose Output)
4
   //CLK = PB13 (Alternative Function, Push Pull)
5
   //SDI(MISO) = PB14 (Input Pull-Up)
6
   //SDO(MOSI) = PB15 (Alternative Function, Push Pull)
7
8
   RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //GPIOB clock enable
9
   GPIOB->CRH &= ~0xFFFF0000;    //clear GPIOB configuration bits
10
   GPIOB->CRH |= 0xB4B30000; //set GPIOB configuration
11
12
   RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;   //SPI2 clock enable
13
   SPI2->CR1 &= ~SPI_CR1_SPE;  //SPI2 disable
14
   SPI2->CR1 &= ~SPI_CR1_BIDIMODE; //fullduplex data mode enable
15
   SPI2->CR1 &= ~SPI_CR1_DFF; //data frame format = 8bit
16
   SPI2->CR2 |= SPI_CR2_SSOE; //slave select output is enabled in master mode
17
   
18
   SPI2->CR1 |= SPI_CR1_SSM; //enable software slave management
19
  
20
   SPI2->CR1 |= 0x0028;  //fpclk /64
21
22
   SPI2->CR1 &= ~SPI_CR1_LSBFIRST; //MSB transmitted first
23
24
   SPI2->CR1 |= SPI_CR1_MSTR; //configure as master
25
   
26
   SPI2->CR1 |= SPI_CR1_CPOL; //CPOL = 1
27
   SPI2->CR1 |= SPI_CR1_CPHA; //CPHA = 1
28
29
   SPI2->CR1 |= SPI_CR1_SPE;  //SPI2 enable
30
}
1
unsigned char spi_send(unsigned char data){
2
  unsigned char dat = 0x00;
3
4
  while ((SPI2->SR & SPI_SR_TXE) == 0);
5
  SPI2->DR = data;
6
  while((SPI2->SR & SPI_SR_RXNE) == 0);
7
  dat =  SPI2->DR; //get data
8
9
  return dat;
10
}
1
int main(void){
2
   char data = 0;
3
   int i = 0;
4
   usart2_init(9600);
5
   USART_TARGET = 2;
6
   spi_init();
7
8
   
9
     delay_ms(50);
10
       GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
11
       for(i=0; i < 10; i++){
12
        delay_ms(2);
13
        spi_send(0xFF);   //send 8*10 = 80 clock pulses
14
       }
15
   
16
   
17
   
18
   GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
19
   //send CMDO
20
   for(i = 0; i < 500; i++) {
21
        delay_ms(2);
22
       GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
23
       spi_send(0x40);
24
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
25
26
    delay_ms(2);
27
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
28
       spi_send(0x00);
29
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
30
31
    delay_ms(2);
32
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
33
       spi_send(0x00);
34
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
35
36
    delay_ms(2);
37
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
38
       spi_send(0x00);
39
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
40
41
    delay_ms(2);
42
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
43
       spi_send(0x00);
44
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
45
46
      delay_ms(2);
47
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
48
       spi_send(0x95);
49
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
50
51
    delay_ms(2);
52
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
53
    data = spi_send(0xFF);
54
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12
55
56
    printf("Data = %d\n", data);
57
       if (data==1) break;  
58
   }
59
60
  for(;;){
61
  }     
62
}

Danke nochmals für die vielen Gedankenanstöße bzw 
Verbesserungsvorschläge!

von gggg (Gast)


Lesenswert?

soweit ich das sehe musst du für die init

0x40 0x00 0x00 0x00 0x00 senden mit 0x95 als CRC
das ganze aber als block ohne CS zu ändern
also im prinzip ist das bei jedem commando so ..

CS wird hierbei erst wieder gesetzt wenn man fertig is ..

1
    GPIOB->BSRR = GPIO_BSRR_BR12; //reset pin PB12
2
    //send CMDO
3
    spi_send(0x40);
4
    spi_send(0x00);
5
    spi_send(0x00);
6
    spi_send(0x00);
7
    spi_send(0x00);
8
    spi_send(0x95);
9
    
10
    n = 10;
11
    do
12
    {
13
       data = spi_send(0xFF);
14
    }
15
    while ((data & 0x80)&& --n)
16
17
    // hier CS nur wieder setzen wenn man nix weiter tun will 
18
    // ansonst .. LOW lassen
19
20
    GPIOB->BSRR = GPIO_BSRR_BS12; //set pin PB12


bei http://elm-chan.org/fsw/ff/00index_e.html
brauch man "nur" die SPI schreib/lese routinen anzupassen
in den samples gibts genug beispiele

also insgesammt 5 funktionen anpassen
init
spi_send
spi_receive
spi_send_block
spi_receive_block

von Bernhard B. (schluchti)


Lesenswert?

Gut zu wissen. Ich dachte, dass man CS nach jedem Byte wieder auf High 
ziehen muss. Ich hab den Sourcecode jetzt mal dahingehend umgebaut, aber 
leider bekomm ich noch immer 0xFF zurück.

Ich werd jetzt nochmal das Oszi anwerfen, vielleicht entdeck ich da 
etwas.

von Bernhard B. (schluchti)


Lesenswert?

Juhu, es funktioniert!

Ich hab die CS Leitung irrtümlicherweise schon nach dem Befehl 0x95 auf 
High gezogen.

Danke an alle die hier gepostet haben. Ohne euch würd ich wahrscheinlich 
jetzt noch an dem Problem sitzen!

von STM32 (Gast)


Lesenswert?

btw. ich würde sowas einfach über ein define machen..

#define SD_CS0 (GPIOB->BSRR = GPIO_BSRR_BR12)  //reset pin PB12
#define SD_CS1 (GPIOB->BSRR = GPIO_BSRR_BR12)  //  set pin PB12

SD_CS0;

...


SD_CS1;

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.