SD - Card antwortet nicht

OP #4470765
Lesenswert?

Hallo Leute,

ich versuche eine SD-Karte über SPI anzusteuern. uC (Atmega32) hängt an 
einem 4Mhz Quarz. SPI Clock liegt bei 250kHz. Für den Fall, dass die 
SD-Card nicht mit dem antwortet, was ich erwarte bzw. garnicht 
antwortet, schreibt der uC mir den Response mit einer 1 als MSB auf mein 
LCD (MSB sollte eigentlich immer 0 sein). Auf dem LCD landet eine 128, 
also gibt SD_Command eine 0 zurück. Das sollte nur passieren, wenn die 
SD Karte garnicht antwortet.

- Pegel an der SD-Card stimmen (3,3V und 0V)
- Anschluss des SPI über kleine Kabel ~17cm (schon zu lang?)
- x-Mal richtigen Anschluss geprüft.
- /SS Pin als Output
- keine Warnungen / Fehler des Compilers (avr-gcc mit Atmel Studio 4)

Hat jemand eine gute Idee (ich nehm auch ne blöde - wenns klappt ;) ), 
woran es liegen könnte?

Hier der Code (eigentlich schön in Header und C-Files ausgegliedert)
1
#include <stdint.h>
2
#include <avr/io.h> 
3
#include <string.h>  
4
#include <avr/interrupt.h>
5
#include <util/delay.h> 
6

7
uint8_t Flags;
8
unsigned char SD_Buffer[16];
9
uint8_t Temp; 
10

11
// PINS
12
#define CS          (1<<PD7)  
13
#define CS_DDR       DDRD
14
#define CS_ENABLE()   (PORTD &= ~CS)
15
#define CS_DISABLE()   (PORTD |= CS)
16

17
// SD-CARD Commands       0b01xxxxxx
18
#define _SD_IDLE       0x40  //0b01000000 - GO-IDLE-STATE
19
#define _SD_Init       0x41  //0b01000001 - Initialize
20
#define _SD_Set_Blocklenght  0x50  //0b01010000 - Set Block lenght
21
#define _SD_Read_Block    0x51  //0b01010001 - Read one Block
22

23
#define SS      (1<<PB4)
24
#define MOSI       (1<<PB5)
25
#define MISO       (1<<PB6)
26
#define SCK       (1<<PB7)  
27
#define SPI_DDR      DDRB
28

29
void SPI_Init(){
30
  // MOSI, /SS and SCK -> Output; MISO -> Input
31
  SPI_DDR = (1<<MOSI)|(1<<SCK)|(1<<SS);
32
  SPI_DDR &= ~(1<<MISO);
33

34
  // Enable SPI, Master, set clock rate
35
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);
36
}
37

38
unsigned char SPI_Write(unsigned char ch){
39
  // write on SPI
40
    SPDR = ch;
41
  // wait for Flag
42
    while(!(SPSR & (1<<SPIF)))  
43
  // return DataRegister    
44
    return SPDR;
45
}
46

47
uint8_t SD_Init(){
48
  uint8_t i, a;
49
  a=0;
50

51
  // Deselect SD-Card
52
  CS_DISABLE();
53
  // Give the SD-Card some time to initialize
54
  for (i=0; i<10; i++)
55
    SPI_Write(0xFF);
56

57
  // GO-IDLE-STATE - Try 10 Times
58
  for (i=0; i<10 && (a = SD_Command(_SD_IDLE, 0x00000000, 0x95, 8)) != 1; i++)
59
    _delay_ms(100);
60
  if (i==10)  // Card did not response
61
    return a + 0x80;
62

63
  // Initialize!
64
  for (i=0; i<10 && (a = SD_Command(_SD_Init, 0x00000000, 0xFF, 8)) != 1; i++)
65
    _delay_ms(100);
66
  if (i==10)  // Card did not come out of idle
67
    return a;
68

69
  // Set Block length 512
70
  SD_Command(_SD_Set_Blocklenght, 0x00000200, 0xFF, 8);
71
        
72
    SD_sector = SD_pos = 0;
73
        
74
    return 0;
75
}
76

77
uint8_t SD_Command(uint8_t cmd, uint32_t arg, uint8_t crc, uint8_t read){
78
  //
79
  uint8_t i, buffer[32], resp = 0;
80

81
  // Be my Slave! :D
82
  CS_ENABLE();
83
  _delay_ms(1);
84

85

86
  SPI_Write(cmd);
87
  SPI_Write(arg>>24);
88
  SPI_Write(arg>>16);
89
  SPI_Write(arg>>8);
90
  SPI_Write(arg);
91
  SPI_Write(crc);
92

93
  for(i=0; i<read; i++)
94
        buffer[i] = SPI_Write(0xFF);
95

96
  // Bye!
97
  CS_DISABLE();
98

99
  // response isnt 0xFF, so wait until something else is send
100
  for(i=0; i<read; i++) {
101
    if(buffer[i] != 0xFF)
102
      resp = buffer[i];
103
  }
104

105
  // Return the response
106
  return resp;
107
}
108

109
int main (void){
110
  // Zeit zum starten geben
111
  _delay_ms(10);
112
  // PORTS  
113
  // DDRx = 0 -> I, = 1 -> O        
114
  DDRD   =   (1<<PD7);
115
  // PORTx= 1 -> Pull-Up bzw. Output high  
116
  
117
  // I2C, SPI, SD
118
  I2C_Init();
119
  SPI_Init();
120

121
  // LCD
122
  LCD_Init();
123
  LCD_Light(1);    
124
  // Menu
125
  //gt_Main();
126

127
  // Systemzeit initialisieren
128
  Timer0_Sys_Clock();
129

130
  // globale Interrupts aktivieren
131
  sei();
132

133
  // Test der SD Karte
134
  
135
  Temp = SD_Init();
136
  my_utoi(Temp, 4, SD_Buffer);
137
  LCD_Cursor(8, 2);
138
  lcd_string(&SD_Buffer);
139

140
  // Hauptschleife
141
  while(1){
142
     }
143
     return 0;
144
}
Gast #4470852
Lesenswert?

> #define SS      (1<<PB4)
> #define MOSI       (1<<PB5)
> #define MISO       (1<<PB6)
> #define SCK       (1<<PB7)
> #define SPI_DDR      DDRB
>  SPI_DDR = (1<<MOSI)|(1<<SCK)|(1<<SS);
>  SPI_DDR &= ~(1<<MISO);

Diese Konstruktion verstehe ich nicht, da wird doch doppelt geschoben, 
oder?
#4470938
Lesenswert?

@  Klaus Peter (phoenix89)


>leider immer noch nicht :(

Betreibe eine systematische Fehlersuche. Erstmal muss deine SPI 
Daten, Takt, und SS ausspucken, egal ob die Karte reagiert oder nicht. 
Prüfe das mit einem Oszi oder Logic Analyzer. Dann geht es weiter.

>Hier der Code (eigentlich schön in Header und C-Files ausgegliedert)

Warum kopierst du das dann zusammen anstatt sinnvollerweise die 
einzelnen Dateien als Anhang zu senden? Siehe Netiquette.
#4471025
Lesenswert?

Versuche mal eine der vorhandenen Libs für SD Karten an Deine 
Pinbelegung anzupassen, das wird einfacher sein. Die Ansteuerung ist 
nicht ganz trivial,
und Deine Kommandos funktionieren spätestens bei SDHC Karten so nicht 
mehr.

Deine SD_Command() Funktion würde IMHO im Zweifelsfalle wichtige 
Antwortbytes verschlucken, z.B. beim Lesen von Registern.
(Firma: Swissbit.com) #4471285
Lesenswert?

1
 
2
for(i=0; i<read; i++)
3
        buffer[i] = SPI_Write(0xFF);
4

5
  // Bye!
6
  CS_DISABLE();
7

8
  // response isnt 0xFF, so wait until something else is send
9
  for(i=0; i<read; i++) {
10
    if(buffer[i] != 0xFF)
11
      resp = buffer[i];
12
  }
Der part ist wohl Fehlerhaft. Die Karte muss die Response nicht sofort 
nach dem Command schicken. Es wird aber direkt angefangen die Anzahl 
bytes von "read" zu lesen, ohne zu prüfen ob die Karte tatsächlich Daten 
schickt. Danach wird die Karte unselektiert, der untere Code bewirkt 
also nichts mehr.

Ein korrektes CMD 0 sollte indes ausreichen.

Nachtrag: Habs nochmal angeguckt und jetzt kapiert was hier passieren 
soll.
Bei welchem Command bekommst du denn die Response 0, wo genau bleibt es 
hängen? Response 0 bedeutet eigentlich "OK".
a = SD_Command(_SD_Init, 0x00000000, 0xFF, 8)) != 1

SD_Init bzw. CMD1 SEND_OP_CMD wird dir eine response 01 zurück liefern 
solange die Karte beschäftigt ist, sobald sie bereit für weitere 
Commands ist wird sie eine Response 00 Antworten.
Gruss
OP #4471730
Lesenswert?

Danke für die Antworten.

Habs nun in
1
...
2
  // GO-IDLE-STATE - Try 10 Times
3
  for (i=0; i<10 && (a = SD_Command(_SD_IDLE, 0x00000000, 0x95, 8)) != 1; i++)
4
    _delay_ms(100);
5
  if (i==10)  // Card did not response
6
    return a;
7

8
  // Initialize!
9
  for (i=0; i<10 && (a = SD_Command(_SD_Init, 0x00000000, 0xFF, 8)) != 0; i++)
10
    _delay_ms(100);
11
  if (i==10)  // Card did not come out of idle
12
    return a;
13
...
und resp = 0xFF initialisiert.

Auf dem LCD erscheint nun "255". Heißt, auf dem BUS liegt ständig 
"high".(?)

Hab blöderweise grade kein Logic Analyzer zur Hand und komme auch erst 
nächste Woche an einen ran...
#4474287
Lesenswert?

Falk B. schrieb:
> @  Klaus Peter (phoenix89)
>
>> ... da fehlt doch was! (; am Ende von while..)
>
> Eine der bösen Stolperfallen von C, die man der Sprache zu recht
> anlastet!

Das sehe ich anders. Viele moderne Sprachen wie Java, Javascript, PHP, 
glsl, etc. ermöglichen genau das auch. Nur in alten sprachen wie z.B. 
bash, wo es ein "done" oder basic mit end while, oder die Andersartigen 
wie Python mit Einrückung haben dass nicht. Ich persönlich will die 
Sonderzeichen nicht missen.
OP #4474353
Lesenswert?

Werde mir dann wohl ein {} bei "leeren" Schleifen angewöhnen..

Nun hängts beim Read-Befehl:
1
void SD_read(unsigned long sector, unsigned short offset, unsigned char * buffer, unsigned short length){
2
    uint8_t i;
3
    
4
    CS_ENABLE();
5
    SPI_Write(0x51);
6
    SPI_Write(sector>>15); // sector*512 >> 24
7
    SPI_Write(sector>>7);  // sector*512 >> 16
8
    SPI_Write(sector<<1);  // sector*512 >> 8
9
    SPI_Write(0);          // sector*512
10
    SPI_Write(0xFF);
11

12
  
13
    
14
    for(i=0; i<10 && SPI_Write(0xFF) != 0x00; i++);   // wait for 0
15
    for(i=0; i<10 && SPI_Write(0xFF) != 0xFE; i++);   // wait for data
16
    
17
    for(i=0; i<offset; i++)               // "skip" bytes
18
        SPI_Write(0xFF);
19
        
20
    for(i=0; i<length; i++)                 // read length bytes
21
        buffer[i] = SPI_Write(0xFF);
22
        
23
    for(i+=offset; i<512; i++)               // "skip" again
24
        SPI_Write(0xFF);
25
        
26
    // skip checksum
27
    SPI_Write(0xFF);
28
    SPI_Write(0xFF);    
29

30
    CS_DISABLE();    
31
}

In der Main aufgerufen mit:
1
SD_read(0, 3, &SD_Buffer, 8);

In der angehängten Datei gibts den Verlauf + den ersten Sektor der 
SD-Card.
Was mich verwundert ist, dass das "Gerümpel" immer das gleiche ist, 
jedoch nicht auf der SD-Card zu finden (muss ja aber irgendwo sein?). 
Die letzten 2 Bytes sind CRC und die davor stimmen mit dem was drauf 
sein soll überein..
Angehängte Dateien:
#4474357
Lesenswert?

@  Daniel Abrecht (daniel-a)

>> Eine der bösen Stolperfallen von C, die man der Sprache zu recht
>> anlastet!

>Das sehe ich anders. Viele moderne Sprachen wie Java, Javascript, PHP,
>glsl, etc. ermöglichen genau das auch.

Das macht es nicht besser.

> Nur in alten sprachen wie z.B.
>bash, wo es ein "done" oder basic mit end while, oder die Andersartigen
>wie Python mit Einrückung haben dass nicht. Ich persönlich will die
>Sonderzeichen nicht missen.

Welche Sonderzeichen? Das ;?

In Pascal wäre das nicht passiert, denn dort gibt es IMMER ein begin und 
end; Wenn es in C wengigstens immer die Klammern zwingend gäbe, würde so 
ein (Tipp)fehler nicht passieren. Was gewinnt man effektiv durch das 
Weglassen von ZWEI Klammern?

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