Forum: Mikrocontroller und Digitale Elektronik SD-Karte formatieren(f_mkfs) geht nur mit 4096 Bytes großem Puffer


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 Felix N. (felix_n888)


Lesenswert?

Moin,

Für mein Projekt setze ich neben einem fest verbauten FLASH Baustein 
eine SD Karte ein um Daten zu speichern. Da es dem Nutzer relativ 
einfach gemacht werden soll habe ich vor eine SD-Formatierungs Funktion 
in mein Projekt mit einzubauen, so das man die SD Karte nicht vorher am 
PC formatieren muss.

Elm Chan's FatFs liefert dafür ja eine passende Funktion: f_mkfs. Ich 
nutze FatFs in der Version 0.14b.

Zum Testen bin ich nach dem Beispielcode von Elm Chan 
vorgegangen(http://elm-chan.org/fsw/ff/doc/mkfs.html). Die Funktion 
f_mkfs benötigt ein Arbeitspuffer welcher dort mit FF_MAX_SS belegt 
wird. Nach der Beschreibung wird zwar gesagt das der Puffer ruhig größer 
sein darf als FF_MAX_SS da dann weniger Schreibzugriffe auf der Karte 
stattfinden und der Formatierungsprozess auch von kürzer Dauer ist. 
Jedoch müsste der Puffer mindestens FatFs Sektorgröße haben(FF_MAX_SS) 
somit sollte das mit FF_MAX_SS auch funktionieren. Tut es aber nicht.

Okay nicht immer sagen wir mal in 20 % der Fällen schlägt f_mkfs fehl 
mit dem Fehlercode 1(FR_DISK_ERR). Auch vergrößern des Puffers anstatt 
FF_MAX_SS(Bei mir auf 512 in der ffconf.h gesetzt) auf 1023 oder 2048 
schlägt in der Regel immer noch fiel. Erst mit 4096 Bytes funktionierte 
es bisher immer(5 Formatierungen durchgeführt).

Ich habe jetzt 20K RAM auf meinem STM32F103CBT6 von daher theoretisch 
kein Problem jedoch sollte das ganze ja auch mit 512 Bytes als Puffer 
funktionieren nur halt dann länger dauern.

Die SD-Karte ist nachdem FR_DISK_ERR zurückgeworfen wird unbrauchbar bis 
man sie wieder richtig in FAT32 am PC formatiert. Die Dateien werden 
wohl noch am PC angezeigt man aber auf sie nicht zugreifen oder neue 
Dateien einfügen das quittiert Windows dann immer mit einem Fehler.

Hier mein Formatierungscode:
1
uint8_t formatSDCard(void) {
2
  if(!useSD) return 99;                        //Only available if an SD card is inserted
3
4
  if(f_unmount("") != FR_OK) return 98;                //Unmount the current SD work area. SD is already mounted and initialized
5
  FRESULT res = f_mkfs("", 0, formatBuffer, sizeof(formatBuffer));  //Format the SD card to FAT32
6
  if(res != FR_OK) {
7
    return res;                            //If f_mkfs fails to format the SD Card the error code of f_mkfs will returned here
8
  }
9
10
  return initSD();                          //Initialize the SD Card again for the system.
11
}

Der Puffer "formatBuffer" ist wie folgt definiert:
1
BYTE formatBuffer[4096] __attribute__ ((aligned (4))) ;  /* Working buffer */

So ist der Puffer auch in dem STM32 FatFs Examples 
angelegt(ffsamples.zip). Mit 4096 Bytes funktioniert das Formatieren bis 
jetzt immer. Mit FF_MAX_SS sollte es laut Beschreibung von der Elm Chan 
Webseite auch gehen tut es aber nicht.

Hat jemand ne Ahnung warum das nur mit 4096 Bytes funktioniert oder hat 
jemand die f_mkfs Funktion schonmal benutzt. Die restlichen Funktionen 
von FatFs funktionieren immer bisher ohne Probleme. Angesprochen wird 
die SD Karte vom STM32 mit 18MHz Takt. Als SD-Karte kommt im Moment eine 
SanDisk Ultra 32GB microSD SDHC Karte zum Einsatz

Mfg

von c-hater (Gast)


Lesenswert?

Felix N. schrieb:

> Hat jemand ne Ahnung warum das nur mit 4096 Bytes funktioniert oder hat
> jemand die f_mkfs Funktion schonmal benutzt. Die restlichen Funktionen
> von FatFs funktionieren immer bisher ohne Probleme. Angesprochen wird
> die SD Karte vom STM32 mit 18MHz Takt. Als SD-Karte kommt im Moment eine
> SanDisk Ultra 32GB microSD SDHC Karte zum Einsatz

Versteh' ich nicht. Du hast die Quelltexte und einen Controller mit 
einer guten Debugschnittstelle. Warum also steppst du nicht einfach mal 
in die Funktion rein und schaust, wo genau sie scheitert?

von Jim M. (turboj)


Lesenswert?

Felix N. schrieb:
> Okay nicht immer sagen wir mal in 20 % der Fällen schlägt f_mkfs fehl

Dann dekoriere mal die Disk Lese und Schreibfunktion so dass Du SD 
Karten Fehler mitbekommst.

Bei diesem Verhalten ist nämlich eher die SD Karte selber das Problem.

von mIstA (Gast)


Lesenswert?

Felix N. schrieb:
> Okay nicht immer sagen wir mal in 20 % der Fällen schlägt
> f_mkfs fehl mit dem Fehlercode 1(FR_DISK_ERR).

Felix N. schrieb:
> Erst mit 4096 Bytes funktionierte
> es bisher immer(5 Formatierungen durchgeführt).

Wie willst Du auf der Basis von nur 5 Versuchen irgendwelche Schlüsse 
ziehen, daß dadurch Dein Problem, das im Schnitt sowieso nur jedes 5. 
Mal (20% der Fälle) auftritt, gelöst wurde?

Also mit weniger als 20 (erfolgreichen) Testläufen würd ich noch 
nichtmal drüber nachdenken, ob ich das Problem als gelöst betrachte; 
wobei selbst dann die Chance, daß alles nur ein (un)glücklicher Zufall 
war noch über 1% liegt.


Felix N. schrieb:
> Als SD-Karte kommt im Moment eine
> SanDisk Ultra 32GB microSD SDHC Karte zum Einsatz

Moment Mal - willst Du damit sagen, daß bei ein und derselben Karte das 
Formatieren mit 512 Byte Buffer in 20% der Fälle schiefgeht?

Das wäre dann nämlich eine eindeutige Bestätigung dieser Vermutung:

Jim M. schrieb:
> Bei diesem Verhalten ist nämlich eher die SD Karte selber das Problem.

von Felix N. (felix_n888)


Lesenswert?

Moin! Sorry für die späte Antwort.

c-hater schrieb:
> Warum also steppst du nicht einfach mal
> in die Funktion rein und schaust, wo genau sie scheitert?

Das mit dem Debugger habe ich bereits versucht jedoch erfolglos. Ich 
kenne mich nicht besonderes gut aus mit dem STM32 Debugger. Ein 
entsprechenden Breakpoint habe ich gesetzt bei der Funktion. Jedoch wenn 
ich das Programm als Debugging starte mit dem Debugging Level Default in 
den Projekt Einstellungen. Startet es auch aber sobald ich auf "Resume" 
klicke passiert nichts mehr. Wenn ich das Programm dann wieder anhalte 
bleibt der Debugger bei der Initialisierung des OLED Display stehen, 
auch wenn ich ihn dann wieder starte und später wieder anhalte bleibt 
der an der genau gleichen stelle. Wahrscheinlich bediene ich aber den 
Debugger auch einfach falsch.

Jim M. schrieb:
> Dann dekoriere mal die Disk Lese und Schreibfunktion so dass Du SD
> Karten Fehler mitbekommst.

Verstehe ich jetzt nicht ganz. Du meinst die disk_read und disk_write 
Funktionen in der mmc.c Datei?

Jim M. schrieb:
> Bei diesem Verhalten ist nämlich eher die SD Karte selber das Problem.

mIstA schrieb:
> Moment Mal - willst Du damit sagen, daß bei ein und derselben Karte das
> Formatieren mit 512 Byte Buffer in 20% der Fälle schiefgeht?
>
> Das wäre dann nämlich eine eindeutige Bestätigung dieser Vermutung:

Nein. Ich habe mehrere SD-Karten mitterweile ausprobiert. Insgesamt 5 
verschiedene SD Karten auch von unterschiedlichen Herstellern.

Folgende:
- SanDisk Ultra 32GB -> 1x von 10x erfolgreich bei 512 Bytes
- SanDisk Extreme 128GB(exFAT Support aktiviert) -> Fehlgeschlagen
- Hama microSD 2GB -> 2x von 10 mal geklappt FAT16
- Hama microSD 4GB -> 10x fehlgeschlagen
- Samsung EVO 64GB -> Fehlgeschlagen(exFAT). f_mkfs hängt teilweise 
komplett hier.

Alles mit Puffer von FF_MAX_SS ausprobiert(512 Bytes).

Ich habe mal in der ff.c wo LEAVE_MKFS(FR_DISK_ERR) zurückgegeben wird 
UART Nachrichten eingefügt folgender Codeteil wirft das FR_DISK_ERR 
immer egal bei welcher SD-Karte mit welcher Größe:
1
    /* Initialize FAT area */
2
    memset(buf, 0, sz_buf * ss);
3
    sect = b_fat;    /* FAT start sector */
4
    for (i = 0; i < n_fat; i++) {      /* Initialize FATs each */
5
      if (fsty == FS_FAT32) {
6
        st_dword(buf + 0, 0xFFFFFFF8);  /* FAT[0] */
7
        st_dword(buf + 4, 0xFFFFFFFF);  /* FAT[1] */
8
        st_dword(buf + 8, 0x0FFFFFFF);  /* FAT[2] (root directory) */
9
      } else {
10
        st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8);  /* FAT[0] and FAT[1] */
11
      }
12
      nsect = sz_fat;    /* Number of FAT sectors */
13
      do {  /* Fill FAT sectors */
14
        n = (nsect > sz_buf) ? sz_buf : nsect;
15
        if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); //<<<<<<<---!
16
        memset(buf, 0, ss);  /* Rest of FAT all are cleared */
17
        sect += n; nsect -= n;
18
      } while (nsect);
19
    }

mIstA schrieb:
> Also mit weniger als 20 (erfolgreichen) Testläufen würd ich noch
> nichtmal drüber nachdenken, ob ich das Problem als gelöst betrachte;
> wobei selbst dann die Chance, daß alles nur ein (un)glücklicher Zufall
> war noch über 1% liegt.

Das dass Problem als gelöst betrachtet sei hab ich ja nicht behauptet. 
Ich habe ja nur festgestellt das wenn der Puffer 4096 Bytes groß ist 
anstatt der Mindestgröße von FF_MAX_SS(512 Bytes) die Funktion besser 
funktioniert. Wenn ich jetzt 100 Testdurchläufe mache und 2 davon 
fehlschlagen wären das 2%. Wenn ich wie jetzt 5 mache und davon mit 4096 
Bytes alle funktionieren während bei 512 Bytes nur 1 oder 2 
funktioniert. Kann man ja schonmal davon ausgehen das irgendwas damit 
nicht stimmt.

Den Compiler habe ich aktuell bei Optimization level auf "Optimize of 
Size (-Os)) stehen. Aber das dürfte ja eigentlich keine Auswirkung haben 
oder?

mfg

von c-hater (Gast)


Lesenswert?

Felix N. schrieb:

> Wahrscheinlich bediene ich aber den
> Debugger auch einfach falsch.

Davon kann man wohl ausgehen. Wie wäre es, wenn du den richtigen Umgang 
damit einfach mal erlernst? Das hilft dir nur nicht nur bei dem 
gegenwärtigen Problem, sondern auch bei jedem zukünftigen.

von Jim M. (turboj)


Lesenswert?

Genau deswegen schrieb ich oben dass Du disk_write mal so dekorieren 
solltest das Schreibfehler ausgegeben werden.

Kann durchaus sein das da irgendwas nicht lange genug wartet zum 
Bleistift.

Das originale Format (oder das mit dem Formatter von sdcard.org) packt 
die FAT nämlich an die internen Blockgrenzen der Karte - das f_mkfs hat 
davon überhaupt keine Ahnung.

Übrigens macht es bei SD Karten einen RIESEN Unterschied ob man sie mit 
512 Byte oder 4KB Blöcken beschreibt. Bei 512 Byte fahren die nach dem 
Schreiben in den Stromspar Modus runter, bei 4K Blöcken nicht.

Ich hatte hier schon Karten die >1 sec fürs Schreiben brauchen. Stelle 
eventuell mal die Timeouts hoch.

von Felix N. (felix_n888)


Lesenswert?

Moin,

c-hater schrieb:
> Wie wäre es, wenn du den richtigen Umgang
> damit einfach mal erlernst?

Werde ich machen. Kannst du mir da gute Quellen nennen die das gut 
erklären?

Jim M. schrieb:
> Genau deswegen schrieb ich oben dass Du disk_write mal so dekorieren
> solltest das Schreibfehler ausgegeben werden.

Ich habe die Funktion nun mal mit ein paar UART Nachrichten 
überarbeitet. Die Funktion ist unten mal mit angehängt damit man das 
nachvollziehen kann wo welche Nachricht ausgeben wird wenn ein Fehler 
eintritt. Die disk_read Funktion hab ich vorsichtshalber auch mal mit 
überarbeitet aber da erfolgt keine Fehlerausgabe.

Dem nach tritt der Fehler beim f_mkfs in der disk_write funktion auf 
beim schreiben des blocks:
1
if ((send_cmd(CMD24, sect) == 0)  /* WRITE_BLOCK */
2
      && xmit_datablock(buff, 0xFE)) {

Es wird beim Formatieren(f_mkfs) immer zurückgeben neben FR_DISK_ERR. 
diskwrite() CMD24 failed.

Jim M. schrieb:
> Übrigens macht es bei SD Karten einen RIESEN Unterschied ob man sie mit
> 512 Byte oder 4KB Blöcken beschreibt. Bei 512 Byte fahren die nach dem
> Schreiben in den Stromspar Modus runter, bei 4K Blöcken nicht.

Hmm okay. Also ich nutze hier ja den STM32F103CBT6 der hat 20K RAM. Da 
könnte ich noch ein paar kB für die SD-Karte verwenden. Ist das der 
FF_MIN_SS und FF_MAX_SS Parameter in der ffconf.h?

Jim M. schrieb:
> Stelle
> eventuell mal die Timeouts hoch.

Falls sich das auf die FF_MIN_SS und FF_MAX_SS bezieht. Die stehen 
aktuell beide auf 512. Wenn ich beide auf 1024, 2048 oder 4096 stelle 
wird die SD-Karte nicht mehr erkannt und ich kann sie nicht mehr 
mounten.

disk_write:
1
#if FF_FS_READONLY == 0
2
DRESULT disk_write (
3
  BYTE drv,      /* Physical drive number (0) */
4
  const BYTE *buff,  /* Ponter to the data to write */
5
  LBA_t sector,    /* Start sector number (LBA) */
6
  UINT count      /* Number of sectors to write (1..128) */
7
)
8
{
9
  DWORD sect = (DWORD)sector;
10
11
12
  if (drv || !count) {
13
    sendToUSART(USART1, "disk_write() RES_PARERR\r\n");
14
    return RES_PARERR;    /* Check parameter */
15
  }
16
  if (Stat & STA_NOINIT)  {
17
    sendToUSART(USART1, "disk_write() RES_NOTRDY\r\n");
18
    return RES_NOTRDY;  /* Check drive status */
19
  }
20
  if (Stat & STA_PROTECT) {
21
    sendToUSART(USART1, "disk_write() RES_WRPRT\r\n");
22
    return RES_WRPRT;  /* Check write protect */
23
  }
24
25
  if (!(CardType & CT_BLOCK)) sect *= 512;  /* LBA ==> BA conversion (byte addressing cards) */
26
27
  if (count == 1) {  /* Single sector write */
28
    if ((send_cmd(CMD24, sect) == 0)  /* WRITE_BLOCK */
29
      && xmit_datablock(buff, 0xFE)) {
30
      count = 0;
31
    }else{
32
      sendToUSART(USART1, "disk_write() CMD24 failed\r\n");
33
    }
34
  }
35
  else {        /* Multiple sector write */
36
    if (CardType & CT_SDC) send_cmd(ACMD23, count);  /* Predefine number of sectors */
37
    if (send_cmd(CMD25, sect) == 0) {  /* WRITE_MULTIPLE_BLOCK */
38
      do {
39
        if (!xmit_datablock(buff, 0xFC)) {
40
          sendToUSART(USART1, "Multi sector write 0xFC failed\r\n");
41
          break;
42
        }
43
        buff += 512;
44
      } while (--count);
45
      if (!xmit_datablock(0, 0xFD)) {
46
        count = 1;  /* STOP_TRAN token */
47
      }else{
48
        sendToUSART(USART1, "STOP_TRAN token failed\r\n");
49
      }
50
    }
51
  }
52
  deselect();
53
54
  return count ? RES_ERROR : RES_OK;  /* Return result */
55
}
56
#endif

Mfg

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.

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