Forum: Mikrocontroller und Digitale Elektronik ARM Cortex M3 (ATSAM3S4B) - SPI mit PDC (DMA-Controller)


von Dieter (Gast)


Lesenswert?

Hallo,

ich bin gerade dabei, eine SPI-Kommunikation mit zwei gleichen 
Controllern aufzubauen (Vollduplex). Der Controller-Typ ist ein ARM 
Cortex M3 von Atmel (ATSAM3S4B).

Da die Übertragungsgeschwindigkeit ca. 10 MBit beträgt, möchte ich die 
zu übertragenden Daten mit Hilfe des PDC (DMA-Controller) übertragen.


Die SPI-Kommunikation mit PDC funktioniert soweit schon, jedoch habe ich 
noch ein Problem:

Die übertragenen Daten kommen beim Slave verschoben an:
Es werden 16 Bytes übertragen, wobei das erste Byte das letzte Byte der 
vorherigen Übertragung ist.

Es sieht danach aus, dass nach Senden (Master) des letzten Bytes dieses 
im Slave im Empfangsregister (RDR) bleibt und nicht durch den PDC in den 
Speicher übertragen wird.
Mit einem Oszilloskop wurde die gesamte SPI-Übertragung analysiert und 
festgestellt, dass die Daten richtig gesendet werden. Somit liegt das 
Problem höchstwahrscheinlich beim PDC.


Die Themen zu SPI und PDC aus dem Datenblatt kenne ich schon auswendig, 
komme jedoch trotzdem nicht weiter, wobei diese auch nur knapp 
beschrieben sind.



Hat jemand auch solche oder ähnliche Probleme gehabt oder kennt das 
Problem?



Dieter

von SeriousSam (Gast)


Lesenswert?

Ich würde auf eine falsch konfigurierte Einstellung der 
Polarität/Phase/Selects tippen.

von Dieter (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

Danke für die Antwort.
Natürlich habe ich die SPI-Modes vorher überprüft und
ausgiebig getestet - leider ohne Erfolg.


Hier erstmal noch ein paar weitere Infos:

Die beiden Controller laufen mit 64 MHz und die SPI-Geschwindigkeit 
beträgt 10,6 MBit.
Es werden 16 Bytes gesendet und empfangen. Die zu sendenden Testdaten 
sehen wie folgt aus (Master und Slave):

Oberes Nibble   = Byte-Nummer (0x0 bis 0xF)
Unteres Nibble  = Testzähler  (0x0 bis 0xF)

Dadurch können die einzelnen Bytes in der Position genau zugeordnet 
werden (Senden und Empfangen beim Master und Slave).





Zum SPI-Master:
Auf dem Master läuft der SysTick-Interrupt, der alle 125 µs ausgeführt 
wird. In der Interrupt Service Routine wird der PDC zyklisch gestartet 
und somit die SPI-Kommunikation ausgelöst.


Nachfolgend einige Codeausschnitte:


SysTick-Interrupt Master:
1
void SysTick_Handler (void)
2
{
3
  SPI->SPI_PTCR = (SPI_PTCR_TXTEN | SPI_PTCR_RXTEN);    // DMA enable
4
  
5
  
6
  while(!(SPI->SPI_SR & SPI_SR_ENDTX)){}
7
  SPI->SPI_PTCR = SPI_PTCR_TXTDIS;        // DMA-Transmit disable
8
  
9
  while(!(SPI->SPI_SR & SPI_SR_ENDRX)){}
10
  SPI->SPI_PTCR = SPI_PTCR_RXTDIS;        // DMA-Receive disable
11
  
12
  
13
  
14
  SPI->SPI_TPR = &spi_tx_buffer;
15
  SPI->SPI_TCR = sizeof(spi_tx_buffer);
16
17
  SPI->SPI_RPR = &spi_rx_buffer;
18
  SPI->SPI_RCR = sizeof(spi_rx_buffer);
19
  
20
21
  
22
  for (uint8_t i=0; i<sizeof(spi_tx_buffer); i++)      // Sendedaten aktualisieren
23
  {
24
    spi_tx_buffer[i] &= 0xF0;
25
    spi_tx_buffer[i] |= (testZaehler & 0x0F);
26
  }
27
  testZaehler++;
28
}





Hier die SPI-Konfiguration des Masters:
1
void SPI_init_Master (void)
2
{
3
  // --- Parallel Input/Output Controller A ---
4
  PIOA->PIO_WPMR = PIO_WPMR_WPKEY_PASSWD;                              // Disables the Write Protect (PIOA)
5
  PIOA->PIO_ABCDSR[0] &= ~(PIO_ABCDSR_P11 | PIO_ABCDSR_P12 | PIO_ABCDSR_P13 | PIO_ABCDSR_P14);  // Peripheral Select A: NSS, MISO, MOSI, SPCK
6
  PIOA->PIO_ABCDSR[1] &= ~(PIO_ABCDSR_P11 | PIO_ABCDSR_P12 | PIO_ABCDSR_P13 | PIO_ABCDSR_P14);  // Peripheral Select A: NSS, MISO, MOSI, SPCK
7
  PIOA->PIO_PDR = (PIO_PDR_P11 | PIO_PDR_P12 | PIO_PDR_P13 | PIO_PDR_P14);            // PIO Disable: PA11, PA12, PA13, PA14
8
  PIOA->PIO_PUDR = (PIO_PUDR_P11 | PIO_PUDR_P12 | PIO_PUDR_P13 | PIO_PUDR_P14);          // Disables the pull up resistor on PA11, PA12, PA13, PA14
9
  
10
  
11
  // --- SPI (Configuration as Master) ---
12
  PMC->PMC_PCER0 = PMC_PCDR0_PID21;                                // Enables the peripheral clock for SPI
13
  SPI->SPI_WPMR = SPI_WPMR_WPKEY_PASSWD;                              // Disables the Write Protect (SPI)
14
  SPI->SPI_CR = (SPI_CR_SPIDIS | SPI_CR_SWRST);                          // SPI Disable; SPI Software Reset
15
  SPI->SPI_MR = (SPI_MR_MSTR | SPI_MR_PCS(0xE));                           // SPI Master Mode; Peripheral Chip Select 0
16
  SPI->SPI_CSR[0] = (SPI_CSR_BITS_8_BIT | SPI_CSR_SCBR(0x06));
17
  SPI->SPI_CR = SPI_CR_SPIEN;                                    // SPI Enable
18
}





SPI-Slave:
Auf dem Slave läuft ebenfalls ein Interrupt mit der Frequenz von 8 kHz. 
Da die Quarze der beiden Controller nicht 100% dieselbe Frequenz haben, 
werden die Interrupts synchronisiert, wobei der Regler natürlich schon 
funktioniert.
Im Anhang ist dazu ein Oszilloskopbild dargestellt. Das gelbe Signal 
(Kanal 1) ist dabei die Dauer des PWM-Interrupts im Slave (Beim Eintritt 
in den ISR-Handler wird ein Ausgang gesetzt, beim Austritt wieder 
zurückgesetzt).

Code-Ausschnitt des PWM-Interrupts im Slave:
1
 void PWM_Handler(void)
2
{
3
  PIOA->PIO_SODR = PIO_SODR_P17;
4
5
   PWM->PWM_ISR1;
6
     
7
8
   
9
   SPI->SPI_TPR = &spi_tx_buffer;
10
   SPI->SPI_TCR = sizeof(spi_tx_buffer);
11
   
12
   SPI->SPI_RPR = &spi_rx_buffer;
13
   SPI->SPI_RCR = sizeof(spi_rx_buffer);
14
   
15
   SPI->SPI_PTCR = (SPI_PTCR_TXTEN | SPI_PTCR_RXTEN);
16
   
17
   
18
   for (uint8_t i=0; i<sizeof(spi_tx_buffer); i++)
19
   {
20
     spi_tx_buffer[i] &= 0xF0 ;
21
     spi_tx_buffer[i] |= (testZaehler & 0x0F);
22
   }
23
   
24
   testZaehler++;
25
   
26
  PIOA->PIO_CODR = PIO_CODR_P17;
27
   NVIC_ClearPendingIRQ(PWM_IRQn);  
28
}





Und hier die dazugehörige SPI-Konfiguration des Slaves:
1
void SPI_init_Slave (void)
2
{
3
  // --- Parallel Input/Output Controller A ---
4
  PIOA->PIO_WPMR = PIO_WPMR_WPKEY_PASSWD;                              // Disables the Write Protect (PIOA)
5
  PIOA->PIO_ABCDSR[0] &= ~(PIO_ABCDSR_P11 | PIO_ABCDSR_P12 | PIO_ABCDSR_P13 | PIO_ABCDSR_P14);  // Peripheral Select A: NSS, MISO, MOSI, SPCK
6
  PIOA->PIO_ABCDSR[1] &= ~(PIO_ABCDSR_P11 | PIO_ABCDSR_P12 | PIO_ABCDSR_P13 | PIO_ABCDSR_P14);  // Peripheral Select A: NSS, MISO, MOSI, SPCK
7
  PIOA->PIO_PDR = (PIO_PDR_P11 | PIO_PDR_P12 | PIO_PDR_P13 | PIO_PDR_P14);            // PIO Disable: PA11, PA12, PA13, PA14
8
  PIOA->PIO_PUDR = (PIO_PUDR_P11 | PIO_PUDR_P12 | PIO_PUDR_P13 | PIO_PUDR_P14);          // Disables the pull up resistor on PA11, PA12, PA13, PA14
9
  
10
  
11
  // --- SPI (Configuration as Slave) ---
12
  PMC->PMC_PCER0 = PMC_PCDR0_PID21;                                // Enables the peripheral clock for SPI
13
  SPI->SPI_WPMR = SPI_WPMR_WPKEY_PASSWD;                              // Disables the Write Protect (SPI)
14
  SPI->SPI_CR = (SPI_CR_SPIDIS | SPI_CR_SWRST);                          // SPI Disable; SPI Software Reset
15
  SPI->SPI_MR = 0x00000000;
16
  SPI->SPI_CSR[0] = 0x00000000;
17
  SPI->SPI_CR = SPI_CR_SPIEN;                                    // SPI Enable
18
}





Meine Frage ist jetzt, wie der PDC genau konfiguriert werden muss bzw. 
ob der Fehler (verschobene Bytes - siehe Anhang und 1. Beitrag) durch 
eine falsche Konfiguration des PDC/SPI zustande kommt oder ob die 
Reihenfolge des PDC falsch ist (PDC aktivieren, aktualisieren, 
deaktivieren).



Könnt ihr weiterhelfen? Bin für Hinweise und Tipps sehr dankbar.


Dieter

von Mirco (Gast)


Lesenswert?

hast du schon beim atmel support angefragt? die jungs sind da relativ 
flott mit hinweisen

von Dieter (Gast)


Lesenswert?

Hallo,

das Atmel-Supportteam hat mich nur auf das SPI/PDC Beispiel aus dem 
Atmel-Studio hingewiesen, welches mich jedoch nicht weiter gebracht hat.

Für einen weiteren Tipp wäre ich Euch wirklich dankbar!


Dieter

von Karl K. (leluno)


Lesenswert?

Das ganze soweit verlangsamen dass du auf deinem oszi den Ablauf der 
Kommmunikation auch wirklich erkennen kannst.

Ein Problem mit Polarität/Phase ist naheliegend.

von Dieter (Gast)


Lesenswert?

Hallo,

das dargestellte Oszillogramm sollte nur schematisch die 
SPI-Kommunikation darstellen. Bei weitern hineinzoomen kann ich die 
selben Bits wie im Sende- und Empfangsbuffer ausgelesen, feststellen.

Wenn ich mich nicht täusche, sind vier verschiedene Betriebsmodi für die 
Polarität/Phase möglich (hier mittels CPOL und NCPHA -> S. 561 aus dem 
Datenblatt: http://www.atmel.com/devices/sam3s4b.aspx?tab=documents).
Selbst bei sinnlosen ausprobieren aller dieser Betriebsmodi ist 
bestenfalls dieser Byte-Versatz enthalten. Somit dürfte dieser Fehler 
ausgeschlossen sein?!


Dieter

von Dieter (Gast)


Lesenswert?

Hallo,

das Problem ist gelöst - hier die Erläuterung, damit alles etwas davon 
haben:


Um die empfangenen Daten zu überprüfen (siehe oben, 3. Beitrag), habe 
ich mit dem J-Link Debugger (von Segger) den Prozessor gestoppt. Das 
Senden (über SPI) wird über den PDC (DMA-Controller) gemacht, wobei 
dieser parallel zum Prozessor läuft.

Das Problem hierbei ist, dass durch den Prozessorhalt der 
Mikrocontroller-interne (Hardware)-Ablauf offensichtlich durcheinander 
gebracht wird und somit der Datenversatz entsteht.
Durch Programmierung von entsprechenden Testfunktion, bei denen auf 
einen Prozessorhalt verzichtet werden kann, wurde die Datenübertragung 
nun erfolgreich überprüft.



Dennoch, vielen Dank für die Tipps, Hinweise und Anregungen.


Dieter

von Konstantin (Gast)


Lesenswert?

Hallo,


I think WDRBT bit in SPI_MR should be set during bidirectional SPI 
transfer with PDC. This solves our issue with wrong PCSes and maybe help 
you too.

--
Konstantin

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Konstantin schrieb:
> Hallo,

He did already solve the issue, and it's been related to the debugger.

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.