Forum: Mikrocontroller und Digitale Elektronik AT91SAM7X256: SPI nur senden über DMA ohne Empfang


von Peter (Gast)


Lesenswert?

Hallo,
Ich möchte gern nur senden, ohne etwas zu empfangen (d.h. die 
empfangenen Bytes interessieren nicht).

Wenn ich es so mache:

....
  // Enable DMA
  AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

  // Set DMA pointers and length
  AT91C_BASE_SPI0->SPI_RPR = (unsigned int)dummy;
  AT91C_BASE_SPI0->SPI_RCR = sizeof(buff);
  AT91C_BASE_SPI0->SPI_TPR = (unsigned int)buff;
  AT91C_BASE_SPI0->SPI_TCR = sizeof(buff);

  // Wait until DMA complete
  while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXBUFE));
  while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY));

  // Disable DMA
  AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
....

funktioniert es, aber mich stört dabei dieser Dummy Buffer, in dem Daten 
landen, die ich gar nicht brauche.

wenn ich es aber so versuche:

....
  // Enable DMA
  AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_TXTEN;

  // Set DMA pointers and length
  AT91C_BASE_SPI0->SPI_TPR = (unsigned int)buff;
  AT91C_BASE_SPI0->SPI_TCR = sizeof(buff);

  // Wait until DMA complete
  while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXBUFE));
  while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY));

  // Disable DMA
  AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_TXTDIS;
....

funktioniert es nicht :(

Weiss jemand von euch vielleicht, was ich falsch mache?

Danke im Voraus,
 --> Peter

von Peter (Gast)


Lesenswert?

Noch ein Versuch, diesmal mit der lib_AT91SAM7X256_H, brachte auch 
keinen Erfolg.

Das hier:

...
    AT91F_PDC_Open (AT91C_BASE_PDC_SPI0);
    AT91F_PDC_SendFrame (AT91C_BASE_PDC_SPI0, (char*)buff, 512, 0,0);
    AT91F_PDC_ReceiveFrame (AT91C_BASE_PDC_SPI0, (char*)dummy, 512, 
0,0);
    while (!AT91F_PDC_IsTxEmpty(AT91C_BASE_PDC_SPI0 ));
    while (!AT91F_PDC_IsRxEmpty(AT91C_BASE_PDC_SPI0 ));
    AT91F_PDC_Close (AT91C_BASE_PDC_SPI0);
...

funktioniert, aber das:

...
    AT91F_PDC_Open (AT91C_BASE_PDC_SPI0);
    AT91F_PDC_SendFrame (AT91C_BASE_PDC_SPI0, (char*)buff, 512, 0,0);
    while (!AT91F_PDC_IsTxEmpty(AT91C_BASE_PDC_SPI0 ));
    AT91F_PDC_Close (AT91C_BASE_PDC_SPI0);
...

funktioniert nicht :-(

wieso lässt sich der PDC nicht so konfigurieren, dass er empfangene 
Bytes einfach wegwirft?

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Bei mir funktioniert das so einwandfrei (AT91SAM7S256, aus 
http://www.google.com/codesearch?hl=de&q=show:VEJV2SrvXxc:uXNEDD2Kg9A:a4I1dNTs21E&sa=N&ct=rd&cs_p=svn://mikrocontroller.net/mp3dec/trunk&cs_f=fatfs/mmc.c):
1
    // enable DMA transfer
2
    *AT91C_SPI_TPR = buff;
3
    *AT91C_SPI_TCR = 512;
4
    *AT91C_SPI_PTCR = AT91C_PDC_TXTEN;
5
6
    while(! (*AT91C_SPI_SR & AT91C_SPI_ENDTX));
7
    *AT91C_SPI_PTCR = AT91C_PDC_TXTDIS;
8
    (BYTE)( pSPI->SPI_RDR ); // it's important to read RDR here!


Siehe auch den Artikel DMA.

von Peter (Gast)


Lesenswert?

Hallo Andreas,
Danke für deine Antwort.

Ich kenne den Code und den Artikel, so hatte ich es als erstes probiert, 
aber es ging leider nicht. Jetzt habe ich es mal so gemacht:

...
    // enable DMA transfer
    AT91C_BASE_SPI0->SPI_TPR = (unsigned int)buff;
    AT91C_BASE_SPI0->SPI_TCR = 512;
    AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_TXTEN;

    while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXBUFE));
    while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY));
    while(! (AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_ENDTX));
...

und es geht !?!??

sobald ich aber eins von den 'while's weglassen, funktioniert es nicht, 
dann rauscht er zu schnell durch, so als hätten die schleifen keine 
wirkung.
ich kann mir das nicht erklären... :-(

von Peter (Gast)


Lesenswert?

nachtrag, vom code fehlte etwas

...
    // enable DMA transfer
    AT91C_BASE_SPI0->SPI_TPR = (unsigned int)buff;
    AT91C_BASE_SPI0->SPI_TCR = 512;
    AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_TXTEN;

    while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXBUFE));
    while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY));
    while(! (AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_ENDTX));

    // disable DMA
    AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_TXTDIS;

    {
      volatile BYTE xx = (BYTE)( AT91C_BASE_SPI0->SPI_RDR ); // it's 
important to read RDR here!
    }


...

von Peter (Gast)


Lesenswert?

Hallo,
Ich habe mich jetzt entschlossen, ein wenig RAM zu verschwenden, also 
meinen ursprünglichen Code zu verwenden.
Der Code von Andreas lief nur sehr unzuverlässig auf meinem SAM7X256, 
die Version mit dem unnützen Empfangsbuffer dagegen läuft sehr stabil.

Es sieht jetzt so aus:

...
    // Enable DMA
    AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

    // Set DMA info
    AT91C_BASE_SPI0->SPI_RPR = (unsigned int)dummy_rx_buffer;
    AT91C_BASE_SPI0->SPI_RCR = 512;
    AT91C_BASE_SPI0->SPI_TPR = (unsigned int)buff;
    AT91C_BASE_SPI0->SPI_TCR = 512;

    // Wait until DMA complete
    while (AT91C_BASE_SPI0->SPI_RCR != 0);

    // Disable DMA
    AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
...

Die Sendefunktion (benutzt ebenfalls DMA) und sieht fast identisch aus.

@Andreas: Es würde mich trotzdem interessieren...
Ich weiss nicht, warum deine Funktion nicht bei mir funzt.
Wieso machst du am Schluss den Lesezugriff auf das RDR-Register?
Ist diese Vorgensweise irgendwo dokumentiert?

Danke,
 --> Peter

von Michael W. (mictronics) Benutzerseite


Lesenswert?

>>Ist diese Vorgensweise irgendwo dokumentiert?

Im Datenblatt, unter SPI Master Mode Operations. Aber man muss halt 
lesen.

Unbedingt zu beachten sind auch die Errata zum SPI und DMA Mode wenn du 
mehrere Channels mit unterschiedlichen Geschwindigkeiten benutzen 
willst.

von Peter (Gast)


Lesenswert?

>> Im Datenblatt, unter SPI Master Mode Operations. Aber man muss halt lesen.
Also ich kann nirgends finden, dass man das RDR lesen muss nach einem 
PDC Transfer.
Hier scheint einer ein ähnliches Problem zu haben: 
http://www.at91.com/phpbb/viewtopic.php?t=2001&postdays=0&postorder=asc&highlight=spi+dma&start=15
Die AT91SAM7 scheinen ja ganz merkwürdige Dinger zu sein. Was bei dem 
einen Typ klappt, geht bei dem anderen nur mit Tricks...

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

RDR muss man lesen, um später an anderer Stelle keine Probleme zu haben:
Beitrag "Re: SDcard schnell beschreiben, Problem"

von Peter (Gast)


Lesenswert?

> RDR muss man lesen, um später an anderer Stelle keine Probleme zu haben

Ach deshalb, ja, wenn man die übliche RX/TX-Weise benutzt, indem man 
TDRE und RDRF abfragt.

UINT8 SPI_RxTx (UINT8 out)
{
  while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TDRE));
  AT91C_BASE_SPI0->SPI_TDR = out;

  while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_RDRF)); // Könnte zu früh 
gesetzt sein!!!
  return (UINT8)(AT91C_BASE_SPI0->SPI_RDR);
}


...aber man kann natürlich auch gleich beides in einem haben ;-)

UINT8 SPI_RxTx (UINT8 out)
{
  AT91C_BASE_SPI0->SPI_TDR = out;
  while (!( AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_TXEMPTY)); // Danach hat 
RDR immer den aktuellen Wert.
  return (UINT8)(AT91C_BASE_SPI0->SPI_RDR);
}

Das TXEMPTY-Bit wird gesetzt, wenn der Vorgang komplett abgschlossen 
ist, also gesendet und empfangen wurde. In dem Fall dürfte ein fehlendes 
'Read' von SPI_RDR im DMA-Code nichts ausmachen...

von Peter (Gast)


Lesenswert?

Trotzdem würde mich immer noch brennend interessieren, warum der Code 
von Andreas bei mir streikt.
Ich habe schon eine ganze Weile herumgesucht, konnte aber nirgends was 
finden, das auf irgendwelche Unterschiede zwischen den beiden 
AT91SAM7-Typen hinweist...

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.