Forum: Mikrocontroller und Digitale Elektronik SPI Geschwindigkeit XMega


von Christoph M. (chrito)


Angehängte Dateien:

Lesenswert?

Moin,

hab zwei XMega 8e5, die über SPI kommunizieren.

Jetzt versuche ich die Geschwindigkeit bis zum Maximum hochzudrehen. 
Dazu habe ich den Modus mit Pufferung aktiviert (SPI_PORT.CTRLB = 
0b11000000).
Optimierung ist auf -O1. SPI-IRs haben höchste Priorität.

Die Datenübertragung ist sauber, die Werte kommen an. Auf dem Oszi sehe 
ich jetzt, dass zwischen den Bytes immer eine riesige Lücke ist. Also 
1µSek erstes Byte, dann 1,5µSek Loch, dann 1µSek zweites Byte, dann 
1,5µSek Loch und so weiter. Ich komme damit auf 400 kB pro Sekunde 
Maximum. Ist hier Ende Gelände? Im Datenblatt find ich nichts zur 
maximalen Geschwindigkeit.

Danke!
Christoph



Für alle, die es intressiert, hier der Code des Masters, der des Slave 
ist ähnlich:
1
ISR(SPIC_INT_vect)        // SPI-Interrupt
2
{  if (SPI_PORT.STATUS & 0b10000000)  // empfangenes Byte bereit zum Auslesen?
3
  {  SPI_Empfangspuffer[Index_Empfangen] = SPI_PORT.DATA;     // empfangenes Byte abholen    
4
    Index_Empfangen++;
5
    if (Index_Empfangen==3)
6
    {  
7
      asm("NOP");  // Assembler-Nop
8
    }
9
  }
10
  if (SPI_PORT.STATUS & 0b00100000)  // Sende-Puffer bereit?
11
  {  if (Index_Senden<3)
12
    {  SPI_PORT.DATA = SPI_Sendepuffer[Index_Senden];
13
      Index_Senden++;
14
    }
15
    else
16
    {  PORTC.OUTSET =0b00010000;    // Chip Select ausschalten
17
      SPI_PORT.INTCTRL &= ~0b00100000;   // deaktiviere Interrupts bei bereitem Sendepuffer
18
    }
19
  }
20
}
21
    
22
23
void initialisiere_SPI_Master(void)  // SPI an PORTC einstellen
24
{  cli();                // Interrupts deaktivieren
25
  SPI_PORT.CTRL =    0b01010000;    // SPI master, clock idle low, data setup on trailing edge, data sampled on leading edge, Geschw 32MHZ/4
26
  SPI_PORT.CTRLB   = 0b11000000;    // gepuffert Modus 1, sofort laden
27
  SPI_PORT.INTCTRL = 0b10000010;    // 00: aus, 01: niedrige, 10: mittelere, 11: hohe Priorität => mittlere, denn Master darf zwischendurch anhalten, Slave muss reagieren
28
  Pufferindex=0;
29
  sei();                // Interrupts aktivieren
30
}

von Bastian W. (jackfrost)


Lesenswert?

Nur mit der ISR und dem Init wir dir hier kaum einer helfen können.

1,5 us sind bei 32 MHz 48 Takte. Schau einfach im dissambly was der 
xMega von Start vom senden bis zum nächsten Byte macht.

Gruß JackFrost

von Frickelfritze (Gast)


Lesenswert?

Wenn du jedes Byte per Interrupt holst wird das so langsam sein.
Die ISR verschlingt ja einiges an Zeit/Overhead.

Meine XMega128A1 konnten / können auch SPI mit Double Speed,
d.h. bei 32 MHz Taktfrequenz läuft das SPI mit maximal 16MHz
und nicht mit Clock/4.

von Dieter F. (Gast)


Lesenswert?


von Frickelfritze (Gast)


Lesenswert?

Christoph M. schrieb:
> Auf dem Oszi sehe ich jetzt ....  (Scheisse)

Sollen wir mal sammeln gehen für Tastköpfe?

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


Lesenswert?

Frickelfritze schrieb:
> Wenn du jedes Byte per Interrupt holst wird das so langsam sein.

Sehe ich genauso.  Interrupt bei SPI hat höchstens dann Sinn, wenn
man mit sehr niedriger SPI-Taktrate arbeitet.  Wenn man versucht, da
maximal Geschwindigkeit rauszuholen, geht das nur mit Polling.  Sind
ja eh nur ein paar CPU-Takte, die man damit verwartet.

Alternativ sollte Burst-SPI beim Xmega ja auch per DMA machbar sein,
sofern das mit dem Client auch passt.

von c-hater (Gast)


Lesenswert?

Jörg W. schrieb:

> Sehe ich genauso.  Interrupt bei SPI hat höchstens dann Sinn, wenn
> man mit sehr niedriger SPI-Taktrate arbeitet.  Wenn man versucht, da
> maximal Geschwindigkeit rauszuholen, geht das nur mit Polling.  Sind
> ja eh nur ein paar CPU-Takte, die man damit verwartet.

Das ist Unsinn. Wenn man SPI wirklich so ausnutzt, sind Interrupts 
tatsächlich nicht zielführend, dann braucht man aber auch kein Polling, 
sondern nur optimale (taktzyklengenaue) Ansteuerung, erst dann ergibt so 
ein Burst-Betrieb irgendeinen Sinn.

Ja, das geht nicht mit C, but who cares... In C geht es immerhin, wenn 
man DMA dafür benutzen kann. Aber am Ende jeder DMA-Sequenz steht dann 
doch wieder eine ISR...

Und wie wenig effizient in C implementierte ISRs sind, brauche ich ja 
wohl kaum noch zu erwähnen. Höchstens auf ausdrücklichen Wunsch...

von Dieter F. (Gast)


Lesenswert?

c-hater schrieb:
> Aber am Ende jeder DMA-Sequenz steht dann
> doch wieder eine ISR...

Kann es sein, dass man ab und an auch Daten "verarbeiten" will? Nur mal 
so gefragt ...

von c-hater (Gast)


Lesenswert?

Dieter F. schrieb:

> Kann es sein, dass man ab und an auch Daten "verarbeiten" will? Nur mal
> so gefragt ...

Natürlich, das ist sogar der absolute Normalfall.

Das geht am am Einfachsten, wenn man möglichst linear Code vor sich 
hinrödeln lassen kann. Das ist, wofür z.B. C-Compiler optimieren. Das 
können sie (inzwischen) sogar sehr gut.

Nur ist die böse Realität halt nie so gnädig, die Eingabe genau in dem 
Moment bereit zu stellen, wo der Code sie braucht und nur sehr selten 
ist sie so gnädig, das Ergebnis der Berechnungen instantan im vollen 
Umfang genau in dem Moment weiterleiten zu können, wenn sie vorliegen.

Kurz: die bösen IO-Erfordernisse sind oft der Knackpunkt, nicht die 
eigentliche Rechenaufgabe...

von Christoph M. (chrito)


Lesenswert?

Frickelfritze schrieb:
> Wenn du jedes Byte per Interrupt holst wird das so langsam sein.
> Die ISR verschlingt ja einiges an Zeit/Overhead.

Ok, ihr meint also die ISR ist zu lahm... da ich mit den 
SPI-Hardware-Puffern arbeite, und die ISR während der Übertragung 
bereits nachlädt, hätte ich jetzt nicht darauf getippt.

Werde mal ausprobieren, die Daten in einer stupiden while-Schleife in 
die Hardware zu ballern, nur mal um zu gucken, ob es dann schneller 
wird.

VG

von Dieter F. (Gast)


Lesenswert?

Du kannst Dir ja auch mal die Verwendung des USART im SPI-Mode 
anschauen.

von Christoph M. (chrito)


Lesenswert?

Danke für eure Tipps! Es ist tatsächlich die ISR, die alles ausbremst.

Mit dem Code unten bin ich bei ca. 1MB/Sekunde.
1
PORTC.OUTCLR = 0b00010000;  // Chip Select aktivieren
2
  
3
while (Index_Senden<3)
4
{  if (SPI_PORT.STATUS & 0b00100000)  // Sende-Puffer bereit?
5
  {   SPI_PORT.DATA = SPI_Sendepuffer[Index_Senden];
6
      Index_Senden++;
7
  }
8
}
9
    
10
PORTC.OUTSET =0b00010000;    // Chip Select ausschalten

Mit Overhead werden es wahrscheinlich um die 500 kBps. Muss wohl mal 
EDMA ausprobieren... Das sieht zwar kompliziert aus, aber wohl die 
einzige Lösung mit wenig CPU-Last und viel Geschwindigkeit!?

Danke noch mal! :)

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


Lesenswert?

Christoph M. schrieb:
> Es ist tatsächlich die ISR, die alles ausbremst.

Interruptannahme, Registersicherung etc. verplempert einfach viel
Zeit.

> Muss wohl mal EDMA ausprobieren... Das sieht zwar kompliziert aus, aber
> wohl die einzige Lösung mit wenig CPU-Last und viel Geschwindigkeit!?

Wenn man längere Sequenzen am Stück senden möchte, ist das die
sinnvollste Variante.

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.