Forum: Mikrocontroller und Digitale Elektronik Macht ein FAT Filesystem mit DMA überhaupt Sinn ohne RTOS


von Thomas B. (escamoteur)


Lesenswert?

Hallo,

ich bin gerade dabei Elm Chans FAT Filesystem für eine SD-Karte in einem 
neuen Projekt einzubinden.
Es gibt ja einige Beispielportierungen, die für das Lesen der Blöcke SPI 
mit DMA verwenden.

DMA hört sich ja erst mal gut an, der µC kann in der Zwischenzeit sich 
um andere Sachen kümmern. ABER in den Beispielen habe ich nur gesehen, 
dass nach Starten des DMAs auf dessen Ende gepollt wird. Wo ist da der 
Gewinn?

Wenn ich ein RTOS mit mehreren Threads habe, dann könnte der µC ja 
wirklich andere Dinge machen bis der DMA fertig ist. Aber ohne RTOS läßt 
sich ein Kontextswitch nicht so einfah in die FAT-Funktionen 
integrieren.

Daher die Frage: Macht DMA hier überhaupt Sinn? Das einzige was ich mir 
vorstellen könnte, dass mit DMA eine bereits gestartete Übertragung 
nicht durch auftretende IRQs unterbrochen/verzögert wird. Wobei sich die 
Frage ergibt, ob die DMA durch IRQs beeinflusst wird.

Wie seht Ihr das?

Gruß
Thomas

von Timmo H. (masterfx)


Lesenswert?

Wieso pollen? Es gibt doch auch "DMA Transfer Complete"-Interrupts. Wenn 
dieser eintritt kann man irgendein Flag setzen und "irgendwann" mal 
danach schauen wenn man gerade Zeit hat, oder direkt den nächsten 
Transfer starten. Wobei man im Gewissen Sinne natürlich dann auf das 
"Flag" pollen würde. Aber das lässt sich ja implementieren wie man will 
(zumindest wenn man nicht an ein OS gebunden ist)

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Oft ist ein DMA schneller als ein Transfer über die CPU.

Matthias

von Thomas B. (escamoteur)


Lesenswert?

@Timmo:

Schon klar. Aber leider wird die entsprechende Funktion nicht direkt vo 
meinem Programm sondern in den tiefen der FAT Bibliothek aufgerufen 
wird, hab ich keine Idee was mir da der IRQ helfen soll.

1
static
2
void stm32_dma_transfer(
3
  BOOL receive,    /* FALSE for buff->SPI, TRUE for SPI->buff               */
4
  const BYTE *buff,  /* receive TRUE  : 512 byte data block to be transmitted
5
               receive FALSE : Data buffer to store received data    */
6
  UINT btr       /* receive TRUE  : Byte count (must be multiple of 2)
7
               receive FALSE : Byte count (must be 512)              */
8
)
9
{
10
  DMA_InitTypeDef DMA_InitStructure;
11
  WORD rw_workbyte[] = { 0xffff };
12
13
  /* shared DMA configuration values */
14
  DMA_InitStructure.DMA_PeripheralBaseAddr = (DWORD)(&(SPI_SD->DR));
15
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
16
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
17
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
18
  DMA_InitStructure.DMA_BufferSize = btr;
19
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
20
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
21
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
22
23
  DMA_DeInit(DMA_Channel_SPI_SD_RX);
24
  DMA_DeInit(DMA_Channel_SPI_SD_TX);
25
26
  if ( receive ) {
27
28
    /* DMA1 channel2 configuration SPI1 RX ---------------------------------------------*/
29
    /* DMA1 channel4 configuration SPI2 RX ---------------------------------------------*/
30
    DMA_InitStructure.DMA_MemoryBaseAddr = (DWORD)buff;
31
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
32
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
33
    DMA_Init(DMA_Channel_SPI_SD_RX, &DMA_InitStructure);
34
35
    /* DMA1 channel3 configuration SPI1 TX ---------------------------------------------*/
36
    /* DMA1 channel5 configuration SPI2 TX ---------------------------------------------*/
37
    DMA_InitStructure.DMA_MemoryBaseAddr = (DWORD)rw_workbyte;
38
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
39
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
40
    DMA_Init(DMA_Channel_SPI_SD_TX, &DMA_InitStructure);
41
42
  } else {
43
44
#if _FS_READONLY == 0
45
    /* DMA1 channel2 configuration SPI1 RX ---------------------------------------------*/
46
    /* DMA1 channel4 configuration SPI2 RX ---------------------------------------------*/
47
    DMA_InitStructure.DMA_MemoryBaseAddr = (DWORD)rw_workbyte;
48
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
49
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
50
    DMA_Init(DMA_Channel_SPI_SD_RX, &DMA_InitStructure);
51
52
    /* DMA1 channel3 configuration SPI1 TX ---------------------------------------------*/
53
    /* DMA1 channel5 configuration SPI2 TX ---------------------------------------------*/
54
    DMA_InitStructure.DMA_MemoryBaseAddr = (DWORD)buff;
55
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
56
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
57
    DMA_Init(DMA_Channel_SPI_SD_TX, &DMA_InitStructure);
58
#endif
59
60
  }
61
62
  /* Enable DMA RX Channel */
63
  DMA_Cmd(DMA_Channel_SPI_SD_RX, ENABLE);
64
  /* Enable DMA TX Channel */
65
  DMA_Cmd(DMA_Channel_SPI_SD_TX, ENABLE);
66
67
  /* Enable SPI TX/RX request */
68
  SPI_I2S_DMACmd(SPI_SD, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
69
70
  /* Wait until DMA1_Channel 3 Transfer Complete */
71
  /// not needed: while (DMA_GetFlagStatus(DMA_FLAG_SPI_SD_TC_TX) == RESET) { ; }
72
  /* Wait until DMA1_Channel 2 Receive Complete */
73
  while (DMA_GetFlagStatus(DMA_FLAG_SPI_SD_TC_RX) == RESET) { ; }
74
  // same w/o function-call:
75
  // while ( ( ( DMA1->ISR ) & DMA_FLAG_SPI_SD_TC_RX ) == RESET ) { ; }
76
77
  /* Disable DMA RX Channel */
78
  DMA_Cmd(DMA_Channel_SPI_SD_RX, DISABLE);
79
  /* Disable DMA TX Channel */
80
  DMA_Cmd(DMA_Channel_SPI_SD_TX, DISABLE);
81
82
  /* Disable SPI RX/TX request */
83
  SPI_I2S_DMACmd(SPI_SD, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
84
}

wird hier aufgerufen
1
static
2
BOOL rcvr_datablock (
3
  BYTE *buff,      /* Data buffer to store received data */
4
  UINT btr      /* Byte count (must be multiple of 4) */
5
)
6
{
7
  BYTE token;
8
9
10
  Timer1 = 10;
11
  do {              /* Wait for data packet in timeout of 100ms */
12
    token = rcvr_spi();
13
  } while ((token == 0xFF) && Timer1);
14
  if(token != 0xFE) return FALSE;  /* If not valid data token, return with error */
15
16
#ifdef STM32_SD_USE_DMA
17
  stm32_dma_transfer( TRUE, buff, btr );
18
#else
19
  do {              /* Receive the data block into buffer */
20
    rcvr_spi_m(buff++);
21
    rcvr_spi_m(buff++);
22
    rcvr_spi_m(buff++);
23
    rcvr_spi_m(buff++);
24
  } while (btr -= 4);
25
#endif /* STM32_SD_USE_DMA */
26
27
  rcvr_spi();            /* Discard CRC */
28
  rcvr_spi();
29
30
  return TRUE;          /* Return with success */
31
}

von W.S. (Gast)


Lesenswert?

Thomas Burkhart schrieb:
> Wo ist da der Gewinn?

Ich hab mit diese Frage auch schon mal gestellt und bin zu dem Schluß 
gekommen, daß es mit der DMA nur dann einen Sinn macht, wenn der 
restliche Controller zwecks Stromsparen deftig heruntergetaktet wird und 
dann mit der Übertragungsrate der SDIO Schnittstelle nicht mehr 
mithalten kann oder wenn man mit nem externen Speicher arbeitet, wo auch 
noch der Bildschirm-Refresh drüberläuft und man während der 
Blockübertragung die CPU stoppt, damit die Bandbreite des Busses nicht 
zum Flaschenhals wird oder wenn du heftigen Interruptverkehr hast, der 
für das Grundprogramm nur noch 20% Cpu-Zeit oder weniger übrig läßt.

Aber für gewöhnliche Anwendungen, wo die jetzt üblichen uC mit 72 MHz 
oder mehr laufen, ist simples Polling genauso gut wie DMA, bloß 
einfacher und mit weniger Overhead. Immerhin kann man bei den STM's mit 
32 Bit pollen, das ist schnell genug für Byteraten von 13 MHz. Für ein 
Filesystem wie ChaN's oder EFSL mußt du ja sowieso auf das Ende der 
Übertragung warten.

W.S.

von holger (Gast)


Lesenswert?

>Aber für gewöhnliche Anwendungen, wo die jetzt üblichen uC mit 72 MHz
>oder mehr laufen, ist simples Polling genauso gut wie DMA, bloß
>einfacher und mit weniger Overhead.

Dem würde ich jetzt so pauschal nicht zustimmen.
Wenn die CPU mit vielen Interrupts beschäftigt ist
ist DMA auf jeden Fall besser als Polling.

>Immerhin kann man bei den STM's mit
>32 Bit pollen, das ist schnell genug für Byteraten von 13 MHz.

Das hab ich jetzt nicht verstanden.

>Für ein Filesystem wie ChaN's oder EFSL mußt du ja sowieso
>auf das Ende der Übertragung warten.

Bei einem UART musst du auch warten bis mal wieder
Platz ist um zu senden. Die Frage ist nur wie man
das am besten in die Programmstruktur einbindet.
Ein RTOS bietet da durchaus Lösungen an. Task für eine
bestimmte Zeit schlafen legen und das DMA arbeiten lassen.
Den Rest der Rechenzeit kann man dann für andere Arbeiten
nutzen. Wenn nix zu tun ist musst du aber trotzdem auf das Ende
des DMA Transfers warten bevor du neue Daten senden kannst.

von TTL (Gast)


Lesenswert?

Einfach mal mit dem Scope auf den SPI schauen! Du erhältst mit DMA einen 
lückenlosen Transfer. Ohne DMA musst du ja die Datenbytes immer 
nachladen. DMA bringt bei 19MHz Bustakt ca. 30% mehr Geschwindigkeit

von Albert B. (Gast)


Lesenswert?

Hallo zusammen,

DMA macht natürlich bei der Verwendung eines Schedulers als Teil des 
Betriebssystems (in solch einem Fall) IMMER Sinn. Denn 
CPU-Unterbrechungen durch DMA-Zugriffe können für andere 
Tasks/Prozesse/... verwendet werden.

Aber: Bei solch einer Frage darf auf keinen Fall die hardwaremäßige 
Architekturfrage vergessen werden. Multi-Master Architekturen, wie es 
solche mit DMA-Zugriffen vermutlich sind, vermindern Buszugriffszeiten 
enorm.

Ihr wisst bestimmt was ich meine:
Sind viele Teilnehmer an einem Bus, so müssen ohne DMA die Daten 
zunächst vom SRAM an die CPU - und von dort aus an andere Medien 
verteilt werden.
Ohne CPU-interaction kann die Zugriffszeit deutlich verringert werden. 
Daten werden vom SRAM direkt an den DMA-Controller übertragen (ohne 
CPU)...
Dies ist die ursprüngliche Idee hinter DMA-Zugriffen.


Beste Grüße
Albert B.

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.