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
Ich würde auf eine falsch konfigurierte Einstellung der Polarität/Phase/Selects tippen.
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
hast du schon beim atmel support angefragt? die jungs sind da relativ flott mit hinweisen
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
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.
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
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
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
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.

