Forum: Mikrocontroller und Digitale Elektronik FatFS check_fs ergibt immer FR_NO_FILESYSTEM


von Patrick B. (p51d)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich bin langsam aber sicher am verzweifeln... Mein aktuelles Projekt 
sollte mal mit SD-Karten zu tun haben.
Die viel gerühmte FatFS (0.13b) habe ich nun auf meinem STM32F091 mit 
Atollic Truestudio und J-Link aufgesetzt und Chans Beispiel angepasst.

Nur leider stolpere ich immer wieder über den gleichen Fehler: Sobald 
check_fs aufgerufen wird (ob das forciert über f_mount oder nachträglich 
bei f_open ist, spielt keine Rolle), erhalte ich NACH dem erfolgreichen 
lesen des MBR einen FR_NO_FILESYTEM Fehler beim suchen der Partitionen 
(Offset wird als 8192 identifiziert).
Da scheitert es immer an der move_window Funktion...

Zur Kontrolle habe ich nun alles mögliche ausprobiert:
-Die micro SD Karte HC  class 10 mit 4GB in allen Varianten am PC 
formatiert...
-Den MBR über winHex analysiert und mit dem SPI-Buffer verglichen sowie 
den Logic-Analyzer angeschlossen.

Nun wirds richtig schräg: Der MBR sieht auf dem PC komplett anders aus. 
SPI-Buffer und Bus-Aktionen stimmen überein.

Die Funktionen disk_initialize und disk_read geben immer ein OK zurück. 
Also grundsätzlich funktioniert alles.
Dass das SPI auch läuft ist gemäss Logic Analyzer auch ersichtlich.
Initialisierungsfrequenz ist <400kHz und später wird mit 12,5MHz 
getacktet.

Hat da jemand einen Tipp?
1
///////////////////////////////////////////////////////////////////////////
2
/// Initialize MMC interface
3
///////////////////////////////////////////////////////////////////////////
4
static void init_spi(void)
5
{
6
  GPIO_InitTypeDef oGpioStruct;
7
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
8
9
  // Card Detect Pin
10
  oGpioStruct.GPIO_Pin = GPIO_Pin_0;
11
  oGpioStruct.GPIO_Mode = GPIO_Mode_IN;
12
  oGpioStruct.GPIO_Speed = GPIO_Speed_2MHz;
13
  oGpioStruct.GPIO_OType = GPIO_OType_PP;
14
  oGpioStruct.GPIO_PuPd = GPIO_PuPd_UP;
15
  GPIO_Init(GPIOB, &oGpioStruct);
16
17
  // Chip Select
18
  oGpioStruct.GPIO_Pin = GPIO_Pin_12;
19
  oGpioStruct.GPIO_Mode = GPIO_Mode_OUT;
20
  oGpioStruct.GPIO_Speed = GPIO_Speed_50MHz;
21
  oGpioStruct.GPIO_OType = GPIO_OType_PP;
22
  oGpioStruct.GPIO_PuPd = GPIO_PuPd_UP;
23
  GPIO_Init(GPIOB, &oGpioStruct);
24
25
  // SPI
26
  oGpioStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
27
  oGpioStruct.GPIO_Mode = GPIO_Mode_AF;
28
  oGpioStruct.GPIO_Speed = GPIO_Speed_50MHz;
29
  oGpioStruct.GPIO_OType = GPIO_OType_PP;
30
  oGpioStruct.GPIO_PuPd = GPIO_PuPd_UP;
31
  GPIO_Init(GPIOB, &oGpioStruct);
32
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_0);
33
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_0);
34
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_0);
35
36
37
  SPI_InitTypeDef  SPI_InitStructure;
38
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
39
  SPI_I2S_DeInit(SPI2);
40
  SPI_StructInit(&SPI_InitStructure);
41
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
42
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
43
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
44
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
45
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
46
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
47
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
48
  SPI_InitStructure.SPI_CRCPolynomial = 7;
49
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
50
  SPI_Init(SPI2, &SPI_InitStructure);
51
  SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF); // Set RXNE Flag at 8-Bit
52
  SPI_Cmd(SPI2, ENABLE);
53
  
54
  CS_HIGH();      // Set CS# high
55
56
  for(m__u32Timer1 = 10; m__u32Timer1; );  // Wait for 10ms
57
}
58
59
60
///////////////////////////////////////////////////////////////////////////
61
/// Exchange a byte 
62
///
63
/// \param dat  Data to send
64
///////////////////////////////////////////////////////////////////////////
65
inline static BYTE xchg_spi(BYTE dat)
66
{
67
  const uint32_t spixbase = (uint32_t)SPI2 + 0x0C;
68
  while ((SPI2->SR & SPI_I2S_FLAG_TXE) == 0);
69
  *(__IO uint8_t *) spixbase = dat;
70
  while ((SPI2->SR & SPI_I2S_FLAG_RXNE) == 0);    // Wait for end of the transaction
71
  return (*(__IO uint8_t *) spixbase);        // Return received byte
72
}
73
74
75
///////////////////////////////////////////////////////////////////////////
76
/// Receive multiple byte
77
///
78
/// \param buff    Pointer to data buffer
79
/// \param btr    Number of bytes to receive (even number)
80
///////////////////////////////////////////////////////////////////////////
81
static void rcvr_spi_multi(BYTE *buff, UINT btr)
82
{
83
  do
84
  {
85
    *buff++ = xchg_spi(0xFF);
86
    *buff++ = xchg_spi(0xFF);
87
  }
88
  while(btr -= 2);
89
}
90
91
92
///////////////////////////////////////////////////////////////////////////
93
/// Send multiple byte 
94
///
95
/// \param buff    Pointer to the data
96
/// \param btx    Number of bytes to send (even number)
97
///////////////////////////////////////////////////////////////////////////
98
#if _USE_WRITE
99
static void xmit_spi_multi(const BYTE *buff, UINT btx)
100
{
101
  do
102
  {
103
    (void)xchg_spi(*buff++);
104
    (void)xchg_spi(*buff++);
105
  }
106
  while(btx -= 2);
107
}
108
#endif

Gruss
Patrick

von Küsel (Gast)


Lesenswert?

Es sieht so aus, dass einmal der MBR und einmal der VBR  gelesen wird.

Wie sehen die Routinen zum Mounten der SD-Card aus?

Tipp: Für das Formatieren nehme ich den "SD-Card Formatter".

https://www.sdcard.org/downloads/formatter_4/

von Jim M. (turboj)


Lesenswert?

Patrick B. schrieb:
> Nun wirds richtig schräg: Der MBR sieht auf dem PC komplett anders aus.
> SPI-Buffer und Bus-Aktionen stimmen überein.

Nö, sieht er nicht. Er ist nur unter Windoof verdammt kompliziert 
einzulesen: Nur als Admin und nur vom physischen Device und nicht vom 
Laufwerksbuchstaben!

Was für Dich wie der 0. Sektor aussieht ist in Wirklichkeit der 1. 
Sektor der Partition.

Schau mal nach ob FatFS den PartitionsTypeCode 0x0C korrekt versteht. 
Die LBA Einträge sehen für eine 8GB Karte gesund aus.

Wieso greifst Du in xchg_spi() nicht direkt aus SPI2->DR zu? Die ganzen 
Pointer casts machen mir Kopfweh.

von M.K. B. (mkbit)


Lesenswert?

Versuch doch mal disk_initialize und disk_read direkt zu verwenden, um 
einzelne Sektoren zu lesen. Wenn das funktioniert, dann müsstest du ja 
die Daten aus deinem Windows Dump wiederfinden.

Nur weil disk_read ein OK zurückgibt, heißt das ja nicht, dass es auch 
das richtige gelesen hat. Vielleicht kannst du in disk_read dann mal mit 
dem Debugger reinschauen, ob man da was sieht. Ansonsten wäre noch zu 
prüfen, ob die Befehle für die Karte korrekt sind.

von Patrick B. (p51d)


Lesenswert?

Jim M. schrieb:
> Wieso greifst Du in xchg_spi() nicht direkt aus SPI2->DR zu? Die ganzen
> Pointer casts machen mir Kopfweh.

8-Bit zugriff auf ein 16Bit Register, und da ich nur 8 Bit senden möchte 
muss ich den Umweg gehen.

M.K. B. schrieb:
> ersuch doch mal disk_initialize und disk_read direkt zu verwenden, um
> einzelne Sektoren zu lesen. Wenn das funktioniert, dann müsstest du ja
> die Daten aus deinem Windows Dump wiederfinden.

Habe ich auch gemacht. Das Resultat sd_mbr_over_spi ist über disk_read 
gelesen.


Küsel schrieb:
> Es sieht so aus, dass einmal der MBR und einmal der VBR  gelesen wird.

Ich vermute nun stark, dass du recht hast.

Ich konnte die Karte in Zwischenzeit mounten und sogar schreiben/lesen.. 
juhu!

Aber erst, nachdem ich diese auf einem Win10 Gerät neu formatiert habe.
Dann habe ich die transmitt und receive multi-Byte Funktionen 
umgeschrieben, das sie die xmit_byte 2 mahl aufrufen. Im Beispiel ist 
sowieso etwas komisch. Das kann von mir aus gesehen nicht richtig 
funktionieren, da die Anzahl Bytes nicht stimmt.

Direkt aus der Sample-Datei von Chan (mmc_stm32f1_spi.c), ohne 
Änderungen
1
void rcvr_spi_multi (
2
  BYTE *buff,    /* Pointer to data buffer */
3
  UINT btr    /* Number of bytes to receive (even number) */
4
)
5
{
6
  WORD d;
7
8
9
  SPIx_CR1 &= ~_BV(6);
10
  SPIx_CR1 |= (_BV(6) | _BV(11));  /* Put SPI into 16-bit mode */
11
12
  SPIx_DR = 0xFFFF;    /* Start the first SPI transaction */
13
  btr -= 2;
14
  do {          /* Receive the data block into buffer */
15
    while ((SPIx_SR & 0x83) != 0x03) ;  /* Wait for end of the SPI transaction */
16
    d = SPIx_DR;            /* Get received word */
17
    SPIx_DR = 0xFFFF;          /* Start next transaction */
18
    buff[1] = d; buff[0] = d >> 8;     /* Store received data */
19
    buff += 2;
20
  } while (btr -= 2);
21
  while ((SPIx_SR & 0x83) != 0x03) ;    /* Wait for end of the SPI transaction */
22
  d = SPIx_DR;              /* Get last word received */
23
  buff[1] = d; buff[0] = d >> 8;      /* Store it */
24
25
  SPIx_CR1 &= ~(_BV(6) | _BV(11));  /* Put SPI into 8-bit mode */
26
  SPIx_CR1 |= _BV(6);
27
}
Ich habe den Code nun etwas genauer angeschaut und analysiert:
wenn hier btr=2 ist und buf 2 gross (also 2 Byte empfangen), wird das 
Datenregister 2 mal mit 16 Bit gelesen und in den Buffer geschrieben. 
Somit würde im schlimmsten Fall nicht nur falsche Daten eingelesen, 
sondern auch gleich unbekannter Speicher überschrieben!
Das Lesen nach der do-while Schlaufe ist zu viel. Oder sehe ich das 
falsch?

Kombiniert mit rcrv_datablock kann man nichtmal sagen, dass die CRC 
nebenbei noch eingelesen wird, da dies noch nachträglich "manuell" 
gemacht wird.
1
int rcvr_datablock (  /* 1:OK, 0:Error */
2
  BYTE *buff,      /* Data buffer */
3
  UINT btr      /* Data block length (byte) */
4
)
5
{
6
  BYTE token;
7
8
9
  Timer1 = 200;
10
  do {              /* Wait for DataStart token in timeout of 200ms */
11
    token = xchg_spi(0xFF);
12
    /* This loop will take a time. Insert rot_rdq() here for multitask envilonment. */
13
  } while ((token == 0xFF) && Timer1);
14
  if(token != 0xFE) return 0;    /* Function fails if invalid DataStart token or timeout */
15
16
  rcvr_spi_multi(buff, btr);    /* Store trailing data to the buffer */
17
  xchg_spi(0xFF); xchg_spi(0xFF);      /* Discard CRC */
18
19
  return 1;            /* Function succeeded */
20
}

Ich werde da wohl wieder ein paar Schritte zurückgehen und schauen, was 
wo genau den Fehler verursacht.

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.