Forum: Mikrocontroller und Digitale Elektronik Problem mit Micro-SD-Karte


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 Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Hallo Allerseits,

ich hab hier schon seit 2 Tagen ein Problem mit einer Micro-SD-Karte.

Die Karte ist von Intenso, 4GB, class 4, nix Besonderes. Das Ganze soll 
auf einem selbstentwickelten Board laufen, ATXmega128A1U, alles 3,3V. An 
CS, DO und den 2 ungenutzten IOs hängt je ein 10K Pull-Up gegen 3,3V. 
Über UART kriege ich meine Debug-Ausgaben. Als Lib habe ich die von ELM 
Chan, die hatte ich schon erfolgreich in einem anderen Projekt benutzt, 
wenn gleich dort mit einem ATmega64. Also flink die Dateien kopiert, die 
Low Level Sachen angepasst, los gehts. Oder auch nicht :-(
Zu Testzwecken öffne ich einfach eine Datei und schreibe dort  ein paar 
Zeichen rein. Das geht aber leider nicht.

Wenn ich nun mehrfach einen Reset am Controller mache, bekomme ich von 
der Funktion f_open() abwechselnd den Fehler

FR_NOT_READY,      /* (3) The physical drive cannot work */

oder

FR_NO_FILESYSTEM,    /* (13) There is no valid FAT volume */

Den SPI-Bus sehe ich auf meinem Logicanalyzer. Beim Fehler 3 schlägt 
schon die elementare Initialisierung mit 200kHz SPI-Takt fehl. Bei 
Fehler 13 funktioniert sie, die Lib schaltet auf vollen SPI Takt von 8 
MHz um und will dann weiter machen. Dort gibt es scheinbar nur ein 
Kommando, das liefert einmal eine 0, dann viele 0xFF. Dann wird ein 
Blockzugriff gemacht (sieht man am lückenlosen SPI-Takt), komischerweise 
werden dort aber nur ~160 Byte gelesen, wobei ALLE gelesenen Daten 0 
sind! Sehr mehrwürdig! Zur Initialisierung des FAT muss doch erstmal der 
Bootsektor gelesen werden, das sieht man auch im Code von ff.c. Aber 
soweit kommt es scheinbar nicht :-(

Hat irgendjemand eine Idee?
Ich habe schon mal 33 Ohm in die SCK Leitung zur SD-Karte eingelötet, 
ich vermutete schlechte Signalflangen, auch wenn das eher 
unwahrlscheinlich ist (Die Platine hat 4 Lagen und eine vollständige 
Massefläche). 100nF sind auch ca. 5mm von der Karte entfernt an VCC/GND. 
Im Anhang mal der SPI-Datenverkehr. Ab 16.52ms geht der High Speed 
Verkehr los.

MFG
Falk

: Bearbeitet durch User
von Markus M. (adrock)


Lesenswert?

Kann es sein, dass es ein Problem mit der CS Leitung gibt und sich die 
Karte manchmal garnicht angesprochen fühlt?

Es gibt ja an ein paar Stellen im Code wo er einen Softwaretimer (aus 
ISR und Zähler) verwendet. Hast Du das korrekt übernommen oder wird da 
vlt. was wegoptimiert? Mal die Optimierung ausgeschaltet?

Musst Du bei Deiner Lowlevel-Implementierung den SPI zwischen 8 und 16 
Bit umschalten? (habe jetzt nicht mehr im Kopf ob der XMega 16-Bit SPI 
kann). Das hatte bei meiner STM32F0 Anpassung für Probleme gesorgt.

von Jim M. (turboj)


Lesenswert?

Der Dump (SPI.TXT) sieht 100%ig OK aus, bis der Code den Lesevorgang 
abbricht.

Der Sektor 0 einer SD-Karte enthält normalerweise keinen Bootloader, und 
die Partitionstabelle steht sehr weit hinten. Dass Du da 160 mal "00" 
bekommtst, ist also normal!

Unter Windoof bekommt man diesen Sektor nur mit Admin-Rechten zu sehen, 
viele Tools zeigen Dir nur den 1. Sektor der FAT Partition an, selbst 
dann wenn sie Admin Rechte haben.

Das Lesekommando sieht gut aus:
1
1652993 (16.53ms) MISO:MOSI  0xFF ÿ : 0x51 Q  ; CMD17
2
1653273 (16.53ms) MISO:MOSI  0xFF ÿ : 0x00    ;
3
1653542 (16.54ms) MISO:MOSI  0xFF ÿ : 0x00    ;
4
1653823 (16.54ms) MISO:MOSI  0xFF ÿ : 0x00    ;
5
1654103 (16.54ms) MISO:MOSI  0xFF ÿ : 0x00    ; Sektor Null
6
1654384 (16.54ms) MISO:MOSI  0xFF ÿ : 0x01    ; Dummy CRC
7
1654677 (16.55ms) MISO:MOSI  0xFF ÿ : 0xFF ÿ  ; ...
8
1654970 (16.55ms) MISO:MOSI  0x00   : 0xFF ÿ  ; ReturnCode 0 = OK
9
1655263 (16.55ms) MISO:MOSI  0xFF ÿ : 0xFF ÿ  ; warten ...
10
...
11
1719550 (17.20ms) MISO:MOSI  0xFE þ : 0xFF ÿ  ; Start Data Token
12
1719843 (17.20ms) MISO:MOSI  0x00   : 0xFF ÿ  ; ab hier gibts die Daten
13
...
14
1738970 (17.39ms) MISO:MOSI  0x00   : 0xFF ÿ

Die SD Karte bestätigt das Lesekommando schon nach dem ersten Dummy Byte 
mit 0x00 = OK. Dann braucht sie ca. 0.7 ms, bis die Daten zur Verfügung 
stehen - es folgt das "Start Data" Token 0xFE. Alles weitere sind die 
Daten, die im Auslieferzustand 0x00 enthalten - die Partitionstabelle 
beginnt erst ab Byte 446, die Übertragung endet vorher.

Dass es danach abbricht, ist vermutilch ein Fehler im AVR Code. Schau 
mal nach den Interrupts - und ob deren Handler existieren - oder dem 
Watchdog.

: Bearbeitet durch User
von grundschüler (Gast)


Lesenswert?

zur Eingrenzung des Fehlers bietet sich chan-foolproof an. Damit kann 
man Fehler an der Hardware feststellen oder eingrenzen.

von Falk B. (falk)


Lesenswert?

@ Markus M. (adrock)

>Kann es sein, dass es ein Problem mit der CS Leitung gibt und sich die
>Karte manchmal garnicht angesprochen fühlt?

Nein, das Signal ist OK.

>Es gibt ja an ein paar Stellen im Code wo er einen Softwaretimer (aus
>ISR und Zähler) verwendet. Hast Du das korrekt übernommen

Ja, das habe ich geprüft, mittel Testpin. Der Interrupt läuft und ruft 
alle 10ms die Funktion disk_timerproc() auf,

>>vlt. was wegoptimiert? Mal die Optimierung ausgeschaltet?

Geht nicht, ich hab diverse _delay_ms() im Code.

>Musst Du bei Deiner Lowlevel-Implementierung den SPI zwischen 8 und 16
>Bit umschalten?

Nein, der ATXmega kann nur 8 Bit. Es wird UARTE1 im SPI-Modus verwendet.

von Dieter F. (Gast)


Lesenswert?

Falk Brunner schrieb:
> SPI Takt von 8
> MHz

Ne ganz doofe Frage - kann die Micro-SD-Karte so schnell liefern? Da 
gibt es doch Geschwindigkeits-Klassen, oder? Der Code wäre auch ganz 
interessant :-)

von Falk B. (falk)


Lesenswert?

@Jim Meba (turboj)

>Der Dump (SPI.TXT) sieht 100%ig OK aus, bis der Code den Lesevorgang
>abbricht.

Eben, SEHR seltsam!

>Der Sektor 0 einer SD-Karte enthält normalerweise keinen Bootloader, und
>die Partitionstabelle steht sehr weit hinten. Dass Du da 160 mal "00"
>bekommtst, ist also normal!

Nein. Auch phne Bootloader kommt die Kennung FAT32 recvht weit am Anfang 
an Offset 82, das müsste man sehen. Ausserdem ist dort noch diverses 
anderes "Gemüse" Verstreut, das man im HEX-Editor sieht.

>Unter Windoof bekommt man diesen Sektor nur mit Admin-Rechten zu sehen,

Ist bekannt, schaue ich mit als Admin an, mit WinHex.

>Das Lesekommando sieht gut aus:

>1652993 (16.53ms) MISO:MOSI  0xFF ÿ : 0x51 Q  ; CMD17
>1653273 (16.53ms) MISO:MOSI  0xFF ÿ : 0x00    ;

Ja,k hab ich mittlerweile auch bis dahin verfolgt. Dann folgen viele 
Polling-Zyklen, bis die Karte mit 0xFE antwortet. Danach wird es 
mysteriös. Eigenlich sollte dann DIREKT der Blocklesezugriff folgen, 
dabei ist der SPI-Takt lückenlos. Auf dem Logicanalyzer sieht man aber 
noch drei Bytes mit Lücken. Das kann nicht sein!

>Die SD Karte bestätigt das Lesekommando schon nach dem ersten Dummy Byte
>mit 0x00 = OK. Dann braucht sie ca. 0.7 ms, bis die Daten zur Verfügung
>stehen - es folgt das "Start Data" Token 0xFE. Alles weitere sind die
>Daten, die im Auslieferzustand 0x00 enthalten

Nein, da seht diverses Zeug drin. Ausserdem hab ich die Karte nochmal 
mit Windows formatiert, dort hat das dämliche System einen Bootloader 
draufgeschrieben. Warum auch immer. Aber davon sehe ich rein gar nichts 
im Datenstrom!

>- die Partitionstabelle
>beginnt erst ab Byte 446, die Übertragung endet vorher.

>Dass es danach abbricht, ist vermutilch ein Fehler im AVR Code. Schau
>mal nach den Interrupts - und ob deren Handler existieren - oder dem
>Watchdog.

Es ist nur ein Interrupt aktiv, ein Timer. Der läuft normal. Wenn es 
einen Reset geben würde, würde ich das im Terminal sehen 
(Debugausschrift nach Reset).

Aus irgend einem Grund, wird diese Funktion nicht korrekt umgesetzt!
Sie wird, da nur einmalig genutzt, per Inline eingefügt.
1
static
2
void rcvr_spi_multi (
3
  BYTE *p,  /* Data buffer */
4
  UINT cnt  /* Size of data block */
5
)
6
{
7
    BYTE tmp;
8
9
    USARTE1.STATUS = USART_TXCIF_bm;        // clear TXC
10
    USARTE1.DATA = 0xFF;
11
    cnt--;
12
    for (; cnt != 0; cnt--) {
13
        while(!(USARTE1.STATUS & USART_TXCIF_bm));
14
        tmp = USARTE1.DATA;
15
        USARTE1.DATA = 0xFF;
16
        *p++=tmp;
17
    }
18
    while(!(USARTE1.STATUS & USART_TXCIF_bm));
19
    *p++=USARTE1.DATA;
20
}

von Falk B. (falk)


Lesenswert?

@ Dieter Frohnapfel (jim_quakenbush)

>> SPI Takt von 8
>> MHz

>Ne ganz doofe Frage - kann die Micro-SD-Karte so schnell liefern?

Kann sie, weil JEDE SD-Karte 25 MHz Takt verkraftet. Class 4 heißt, 
4MB/s (nicht im SPI-Modus, aber mit 4 Bit SDIO).

>gibt es doch Geschwindigkeits-Klassen, oder? Der Code wäre auch ganz
>interessant :-)

Nö, der funktioniert ja problemlos auf dem alten Board. Es ist der voM 
Meister ELM Chan.

http://elm-chan.org/fsw/ff/00index_e.html

von Falk B. (falk)


Lesenswert?

Hier mal der Ausschnit aus dem .lss File. Wenn gleich der Compilier ein 
paar Verwürfelungen (Optimierung -Os) macht, ist der Code FAST korrekt. 
Aber das einmalig cnt-- wird verschluckt! Oder sehe ich es nicht?

Edit: OK, ich sehe es. Es wird mit 1 verglichen, nicht 0 
(Schleifenabbruch)

Dennoch darf der Code nicht nach ~160 Byte abbrechen! Er wird mit 512 
Byte = 1 Sektor aufgerufen!
1
00002952 <rcvr_datablock>:
2
static
3
int rcvr_datablock (
4
  BYTE *buff,      /* Data buffer to store received data */
5
  UINT btr      /* Byte count (must be multiple of 4) */
6
)
7
{
8
    2952:  0f 93         push  r16
9
    2954:  1f 93         push  r17
10
    2956:  cf 93         push  r28
11
    2958:  df 93         push  r29
12
    295a:  ec 01         movw  r28, r24
13
    295c:  8b 01         movw  r16, r22
14
  BYTE token;
15
16
17
  Timer1 = 20;
18
    295e:  84 e1         ldi  r24, 0x14  ; 20
19
    2960:  80 93 b9 20   sts  0x20B9, r24
20
  do {              /* Wait for data packet in timeout of 200ms */
21
    token = xchg_spi(0xFF);
22
    2964:  8f ef         ldi  r24, 0xFF  ; 255
23
    2966:  71 df         rcall  .-286      ; 0x284a <xchg_spi>
24
  } while ((token == 0xFF) && Timer1);
25
    2968:  8f 3f         cpi  r24, 0xFF  ; 255
26
    296a:  29 f4         brne  .+10       ; 0x2976 <rcvr_datablock+0x24>
27
    296c:  80 91 b9 20   lds  r24, 0x20B9
28
    2970:  81 11         cpse  r24, r1
29
    2972:  f8 cf         rjmp  .-16       ; 0x2964 <rcvr_datablock+0x12>
30
    2974:  2c c0         rjmp  .+88       ; 0x29ce <rcvr_datablock+0x7c>
31
  if (token != 0xFE) return 0;  /* If not valid data token, return with error */
32
    2976:  8e 3f         cpi  r24, 0xFE  ; 254
33
    2978:  51 f5         brne  .+84       ; 0x29ce <rcvr_datablock+0x7c>
34
  UINT cnt  /* Size of data block */
35
)
36
{
37
    BYTE tmp;
38
39
    USARTE1.STATUS = USART_TXCIF_bm;        // clear TXC
40
    297a:  80 e4         ldi  r24, 0x40  ; 64
41
    297c:  80 93 b1 0a   sts  0x0AB1, r24
42
    USARTE1.DATA = 0xFF;
43
    2980:  8f ef         ldi  r24, 0xFF  ; 255
44
    2982:  80 93 b0 0a   sts  0x0AB0, r24
45
    cnt--;
46
    2986:  c8 01         movw  r24, r16
47
    2988:  01 97         sbiw  r24, 0x01  ; 1
48
    298a:  fe 01         movw  r30, r28
49
    298c:  b8 01         movw  r22, r16
50
    for (; cnt != 0; cnt--) {
51
        while(!(USARTE1.STATUS & USART_TXCIF_bm));
52
        tmp = USARTE1.DATA;
53
        USARTE1.DATA = 0xFF;
54
    298e:  2f ef         ldi  r18, 0xFF  ; 255
55
    BYTE tmp;
56
57
    USARTE1.STATUS = USART_TXCIF_bm;        // clear TXC
58
    USARTE1.DATA = 0xFF;
59
    cnt--;
60
    for (; cnt != 0; cnt--) {
61
    2990:  61 30         cpi  r22, 0x01  ; 1
62
    2992:  71 05         cpc  r23, r1
63
    2994:  61 f0         breq  .+24       ; 0x29ae <rcvr_datablock+0x5c>
64
        while(!(USARTE1.STATUS & USART_TXCIF_bm));
65
    2996:  30 91 b1 0a   lds  r19, 0x0AB1
66
    299a:  36 ff         sbrs  r19, 6
67
    299c:  fc cf         rjmp  .-8        ; 0x2996 <rcvr_datablock+0x44>
68
        tmp = USARTE1.DATA;
69
    299e:  30 91 b0 0a   lds  r19, 0x0AB0
70
        USARTE1.DATA = 0xFF;
71
    29a2:  20 93 b0 0a   sts  0x0AB0, r18
72
        *p++=tmp;
73
    29a6:  31 93         st  Z+, r19
74
    29a8:  61 50         subi  r22, 0x01  ; 1
75
    29aa:  71 09         sbc  r23, r1
76
    29ac:  f1 cf         rjmp  .-30       ; 0x2990 <rcvr_datablock+0x3e>
77
    29ae:  c8 0f         add  r28, r24
78
    29b0:  d9 1f         adc  r29, r25
79
    }
80
    while(!(USARTE1.STATUS & USART_TXCIF_bm));
81
    29b2:  80 91 b1 0a   lds  r24, 0x0AB1
82
    29b6:  86 ff         sbrs  r24, 6
83
    29b8:  fc cf         rjmp  .-8        ; 0x29b2 <rcvr_datablock+0x60>
84
    *p++=USARTE1.DATA;
85
    29ba:  80 91 b0 0a   lds  r24, 0x0AB0
86
    29be:  88 83         st  Y, r24
87
    token = xchg_spi(0xFF);
88
  } while ((token == 0xFF) && Timer1);
89
  if (token != 0xFE) return 0;  /* If not valid data token, return with error */
90
91
  rcvr_spi_multi(buff, btr);    /* Receive the data block into buffer */
92
  xchg_spi(0xFF);          /* Discard CRC */
93
    29c0:  8f ef         ldi  r24, 0xFF  ; 255
94
    29c2:  43 df         rcall  .-378      ; 0x284a <xchg_spi>
95
  xchg_spi(0xFF);
96
    29c4:  8f ef         ldi  r24, 0xFF  ; 255
97
    29c6:  41 df         rcall  .-382      ; 0x284a <xchg_spi>
98
    29c8:  81 e0         ldi  r24, 0x01  ; 1
99
    29ca:  90 e0         ldi  r25, 0x00  ; 0
100
    29cc:  02 c0         rjmp  .+4        ; 0x29d2 <rcvr_datablock+0x80>
101
102
  Timer1 = 20;
103
  do {              /* Wait for data packet in timeout of 200ms */
104
    token = xchg_spi(0xFF);
105
  } while ((token == 0xFF) && Timer1);
106
  if (token != 0xFE) return 0;  /* If not valid data token, return with error */
107
    29ce:  80 e0         ldi  r24, 0x00  ; 0
108
    29d0:  90 e0         ldi  r25, 0x00  ; 0
109
  rcvr_spi_multi(buff, btr);    /* Receive the data block into buffer */
110
  xchg_spi(0xFF);          /* Discard CRC */
111
  xchg_spi(0xFF);
112
113
  return 1;            /* Return with success */
114
}
115
    29d2:  df 91         pop  r29
116
    29d4:  cf 91         pop  r28
117
    29d6:  1f 91         pop  r17
118
    29d8:  0f 91         pop  r16
119
    29da:  08 95         ret

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Falk Brunner schrieb:

>>Der Sektor 0 einer SD-Karte enthält normalerweise keinen Bootloader, und
>>die Partitionstabelle steht sehr weit hinten. Dass Du da 160 mal "00"
>>bekommtst, ist also normal!
>
> Nein. Auch phne Bootloader kommt die Kennung FAT32 recvht weit am Anfang
> an Offset 82

Quatsch. Das ist ein FAT-Bootsektor. So weit vorne kommt der nur, wenn 
die Karte als "Superfloppy" formatiert ist. Ab Werk sind die Dinger aber 
normalerweise als HD formatiert, vor der ersten Partition kommt also 
mindestens der MBR in der Größe eines Sektors.

Typisch ist sogar, dass ein kompletter "Track" reserviert ist (hier 
natürlich nicht wirklich ein Track im Sinne einer Spur einer HD, sondern 
oft genau die Größe eines Löschblocks des verbauten Flash [oder 
Flashcontrollers???]).

Was übrigens Wixdos macht, ist bei Wechselmedien eh' absolut 
grenzwertig. Obwohl darauf lt. aller jeweiligen Standards immer auch 
HD-Strukturen zumindest zulässig sind, ist Wixdos nicht in der Lage, 
damit korrekt umzugehen. Nur bei "Superfloppy"-Formatierung sieht es 
das, was wirklich dort ist. Ansonsten nur die erste Partition. Der 
NT-Kernel selber sieht aber natürlich alles korrekt. Deshalb muß man 
nicht nur Admin sein, sondern auch noch auf's richtige Devicefile 
zugreifen, um selber alles zu sehen...

Lustige Effekte aus dieser schwachsinnigen Trennung in das, was 
Microsoft will, was der User sieht und das, was wirklich ist, kann man 
erzielen, wenn man in der sog. "Datenträgerverwaltung" mal ein wenig mit 
Datenträgern spielt, die eine HD-Struktur mit mehr als einer Partition 
aufweisen. Die dabei generierten Fehlermeldungen füllen ein komplettes 
Realsatire-Magazin...

Noch extremer und wesentlich weniger lustig wird es, wenn man mit 
bootfähigen Windows-Installationen auf solchen Wechseldatenträgern 
gezwungenermaßen tatsächlich hantieren muß...

Nö, der (richtig gute) NT-Kernel hat wirklich besseres als den völlig 
beschissenen Windows-Aufsatz verdient...

von Falk B. (falk)


Lesenswert?

@ c-hater (Gast)

>> Nein. Auch phne Bootloader kommt die Kennung FAT32 recvht weit am Anfang
>> an Offset 82

>Quatsch. Das ist ein FAT-Bootsektor. So weit vorne kommt der nur, wenn
>die Karte als "Superfloppy" formatiert ist. Ab Werk sind die Dinger aber
>normalerweise als HD formatiert, vor der ersten Partition kommt also
>mindestens der MBR in der Größe eines Sektors.

Stimmt, da hatte ich mich wohl vertan.

>aufweisen. Die dabei generierten Fehlermeldungen füllen ein komplettes
>Realsatire-Magazin...

Wenn das mal nicht die Männer mit der Kalaschnikov hören . . .

von Falk B. (falk)


Lesenswert?

OK,, es geht voran!

Es war noch ein Fehler in den IO-Funktionen. Der UART im SPI-Modus ist 
halt doch ein wenig anders als das normale SPI-Modul, vor allem die 
FIFOs machen da ein wenig Stress, wenn man nicht aufpasst.
1
/*-----------------------------------------------------------------------*/
2
/* Transmit/Receive data from/to MMC via SPI  (Platform dependent)       */
3
/*-----------------------------------------------------------------------*/
4
5
/* Exchange a byte */
6
static
7
BYTE xchg_spi (    /* Returns received data */
8
  BYTE dat    /* Data to be sent */
9
)
10
{
11
    USARTE1.DATA;      // clear old data
12
    USARTE1.DATA;
13
    USARTE1.DATA;
14
    
15
    USARTE1.DATA = dat;
16
    while(!(USARTE1.STATUS & USART_RXCIF_bm));
17
    return USARTE1.DATA;
18
}
19
20
/* Send a data block fast */
21
static
22
void xmit_spi_multi (
23
  const BYTE *p,  /* Data block to be sent */
24
  UINT cnt    /* Size of data block */
25
)
26
{
27
    BYTE tmp;
28
        
29
    USARTE1.STATUS = USART_TXCIF_bm;        // clear TXC
30
    USARTE1.DATA = *p++;
31
    cnt--;
32
    for (; cnt>0; cnt--) {
33
        tmp = *p++;
34
        while(!(USARTE1.STATUS & USART_DREIF_bm));
35
        USARTE1.DATA = tmp;
36
    }
37
    while(!(USARTE1.STATUS & USART_TXCIF_bm));
38
    
39
    USARTE1.DATA;      // clear rx data buffer
40
    USARTE1.DATA;
41
    USARTE1.DATA;
42
}
43
44
45
/* Receive a data block fast */
46
static
47
void rcvr_spi_multi (
48
  BYTE *p,  /* Data buffer */
49
  UINT cnt  /* Size of data block */
50
)
51
{
52
    BYTE tmp;
53
54
    USARTE1.DATA;      // clear rx data buffer
55
    USARTE1.DATA;
56
    USARTE1.DATA;
57
58
    USARTE1.DATA = 0xFF;
59
    cnt--;
60
    for (; cnt != 0; cnt--) {
61
        while(!(USARTE1.STATUS & USART_DREIF_bm));
62
        USARTE1.DATA = 0xFF;
63
        while(!(USARTE1.STATUS & USART_RXCIF_bm));
64
        tmp = USARTE1.DATA;
65
        *p++ = tmp;
66
    }
67
    while(!(USARTE1.STATUS & USART_RXCIF_bm));
68
    *p++ = USARTE1.DATA;
69
}


Mit diesen Funktionen kann ich schon mal Dateien lesen!!!
Aber beim Schreiben geht noch was schief!. Öffnen zum Schreibzugriff 
geht, aber bei f_write() kommt eine Rückgabewert von 1 (FR_DISK_ERR, 
/* (1) A hard error occurred in the low level disk I/O layer */) zurück, 
die Anzahl geschriebener Bytes ist auch Null. Dumm nur, dass dieser 
Fehler an ziemlich vielen Stellen der Funktion zurückgegeben werden 
kann. Einen In Circuit Debugger hab ich auch nicht, nur das gute, alte 
AVR ISP MK II 8-0

von noreply@noreply.com (Gast)


Lesenswert?

Ich habe eine defekte Micro-SD-Karte, die mich unter Linux und einen 
MP3-Player zur Verzweifelung gebracht hat. Lesen ist noch möglich. Beim 
Schreiben tut das Betriebssystem so als ob. Danach ist der alte Inhalt 
der Micro-SD-Karte wieder sichtbar.

Nicht das es an einer defekten SD-Karte liegt,. ;-)

von Falk B. (falk)


Lesenswert?

@ noreply@noreply.com (Gast)

>Nicht das es an einer defekten SD-Karte liegt,. ;-)

Nein, die ist OK, hab ich gerade probiert. Man kann neue Dateien anlegen 
und lesen. Ausserdem habe ich es auch mit einer anderen SD-Karte 
probiert, gleiches Ergebnis. Der Dateiname wird angelegt, der inhalt 
aber nicht geschrieben :-(

Soweit ich es im Moment eingrenzen kann, scheint die Zuweisung des neuen 
(ersten) Clusters für die Datei schief zu gehen! Wie kann das sein? Die 
Karte ist nahezu leer!

von Falk B. (falk)


Lesenswert?

Also, das Problem ist im Moment, dass bei f_open() KEIN Fehler zurück 
kommt, die Datei wird auch im Hauptverzeichnis angelegt (kann man am PC 
sehen), aber der Schreibvorgang f_write() bricht ab, weil scheinbar kein 
Cluster für die Datei verfügbar ist. Sehr merkwürdig.

Ich hab mir mal das ATMELICE bestellt, ich hoffe der Debugger wirft 
deutlich schneller Licht auf die Sache als meine print_f Orgie.

von Falk B. (falk)


Lesenswert?

So, mittels ATMELICE hab ich mich jetzt weiter zum Problem durchgewühlt. 
Wie es scheint, gibt es ein Problem beim Schreiben der FAT auf die 
SD-Karte. Wenn mittels CMD24 der FAT-Sektor 0 zurüchgeschieben werden 
soll, kommt als Anwort der Karte 0xFF, machmal auch 0,1F, wo aber 0x05 
erwartet wird. Scheibenhonig!
Merkwürdig ist, dass das Directory mit dem erstellten Eintrag der neuen 
Datei aber beschrieben wird! Häää??

Allerdings sind die Signale auf dem Logicanalyzer nicht ganz plausibel. 
Am Ende der Übertragung werden 2x 0xFF als Dummybytes für die CRC 
gesendet, danach nochmal 0xFF um die Antwort der SD-Karte zu bekommen. 
Aber diese Einzelsendungen müssten auch zeitlich etwas mit Abstand 
erkennbar sein. Sind sie aber nicht! Der SPI-Takt ist lückenlos und hört 
dann auf! Mysteriös!

von Falk B. (falk)


Lesenswert?

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHH

Ich Depp!

Ich hab den Fehler gefunden! Er lag natürlich mal wieder vor der 
Tastatur!

"Also flink die Dateien kopiert, die Low Level Sachen angepasst, los 
gehts. Oder auch nicht :-("

Denkste! Bei der Schreibroutine hab ich mir, wie bereits bei der 
Empfangsroutione sauber ins Knie geschossen! Und warum?

Wegen des FIFOs!! (Genitiv ins Wasser weil Dativ)

Beitrag "Re: Problem mit Micro-SD-Karte"

Ich dachte, lösch doch einfach das TXC Flag vor der Übertragung, die 
leiert dann schön durch und am Ende wird TXC gesetzt. FALSCH! TXC wird 
ZWISCHENDURCH gesetzt! Warum? Weil die Übertragung des Datenblocks durch 
Interrupts unterbrochen wird, dadurch läuft der FIFO zwischenzeitlich 
leer. Jaja, man hätte DMA nehmen können, die bringt hier aber wenig, 
weil die CPU sowieso warten muss. Ok, also muss man TXC am ENDE der 
Übertragung VOR dem Senden des letzten Bytes löschen und zwar ATOMAR! 
Warum? Wenn zwischen das Löschen von TXC und das Senden des letzten 
Bytes ein Interrupt dazwischenfunkt, wird TXC gelöscht (von vorherigen 
Sendepausen), die aktuelle Übertragung mit vollem FIFO läuft noch, die 
ISR blockiert das Laden des letzten Bytes, der FIFO läuft leer und setzt 
TXC wieder! Nach der Rückkehr der ISR wird das letzte Byte gesendet, die 
Warteschleife geht aber sofort weiter, weil TXC ja gesetzt ist! Damit 
holt man sich Probleme im weiteren Ablauf der FAT-Lib! Denn die macht 
danach noch Einzelübertragungen. Die funktionieren aber nur, wenn die 
vorherige Blockübertragung VOLLSTÄNDIG abgeschlossen ist!

OMG! Das hat mich jetzt $("§&$%")!!! Tage gekostet!!!

SCHEIIIIIIIIBENHONG!!!

Damit sich die Nachwelt diesen Krampf erspart, hier die Routinen, die 
sollten jetzt WIRKLICH passen. Einzufügen in mmc.c
1
// oben einfügen
2
#include <util/atomic.h>
3
4
/* Exchange a byte */
5
static
6
BYTE xchg_spi (    /* Returns received data */
7
  BYTE dat    /* Data to be sent */
8
)
9
{
10
    USARTE1.DATA;      // clear old data
11
    USARTE1.DATA;
12
    USARTE1.DATA;
13
    
14
    USARTE1.DATA = dat;
15
    while(!(USARTE1.STATUS & USART_RXCIF_bm));
16
    return USARTE1.DATA;
17
}
18
19
/* Send a data block fast */
20
static
21
void xmit_spi_multi (
22
  const BYTE *p,  /* Data block to be sent */
23
  UINT cnt    /* Size of data block */
24
)
25
{
26
    BYTE tmp;
27
    
28
    cnt--;
29
    for (; cnt>0; cnt--) {
30
        tmp = *p++;
31
        while(!(USARTE1.STATUS & USART_DREIF_bm));
32
        USARTE1.DATA = tmp;
33
    }
34
35
    // special handling of last byte due to FIFO
36
    // and interrupts, which can set TXCIF in between a transfer!
37
    tmp = *p++;
38
    while(!(USARTE1.STATUS & USART_DREIF_bm));
39
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
40
        USARTE1.STATUS = USART_TXCIF_bm;            // clear TXC
41
        USARTE1.DATA = tmp;        
42
    }
43
    while(!(USARTE1.STATUS & USART_TXCIF_bm));
44
45
    USARTE1.DATA;      // clear rx data buffer
46
    USARTE1.DATA;
47
    USARTE1.DATA;
48
}
49
50
51
/* Receive a data block fast */
52
static
53
void rcvr_spi_multi (
54
  BYTE *p,  /* Data buffer */
55
  UINT cnt  /* Size of data block */
56
)
57
{
58
    BYTE tmp;
59
60
    USARTE1.DATA;      // clear rx data buffer
61
    USARTE1.DATA;
62
    USARTE1.DATA;
63
64
    USARTE1.DATA = 0xFF;
65
    cnt--;
66
    for (; cnt != 0; cnt--) {
67
        while(!(USARTE1.STATUS & USART_DREIF_bm));
68
        USARTE1.DATA = 0xFF;
69
        while(!(USARTE1.STATUS & USART_RXCIF_bm));
70
        tmp = USARTE1.DATA;
71
        *p++ = tmp;
72
    }
73
    while(!(USARTE1.STATUS & USART_RXCIF_bm));
74
    *p++ = USARTE1.DATA;
75
}

von Falk B. (falk)


Lesenswert?

Kleine Ergänzung. So ist es besser in xmit_spi_multi.
1
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
2
        USARTE1.DATA = tmp;        
3
        USARTE1.STATUS = USART_TXCIF_bm;            // clear TXC
4
    }

Denn damit ist ABSOLUT sicher, dass TXC gelöscht wird. Denn theoretisch 
besteht die Möglichkeit, dass zwischen dem Schreibzugriff auf .STATUS 
und .DATA das TXC Flag schon wieder gesetzt ist, auch wenn dort nur 
wenige Takte dazwischen liegen.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Aber was passiert nun, wenn zwischen dem Daten schreiben und dem 
TXC-Flag löschen ein Interrupt auftritt, der länger als die Bytelaufzeit 
ist? Dann löschst Du ein frisch gesetztes TXC-Flag und das wird dann nie 
wieder gesetzt. Ich denke vorher war´s besser.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite

>Aber was passiert nun, wenn zwischen dem Daten schreiben und dem
>TXC-Flag löschen ein Interrupt auftritt, der länger als die Bytelaufzeit
>ist?

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
}

> Dann löschst Du ein frisch gesetztes TXC-Flag und das wird dann nie
>wieder gesetzt. Ich denke vorher war´s besser.

Nö.

von Olaf (Gast)


Lesenswert?

Ein kleiner Tip der sich bei mir bewaehrt hat:

So frueh wie moeglich auch fuer den SPI-Modus die CRC-Datensicherung 
einschalten und danach dann alles mit Pruefsummen uebertragen und 
auswerten. Das macht den Coder erheblich robuster gegen Probleme aller 
Art.

Olaf

von Falk B. (falk)


Lesenswert?

Die Elm Chan Lib arbeitet aber ohne CRC.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.