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
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)
Hi Oft ist ein DMA schneller als ein Transfer über die CPU. Matthias
@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 | }
|
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.
>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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.