Forum: Mikrocontroller und Digitale Elektronik [xMega] USART Master SPI Lesen mit DMA


von Emanuel (Gast)


Lesenswert?

Hallo zusammen,

ich sitze jetzt schon eine Weile an dem Problem, ich glaube mir fehlt 
etwas Verständnis.

Ich schreibe auf eine SD-Karte mittels USART Master SPI vom 
ATXMega256A3U. Das funktioniert soweit einwandfrei, das Senden ganzer 
Blöcke mit DMA läuft über:
1
/* Send a data block fast */
2
static
3
void xmit_spi_multi (
4
  const BYTE *p,  /* Data block to be sent */
5
  UINT cnt    /* Size of data block */
6
)
7
{
8
  while (DMA.CH_TX_SD.CTRLB & DMA_CH_CHBUSY_bm);                  // Wait for last transmission to be complete
9
  USART_Clear(&MMC_USART_data);
10
  dma_puts_sdcard( (char*) p, cnt );                      // Transmit amount of bytes via DMA
11
}

Nun möchte ich auch blockweise über DMA empfangen können, wenn z.B. das 
Dateisystem eingelesen wird. Bisher geht das nur via:
1
/* Receive a data block fast */
2
static
3
void rcvr_spi_multi (
4
  BYTE *p,  /* Data read buffer */
5
  UINT cnt  /* Size of data block */
6
)
7
{
8
char dummyTxChr = 0xff;
9
do
10
  {    
11
    // Transmit dummy char
12
    while (DMA.CH_TX_SD.CTRLB & DMA_CH_CHBUSY_bm);                  // Wait for last transmission to be complete
13
    dma_puts_sdcard( &dummyTxChr, 1 );                      // Send dummy char
14
    
15
    // Read received char
16
    while (!USART_RXBufferData_Available(&MMC_USART_data));            // Wait for data received
17
    *p++ = usart_getc(&MMC_USART_data);
18
  } while (--cnt);
19
}

Dabei bringt mir der DMA natürlich keinen Zeitvorteil.

Ich möchte daher statt der do-while-Schleife Folgendes benutzen 
(funktioniert nicht, hier brauch ich Hilfe):
1
dma_putdummy_sdcard( &dummyTxChr, cnt );
2
  while (DMA.CH_TX_SD.CTRLB & DMA_CH_CHBUSY_bm);
3
  p = MMC_USART_data.buffer.RX;

Das funktioniert aber nicht. Ich kopiere zwar die Daten des RX-Buffers 
(aus der usart_driver-Bibliothek von Atmel übrigens), aber die Daten 
sind überhaupt nur 104 Byte lang statt 512 Byte (sichtbar an RX-Tail). 
RX- und TX-Buffergrößen sind 512 Bytes.

Der DMA:
1
void dma_putdummy_sdcard(char* str, uint16_t byteCount)
2
{
3
  DMA.CH_TX_SD.ADDRCTRL |= DMA_CH_SRCDIR_FIXED_gc;              // Fixed source address of dummy char
4
      
5
  DMA.CH_TX_SD.SRCADDR0 = (char)((int)str);                  // Source address = string address
6
  DMA.CH_TX_SD.SRCADDR1 = (char)((int)str >> 8);
7
      
8
  DMA.CH_TX_SD.TRFCNT = byteCount;
9
  DMA.CH_TX_SD.TRIGSRC |= DMA_CH_TRIGSRC_USARTE1_DRE_gc;            // Trigger each time USART data reg is empty (otherwise data will get lost!!)
10
  DMA.CH_TX_SD.CTRLA |= DMA_CH_ENABLE_bm;                    // Start transmission. Gets disabled automatically when tx finished
11
}

und
1
// Channel 2: USART Data out to SD (USART in Master SPI mode)            // Increment source address (string to transmit)
2
  DMA.CH_TX_SD.ADDRCTRL |= DMA_CH_DESTDIR_FIXED_gc;              // Keep destination address fixed (USART)
3
      
4
  DMA.CH_TX_SD.DESTADDR0 = (char)((int)&MMC_USART_DATA_REG);          // Destination address = SD Card USART data reg
5
  DMA.CH_TX_SD.DESTADDR1 = (char)((int)&MMC_USART_DATA_REG >> 8);
6
      
7
  DMA.CH_TX_SD.CTRLA   |= DMA_CH_SINGLE_bm;                  // Enable single shot mode = transmit bytecount once (bytecount = stringlength)
8
  DMA.CH_TX_SD.TRIGSRC |= DMA_CH_TRIGSRC_USARTE1_DRE_gc;            // Trigger each time USART data reg is empty (otherwise data will get lost!!)


--> Was übersehe ich beim Datenempfang?

von Jim M. (turboj)


Lesenswert?

Emanuel schrieb:
> Dabei bringt mir der DMA natürlich keinen Zeitvorteil.

Kommt darauf an wieviel Interrupt Last zwischendurch anfällt.

Emanuel schrieb:
> Was übersehe ich beim Datenempfang?

So wie ich das sehe: Eine Menge.
Das zu sendene Byte muss 0x00 oder 0xFF sein, sonst gibts Probleme da 
man Kommandos IIRC auch abbrechen kann. Daher ist *str absolut nicht 
geeignet.

Das DMA Setup braucht 2 Kanäle, einen für SPI TX (sendet nur das Dummy 
Byte) und einen für SPI RX. Dabei muss durch Prioritäten o.ä. 
sichergestellt werden das die RX Transaktion zuerst ausgeführt wird. 
Denn bei SPI wird RX und TX gleichzeitig am Ende eines Bytes "scharf" 
und man will keine RX Bytes verlieren.

: Bearbeitet durch User
von Basti (Gast)


Lesenswert?

Jim M. schrieb:
> Das DMA Setup braucht 2 Kanäle, einen für SPI TX (sendet nur das Dummy
> Byte) und einen für SPI RX. Dabei muss durch Prioritäten o.ä.
> sichergestellt werden das die RX Transaktion zuerst ausgeführt wird.
> Denn bei SPI wird RX und TX gleichzeitig am Ende eines Bytes "scharf"
> und man will keine RX Bytes verlieren.

Dafür hat USART als SPI Master auch mindestens ein Pufferbyte... sollte 
eher nicht zum Problem werden... Blöd ist, dass senden und Empfangen 
über das selbe Register abläuft...

Vorschlag:
Starte einen Timer, der den DMA-Kanal zum senden beim CCA triggert und 
den zum Empfangen beim Overflow...
Die Zeiten sollten nur sehr minimal jittern, durch CPU voran der DMA. 
Lass also etwas Reserve, wenn du die Timerwerte zur SPI Frequenz 
berechnest...

von Emanuel (Gast)


Lesenswert?

Ich hab es endlich geschafft!

@Jim Meba:
Die (char*)-Konvertierung war historisch bedingt, tat aber dem Code 
keinen Abbruch. Nur, weil es noch nicht gut aussieht, bedeutet es nicht, 
dass es nicht so funktionieren kann.

Ich weiß immer noch nicht genau, wieso das ein Fehler war, aber es hat 
nicht geklappt wegen der Source-Byte-Adresse im TX-DMA-Kanal.
Sobald ich als Source ein 0xff-Array angegeben habe in der richtigen 
Länge und den DMA die Source-Adresse habe inkrementieren lassen, hat es 
funktioniert. Trotzdem musste ich noch höllisch aufpassen wegen 
Trigger-Sourcen und den richtigen Reihenfolgen.

Falls es mal jemandem helfen sollte:

Triggersource RX-DMA: RXC (wichtig!)
Triggersource TX-DMA: DRE

RX-Kanal VOR dem TX-Kanal enablen, sonst gehenBytes verloren. Hat bei 
mir dazu geführt, dass nach einer Sekunde ständigem Speichern sich alles 
aufgehängt hat. Habe nicht untersucht, wo und weshalb was verloren 
gegangen ist.
Naja und eben als 0xff-Dummy ein Array angeben, das war bei mir 
zumindest nötig. Scheinbar wird das Original-Dummy sonst zufällig oder 
absichtlich überschrieben, jedenfalls hat sich nach dem Vorgang die 
Source-Adresse geändert (trotz setzen auf fixed!).

von Falk B. (falk)


Lesenswert?

@ Emanuel (Gast)

>Sobald ich als Source ein 0xff-Array angegeben habe in der richtigen
>Länge und den DMA die Source-Adresse habe inkrementieren lassen,

BRauchst du nicht, es reicht EINE einzige char-Variable mit 0xFF. Man 
muss einfach den Source Pointer der DMA auf NICHT inkrementieren 
konfigurieren.

>absichtlich überschrieben, jedenfalls hat sich nach dem Vorgang die
>Source-Adresse geändert (trotz setzen auf fixed!).

Dann hast du woanders noch einen Bug, der dir die DMA-Konfiguration 
stört.

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.