Forum: Mikrocontroller und Digitale Elektronik ARM Blue Pill STM32F103 ChanFat StdperiphLib SD Treiber gesucht!


von Christian J. (Gast)


Lesenswert?

Hallo,

es ist sehr speziell aber ich suche jemand, der exakt mal das realisiert 
hat und mir den Source Code der Datei mmc_stm32F1.c und .h schicken 
kann.

Und zwar geschrieben mit der veralteten StdPeripheral Library für den 
STM31F1xx, weil ich EmBitz 1.1 benutze als IDE, mich nur mit dieser 
auskenne und im reinen Hobby Bereich HAL nicht neu "lernen" möchte (weil 
alles ja auch so geht), CubeMX gilt das gleiche für. ChanFatFs hat zwar 
selbst Code dafür veröffentlicht aber dieser basiert auf 
Registeraufrufen einer stm31F1xx.h und ist mit EmBitz nicht 
kompilierbar, weil das eben eine IDE ist, die eine eigene Umgebung 
mitbringt. Vermutlich hat er das als normales GCC Projekt geschrieben 
mit make File, Linker File usw. ohne eine IDE sondern rein Konsole.

In Kürze:
ChanFatFs Treiber für SD Karten
Datei mmc_stm32F1.c
kompiliert unter EmBitz IDE 1.1. durch
benutz die StdPeripheral Librarys als Unterbau

Ich möchte nur eine SD Karte dran pappen und wissen dass es läuft :-)

Christian

Diese Routinen von ihm sind IMHO falsch, sie berücksichtigen nicht die 
Abfrage von wesentlichen Flags der SPI. Das läuft nur durch, solange die 
CPU "langsamer" ist als die SPI, z.b. bei O0 Optimierung, mit O3 klappt 
es dann oft nicht mehr. Daran habe ich mich mal totgesucht früher. Vor 
dem Eintritt in eine SPI Routine muss der Status abgefragt werden, oder 
nachher. Jedenfalls so nicht.
1
/* Receive multiple byte */
2
static
3
void rcvr_spi_multi (
4
  BYTE *buff,    /* Pointer to data buffer */
5
  UINT btr    /* Number of bytes to receive (even number) */
6
)
7
{
8
  WORD d;
9
10
11
  SPIx_CR1 &= ~_BV(6);
12
  SPIx_CR1 |= (_BV(6) | _BV(11));  /* Set SPI to 16-bit mode */
13
14
  SPIx_DR = 0xFFFF;
15
  btr -= 2;
16
  do {          /* Receive the data block into buffer */
17
    while (SPIx_SR & _BV(7)) ;
18
    d = SPIx_DR;
19
    SPIx_DR = 0xFFFF;
20
    buff[1] = d; buff[0] = d >> 8; 
21
    buff += 2;
22
  } while (btr -= 2);
23
  while (SPIx_SR & _BV(7)) ;
24
  d = SPIx_DR;
25
  buff[1] = d; buff[0] = d >> 8;
26
27
  SPIx_CR1 &= ~(_BV(6) | _BV(11));  /* Set SPI to 8-bit mode */
28
  SPIx_CR1 |= _BV(6);
29
}
30
31
32
#if _USE_WRITE
33
/* Send multiple byte */
34
static
35
void xmit_spi_multi (
36
  const BYTE *buff,  /* Pointer to the data */
37
  UINT btx      /* Number of bytes to send (even number) */
38
)
39
{
40
  WORD d;
41
42
43
  SPIx_CR1 &= ~_BV(6);
44
  SPIx_CR1 |= (_BV(6) | _BV(11));    /* Set SPI to 16-bit mode */
45
46
  d = buff[0] << 8 | buff[1];
47
  SPIx_DR = d;
48
  buff += 2;
49
  btx -= 2;
50
  do {          /* Receive the data block into buffer */
51
    d = buff[0] << 8 | buff[1];
52
    while (SPIx_SR & _BV(7)) ;
53
    SPIx_DR;
54
    SPIx_DR = d;
55
    buff += 2;
56
  } while (btx -= 2);
57
  while (SPIx_SR & _BV(7)) ;
58
  SPIx_DR;
59
60
  SPIx_CR1 &= ~(_BV(6) | _BV(11));  /* Set SPI to 8-bit mode */
61
  SPIx_CR1 |= _BV(6);
62
}
63
#endif

von jo mei (Gast)


Angehängte Dateien:

Lesenswert?

Christian J. schrieb:
> Diese Routinen von ihm sind IMHO falsch, sie berücksichtigen nicht die
> Abfrage von wesentlichen Flags der SPI.

Da magst du sehr Recht haben.

Wenn man gugelt findet man schon 2017 Einträge von dir, richtig?

Christian J. schrieb:
> Vor dem Eintritt in eine SPI Routine muss der Status abgefragt
> werden, oder nachher.

Sehr richtig. Daher mach ich das wie im Anhang dargestellt, das
sollte funktionieren. Tatsächlich muss "man" mehr warten als
man vermuten sollte, auch wenn es unplausibel erscheint. Man
muss auch immer das SPI-Datenregister lesen, auch wenn man
SPI schreiben möchte, sonst kommt die Maschine ausser tritt.

Daher nur eine universelle Byte-Routine für Lesen und Schreiben.
Paar kleine Anpassungen für deinen Zweck wirst du machen müssen,
aber das wirst du schon schaffen. 16-Bit Transfers sind hier
nicht nötig und machen die Sacher nur komplizierter aber nicht
spürbar schneller.

von Christian J. (Gast)


Lesenswert?

Hi,

ja gut, aufgepasst, 2017 habe ich das Thema schonmal bearbeitet aber die 
Sache im Zuge eines Umzuges ausser Sicht verloren. Jezt erst greife ich 
sie wieder auf und versuche wieder ins Thema zu kommen. Da war damals 
mal was...es klappte nicht.

Und damit hatte es zu tun, weil die CPU nicht einfach in eine laufende 
Prime Cell eingreifen kann, wenn die sich in einem Zustand befindet, der 
völlig  unbekannt ist. Nur mit den Flags gab es Debatten, diese ominöse 
BSY Flag, RXE war da noch und TXE.

  while ((SPI1->SR & SPI_I2S_FLAG_TXE ) == 0)   { /*  wait  */  };
  while ((SPI1->SR & SPI_I2S_FLAG_BSY)  != 0)   { /*  wait  */  };

Danke erstmal für den Code, schaue ich mir an.

Bei mir sieht es etwas komplexer aus, möglicherweise auch auch nicht 
richtig eben. Und die xmit ist auch anders.
1
/* Exchange a byte */
2
static uint8_t xchg_spi (uint8_t dat)  /* Data to send */
3
{
4
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);   /* Wait for SPIz Tx buffer empty */
5
    SPI_I2S_SendData(SPI1,dat);                                       /* Send SPI1 data */
6
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);   /* Wait for SPI1 data reception */
7
    return SPI_I2S_ReceiveData(SPI1);                                  /* Read SPI1 received data */
8
}

1
#if _USE_WRITE
2
/* Send multiple byte */
3
static void xmit_spi_multi ( const uint8_t *buff,  /* Pointer to the data */
4
                             uint32_t btx)      /* Number of bytes to send (even number) */
5
{
6
  uint16_t d;
7
8
    SPI_16Bit();
9
10
   d = buff[0] << 8 | buff[1];
11
    /* Erstes Word senden */
12
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
13
    SPI1->DR = d;
14
15
  buff += 2;
16
  btx -= 2;
17
18
  do { /* Receive the data block into buffer */
19
    d = buff[0] << 8 | buff[1];
20
21
    /* Receive Words auslesen aber verwerfen */
22
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); /* Wait for SPI1 data reception */
23
    SPI1->DR;
24
25
        /* Word senden */
26
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
27
    SPI1->DR = d;
28
29
    buff += 2;
30
  } while (btx -= 2);
31
32
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); /* Wait for SPI1 data reception */
33
    SPI1->DR;
34
35
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
36
37
    SPI_8Bit();
38
}
39
#endif

von jo mei (Gast)


Lesenswert?

Das SPI_I2S_FLAG_BSY Flag dominiert, daher ist es beim Lesen
der Datenregisters unnötig und "fehlerschwanger" nur das
SPI_I2S_FLAG_RXNE Flag auszuwerten. Bei "not busy" ist dagegen
garantiert dass das gelesene Byte bereits zur Verfügung steht
und die Maschine für weitere Operationen bereit ist.

von Christian J. (Gast)


Lesenswert?

Ok.... ich habe es mal "umgeschrieben" für die API, auch wenn der 
Compiler da den gleichen Senf draus macht, ich kann es leichter lesen
1
/* Exchange a 16 Bit Value */
2
inline __attribute__((__always_inline__))  uint8_t xchg_spi (uint16_t dat)  /* Data to send */
3
{
4
    SPI_I2S_SendData(SPI1,dat);                                       /* Send SPI1 data */
5
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);   /* Wait for SPI1 data reception */
6
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == RESET);   /* Wait for SPIz Tx buffer empty */
7
    return SPI_I2S_ReceiveData(SPI1);                                  /* Read SPI1 received data */
8
}

Und Du hast das getestet? das läuft so? Denn es vergeudet ja eigentlich 
Zeit, wenn die CPU wartet, bis etwas raus ist und dann erst weitermacht. 
Es wäre besser das alles nur beim Eintritt zu prüfen. Aber der Return 
Wert steht ja erst zur Verfügung, wenn der State durchgelaufen ist... 
also doch warten.... glaube man kann nur beim Senden zeit sparen, wenn 
man den Empfang nicht auswertet.

habe noch keine Hardware, alles nur Code ohne Prüfung am Objekt....

von jo mei (Gast)


Lesenswert?

Christian J. schrieb:
> Denn es vergeudet ja eigentlich
> Zeit, wenn die CPU wartet, bis etwas raus ist und dann erst weitermacht.

Ich habe lang genug damit gespielt und kann mich nur wiederholen.

jo mei schrieb:
> Tatsächlich muss "man" mehr warten als
> man vermuten sollte, auch wenn es unplausibel erscheint. Man
> muss auch immer das SPI-Datenregister lesen, auch wenn man
> SPI schreiben möchte, sonst kommt die Maschine ausser tritt.

Und ich habe oft genug die SPI Maschine ausser Tritt gebracht
ohne es zu verstehen.

von Christian J. (Gast)


Lesenswert?

Weil das ist ja schon falsch, du verwendest numbytes im while aber 
decremenierst es nicht.
1
static  void xmit_spi_multi
2
(
3
  uint8_t *buff,      /* Pointer to data buffer */
4
  uint16_t numbytes   /* Number of bytes to receive (even number) */
5
)
6
{
7
  uint16_t ii;
8
9
  SD_CS_LOW;
10
  ii = 0;
11
  do          /* send data block into buffer */
12
  {
13
    spi_xfer(*(buff+ii));
14
    ii++;
15
  } while (numbytes > 0);
16
  SD_CS_HIGH;
17
18
} /* --- xmit_spi_multi --- */

von jo mei (Gast)


Lesenswert?

Christian J. schrieb:
> auch wenn der Compiler da den gleichen Senf draus macht

Wenn du die SPL Calls verwendest kannst du tatsächlich Pech
haben und das Zeug läuft langsamer ..... wenn der Compiler
nicht optimiert oder wenn er es nicht rafft bei Optimierung
da ausreichend Inline-Code draus zu machen.

von jo mei (Gast)


Lesenswert?

Ja ok, dekrementieren vergessen.

von Christian J. (Gast)


Lesenswert?

jo mei schrieb:
> Ja ok, dekrementieren vergessen.

Also ist der Code nicht aus einer laufenden Anwendung heraus genommen? 
Sorry,wenn ich bohre aber meist findet sich dann leider noch mehr (ich 
war früher beim TÜV, angeboren :-)

Außerdem würde die Routine wohl einmal zu wenig durchlaufen, die holt 
nur numbytes-1 Daten rein aber das habe ich jetzt nicht geprüft.

Die SPL Calls werden aufgelöst, das sieht man im ASM beim Debuggen. Man 
darf aber nicht size opt einstellen, sondern maximale Geschwindigkeit. 
Sonst nimmer er sie rein.

von Christian J. (Gast)


Lesenswert?

Das ist die aktuelle Chan Routine
1
do {/* Receive the data block into buffer */
2
    d = buff[0] << 8 | buff[1];
3
    while (SPIx_SR & _BV(7)) ;
4
    SPIx_DR;
5
    SPIx_DR = d;
6
    buff += 2;
7
} while (btx -= 2);
8
while (SPIx_SR & _BV(7)) ;
9
SPIx_DR;

Nachgucken:
1
#define SPI_I2S_FLAG_RXNE               ((uint16_t)0x0001)
2
#define SPI_I2S_FLAG_TXE                ((uint16_t)0x0002)
3
#define I2S_FLAG_CHSIDE                 ((uint16_t)0x0004)
4
#define I2S_FLAG_UDR                    ((uint16_t)0x0008)
5
#define SPI_FLAG_CRCERR                 ((uint16_t)0x0010)
6
#define SPI_FLAG_MODF                   ((uint16_t)0x0020)
7
#define SPI_I2S_FLAG_OVR                ((uint16_t)0x0040)
8
#define SPI_I2S_FLAG_BSY                ((uint16_t)0x0080)

Nr.7 ist das BSY Flag, das benutzt er als einziges und zum Abschluss 
nochmal.

Was ist mit SPI_DR; Das wird doch weg optimiert, wenn es nicht volatile 
ist.

Doof, dass an dem bisschen das Funktionieren des gesamten Code hängt, 
weil das ganz unten läuft... stimmt das nicht klappt gar nichts. Da 
kommt es auf jedes Bit und jede Mikrosekunde an.

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.