Forum: Mikrocontroller und Digitale Elektronik SPI-Slave ATSAMD51 / 24 Bit Frames empfangen


von Sortland (Gast)


Lesenswert?

Hi,

nach Tagen erfolgloser Bastelei und Sucherei muss ich mal mein 
komplettes Problem hier reinstellen - vielleicht hat ja jemand eine 
Idee.

Ich habe ein SPI Signal:
- /SS geht auf LOW
- 24 Bits werden übertragen, die Daten sind jeweils bei der fallenden 
Flanke von SCK gültig
- /SS geht auf HIGH

Dementsprechend habe ich konfiguriert:

- CTRLA.CPOL = 0 und CTRLA.CPHA = 1 für die fallende SCK-Flanke
- CTRLA.MODE = 2 für den SPI-Slave
- CTRLB.SSDE = 1 für /SS auf LOW, wenn Daten übertragen werden
- CTRLB.RXEN = 1 um Daten empfangen zu können
- CTRLC.DATA32B = 1 um mehr als 8 Bit in den Eingangspuffer zu bekommen
- LENGTH.LEN = 3 und LENGTH.LENEN = 1 um eben meine 24 Bit zu empfangen

Ob Daten angekommen sind, frage ich über INTFLAG.RXC ab.

Mein Problem: Die Daten, die eintrudeln, sind nur so ähnlich wie das, 
was ich erwarte. CTRLA.DORD auf 0 oder 1 setzen ändert daran auch 
nichts, wirklich 100% passen die Daten nie.

Weiterhin findet sich im DATA-Register immer mal 0, obwohl das vom 
Master nie abgesendet wurde.

Was ich nicht verstehe: wozu sind diese Werte in LENGTH.LEN und 
LENGTH.LENEN nötig, wenn doch /SS die Framelänge bestimmt? Setze ich 
diese beiden Werte nicht, werden mir alle 32 Bit in DATA beschrieben als 
ob /SS keine Bedeutung hätte (und die Daten sind noch kaputter).

Auch völlig unklar: was machen die Clocks beim SPI wenn im Slave-Mode 
der Master eigentlich einen Clock sendet? Mangels besserer Ideen habe 
ich "Core" und "Slow" infach mal auf 96 MHz gesetzt.

Irgend welche Ideen, warum mein SPI-Master so gar nicht funktionieren 
mag? Oder gibt es eventuell irgendwo einen funktionierenden Beispielcode 
für den ATSAMD51?

Danke!

von Rudolph R. (rudolph)


Lesenswert?

- LENGTH.LEN = 3

Müsste da nicht 24 stehen für den SPI?
Beim I2C ist das die Anzahl der Bytes, aber beim SPI müsste das die 
Anzahl der Bits sein.

von Sortland (Gast)


Lesenswert?

Rudolph R. schrieb:
> Müsste da nicht 24 stehen für den SPI?

Das Manual ist da nicht wirklich eindeutig: "In 32-bit Extension mode, 
this bit field configures the data length after which the flags 
INTFLAG.RCX or
INTFLAG.DRE are raised."

Wenn ich da 24 reinschreibe, ist mein DATA-Register wieder komplett 
voll, sprich alle 32 Bit sind mit irgendwas belegt, nicht nur die 
unteren 24. Also gehe ich davon aus, dass hier Byte gemeint sind, zumal 
man Werte bis maximal 256 dort reinschreiben kann (was ja auch mehr wäre 
als die 32 Bit vom DATA-Register).

Der Sinn erschließt sich mir allerdings nicht wirklich.

von Rudolph R. (rudolph)


Angehängte Dateien:

Lesenswert?

War jetzt nicht so ganz lustig das auszuprobieren, ich nutze den SPI 
sonst nur im Master-Modus und musste erstmal zwei Platinen zusammen 
bringen.

Ich benutze ATSAME51 mit externem 16MHz Quarz.
Auf beiden Platinen habe ich SERCOM5 auf einem Stecker liegen,
für den Slave musste ich die Belegung etwas anpassen, SS kann nur auf 
Pad2 liegen.

Beim Master habe ich das hier laufen:
1
spi_delay++;
2
if(spi_delay > 19)
3
{
4
  spi_delay = 0;
5
6
  cs_set();
7
  small_delay(4);
8
  spi_transmit(0x55);
9
  small_delay(5);
10
  spi_transmit(0xaa);
11
  small_delay(5);
12
  spi_transmit(counter++);
13
  small_delay(4);
14
  cs_clear();
15
}

Also nichts wildes, das ganze in einem 5ms Task verpackt.
Der Frame hat von CS-Low bis CS-High fast 53µs.
Ach ja, den SPI habe ich auf 1MHz eingestellt, CPOL und CPHA auf beiden 
Seiten Null.

Auf der Slave Seite frage ich nur in einer while(1) das RXC Flag ab.
Wenn ich den Endwert von spi_delay anpasse, dann ändert sich auch die 
Blinkfrequenz von der Debug-LED an meinem Slave.

Die Slave-Seite hängt an.
Ohne das hier:
1
SERCOM5->SPI.LENGTH.reg = SERCOM_SPI_LENGTH_LENEN | SERCOM_SPI_LENGTH_LEN(3);

Landen in der Variable spi_data immer 4 Bytes.

Aktiviere ich die Zeile sind es immer 3 Bytes.
Gehe ich mit dem Debugger auf den Controller sehe ich in der Variable 
zum Beispiel: 0x004caa55
Continue / Break -> 0x00d0aa55
Continue / Break -> 0x0015aa55

Also das LENGTH Register ist wirklich die Länge in Bytes und das RXC 
Flag im STATUS Register wird 1 wenn die eingestellte Anzahl empfangen 
wurde.

Wofür auch immer das gut ist, dass man im LENGTH Register bis 0xff 
einstellen kann.

Interessant wäre jetzt nur mal, ob man auch schräge Längen empfangen 
kann.

Laut Datenblatt gibt es noch das hier:
Bit 1 – TXC Transmit Complete
In Slave mode, this flag is set when the _SS pin is pulled high.
Writing '0' to this bit has no effect.
Writing '1' to this bit will clear the flag.

Das funktioniert nur irgendwie nicht, TXC wird nie gesetzt.
Im Debugger sehe ich nur RXC und DRE.
Löschen von RXC durch Schreiben von DATA hilft da auch nicht.
In den Errata finde ich dazu auch nichts.

Als Master sind mir die Dinger lieber. :-)

von Sortland (Gast)


Lesenswert?

Rudolph R. schrieb:

> Ohne das hier:SERCOM5->SPI.LENGTH.reg = SERCOM_SPI_LENGTH_LENEN |
> SERCOM_SPI_LENGTH_LEN(3);
>
> Landen in der Variable spi_data immer 4 Bytes.
> Aktiviere ich die Zeile sind es immer 3 Bytes.

Diese Beobachtung habe ich auch schon gemacht. Die Spezifikation 
beschreibt eigentlich, dass solche Framelengen über /SS ermittelt werden 
- nur scheint das eben nicht zu funktionieren, ohne den Teil wird DATA 
immer komplett gefüllt.

> Also das LENGTH Register ist wirklich die Länge in Bytes
> Wofür auch immer das gut ist, dass man im LENGTH Register bis 0xff
> einstellen kann.
> Interessant wäre jetzt nur mal, ob man auch schräge Längen empfangen
> kann.

...und wofür das auch immer gut ist, dass man das in Bytes konfiguriert, 
statt in Bits, dann wären auch schräge Längen möglich - nur dazu habe 
ich nix gefunden (das müsste eigentlich wieder automatisch über /SS 
gehen können, aber Pustekuchen).

> Laut Datenblatt gibt es noch das hier:
> Bit 1 – TXC Transmit Complete
> In Slave mode, this flag is set when the _SS pin is pulled high.
> Das funktioniert nur irgendwie nicht, TXC wird nie gesetzt.

Nö, das tut bei mir nicht, man muss wirklich RXC abfragen.

> Die Slave-Seite hängt an.

Perfekt, Danke!!!

von Sortland (Gast)


Lesenswert?

OK, das funktionert trotzdem nicht. Was ich geändert habe:

- 120 MHz Clock für den SPI (wie im Beispielcode oben)
- Initialisierung auch wie im Beispiel oben
- sämtliche Kombinationen von CPOL/CPHA durchprobiert
- Eingangssignale überprüft (sieht gut aus, keine Störungen)

Was jetzt noch anders ist bei mir: ich verwende andere Eingänge für 
meine SERCOMs:

- SCK an PA22 (-> PINMUX_PA22D_SERCOM5_PAD1)
- /SS an PA20 (-> PINMUX_PA20C_SERCOM5_PAD2)
- Daten1 an PA21 (-> PINMUX_PA21C_SERCOM5_PAD3)

- SCK an PA05 (-> PINMUX_PA05D_SERCOM0_PAD1)
- /SS an PA06 (-> PINMUX_PA06D_SERCOM0_PAD2)
- Daten2 an PA07 (-> PINMUX_PA07D_SERCOM0_PAD3)

Pinmuxing und MISO/MOSI-Mapping sollten passen (die entstammen auch dem 
ATMEL Start Code, ich habe sie auch noch mal gegengecheckt und konnte 
kein Problem entdecken):

CTRLA.DOPO ist jeweils 0, CTRLA.DIPO jeweils 3.

Könnte da trotzdem noch irgend was durcheinander kommen?

Allerdings hätte ich ja vermutet, dass bei falschem Pinmuxing oder 
falscher MISO/MOSI Signalzuweisung einfach gar nichts empfangen wird 
(also RXC niemals auf 1 geht), weil kein gültiges SPI-Frame erkannt 
wird, oder?

Auf der anderen Seite ist es auch so, dass RXC korrekterweise nie 
gesetzt wird, wenn ich mein Eingangssignal wegnehme, es wird also auch 
nicht der Radiosender von nebenan empfangen.

von Rudolph R. (rudolph)


Lesenswert?

Sortland schrieb:
> ATMEL Start Code

Damit mag ich nicht mal mehr planen für den Schaltplan,da sind mir 
einfach zu viele Fehler drin.

Du hast doch schon ein einfaches Beispiel von mir, pass das doch direkt 
mal an - und zwar genau so wie das ist, nur für die anderen Pins.
Okay, das init_clock() passt vielleicht nicht, aber wenn man das weg 
lässt läuft der GCLOCK0 mit 48MHz Default auch nicht ganz langsam.

von Sortland (Gast)


Lesenswert?

Rudolph R. schrieb:
> Du hast doch schon ein einfaches Beispiel von mir, pass das doch direkt
> mal an - und zwar genau so wie das ist, nur für die anderen Pins.
> Okay, das init_clock() passt vielleicht nicht, aber wenn man das weg
> lässt läuft der GCLOCK0 mit 48MHz Default auch nicht ganz langsam.

Das habe ich gemacht und exakt deinen Code verwendet (nur halt mit dem 
zu meiner Hardware passenden Pinmuxing). Ergebnis: genau der gleiche 
Datenmüll wie mit dem ATMEL Start Code.

Wobei ich "Datenmüll" jetzt etwas genauer spezifizieren kann. Ich sende 
Frames mit folgendem Format:

- 2 Steuerbits (sind immer 01)
- 20 Bits Nutzdaten
- 2 Paritätsbits (werden derzeit noch nicht verwendet).

Nutzdaten auf den Datenkanälen 1 und 2 sind z.B.

0xF0D2B und 0x8F051

es kommen aber an:

0xF2B0D und 0x1F058

Da ließe sich jetzt sogar ein Muster erkennen und die Bits entsprechend 
tauschen. Aber: das passt nicht immer, beim nächsten Neustart können die 
Daten anders gemischt sein. insgesamt scheint es aber nur 2 oder 3 
verschiedene Varianten von Datenvermischung zu geben.

von Sortland (Gast)


Lesenswert?

Da meine Zahlenbeispiele oben wegen der 2-Bit-Verschiebung nicht ganz 
eindeutig waren, habe ich es noch mal mit den Rohdaten (also inklusive 
der oberen und unteren beiden Bits probiert. Es scheitn wirklich so zu 
sein, dass die Bytes wild gemischt werden:
1
             Daten1       Daten2
2
Gesendet:    0x00448dfc   0x007e97c0
3
Empfangen:   0x0044fc8d   0x00c0977e

Wenn das nicht irgend ein blöder Seiteneffekt von irgend was ganz was 
anderem ist, ist das schon eher dubios...

von Filler (Gast)


Lesenswert?

>Die Spezifikation
>beschreibt eigentlich, dass solche Framelengen über /SS ermittelt werden
>- nur scheint das eben nicht zu funktionieren, ohne den Teil wird DATA
>immer komplett gefüllt.

Das ist ein Irrtum, den Du dringend aufklären solltest.
Framelänge hat NIX mit /SS zu tun.

Ausser Slave selektieren und evtl/wahrscheinlich SPI State Machine 
zurücksetzen
macht das GAR nix.

Man könnte SS sogar ständig aktiv lassen, ohne dass das an der SPI 
Funktion viel ändern würde,

von Filler (Gast)


Lesenswert?

Hab's nicht genau durchgelesen, was Du gemacht hast, aber i.d.R muss man
24 bits als 3 mal 8 bits übertragen.
/SS zwischen den Bytes NICHT deaktivieren, also

assert /SS
byte 1
byte 2
byte 3
deassert /SS

von Filler (Gast)


Lesenswert?

Und
>- 120 MHz Clock für den SPI (wie im Beispielcode oben)

scheint mir reichlich hoch.
Übliche Tatktfreq für SPI sind < 50MHz.
Oft besteht auch ein Zusammenhang zwischen dem Core Clock
und dem SPI, z.B SPI clock < 1/4 COre Clock.

von Sortland (Gast)


Lesenswert?

Filler schrieb:
> scheint mir reichlich hoch.
> Übliche Tatktfreq für SPI sind < 50MHz.
> Oft besteht auch ein Zusammenhang zwischen dem Core Clock
> und dem SPI, z.B SPI clock < 1/4 COre Clock.

Es geht hier um den SPI-Slave, die SCK-Frequenz des Masters ist deutlich 
unter den 120 MHz. Dabei handelt es sich nur um den Clock für das 
SPI-Gerät in der MCU, das hat nichts mit den nach außen geführten 
Signalen zu tun. Der externe SCK liegt bei etwa 3 MHz.

von Rudolph R. (rudolph)


Lesenswert?

Filler schrieb:
> Das ist ein Irrtum, den Du dringend aufklären solltest.

"Bit 1 – TXC Transmit Complete
In Slave mode, this flag is set when the _SS pin is pulled high."

Du kennst Dich also mit dem Controller aus, dann erkläre uns doch mal 
diesen Passus im Datenblatt, zu was das gut ist und wie man das benutzt.

von Michael M. (sortland)


Angehängte Dateien:

Lesenswert?

So, ich habe mal ein paar Messungen gemacht, um Probeme bei den 
Eingangssignalen auszuschließen. Das erste Bild ist direkt auf der 
Leitung (differetielles Signal, auf einer Ader gemessen). Dort sind SCK, 
/SS und Daten gut zu sehen, das Timing sollte meiner Meinung nach passen 
(leider nicht mehr im Bild: /SS geht nach den 24 Clocks/Bits nach oben, 
die Datenleitung geht gleichzeitig auch nach oben).

Das Signal ist dort nicht ganz sauber, deswegen das zweite Bild: dieses 
ist nach dem Receiverbaustein aufgenommen, jetzt mit den Logikpegeln, 
wie sie direkt in den ATSAMD eingespeist werden (da fehlt eins der 
Signale, weil ich einfach keine dritte Hand zum Festhalten der Probe auf 
den entsprechenden Pin hatte, das Singal ist aber da). Hier ist zu 
sehen, dass die Singale wesentlich sauberer sind, meiner Einschätzung 
nach sollten die keine Probleme machen.

Damit sollte sich das als mögliche Ursache ja ausschließen lassen!?

: Bearbeitet durch User
von Rudolph R. (rudolph)


Angehängte Dateien:

Lesenswert?

Was mir an den Bildern aufgefallen ist, mein Timing vom Master ist 
dagegen ja extrem entspannt.

Also habe ich das gerade mal hochgeschraubt.

Das erste was mir wieder aufgefallen ist, die SERCOMs sind mit 100 MHz 
maximalem core Takt angegeben.
Okay, das ist bei 85°C und bei 125°C sind es 90 MHz.
Bei 20°C und sonst nichts an wird die eine SERCOM Einheit die 120 MHz 
schon packen.
Aber da es dazu keine Angabe gibt, habe ich den Takt für Master und 
Slave auf 100MHz abgesenkt.
Und da ich keinen Bock hatte die zweite PLL anzuwerfen und einen 
weiteren GCLOCK zu belegen, läut der CPU-Core jetzt auch auf 100 MHz.

Statt dem zahmen SPI-Transfer von oben habe ich jetzt das hier im Master 
laufen:
1
PORT->Group[1].OUTCLR.reg = PORT_PB01;
2
SERCOM5->SPI.DATA.reg = 0x55;
3
while((SERCOM5->SPI.INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC) == 0);
4
SERCOM5->SPI.DATA.reg = 0xaa;
5
while((SERCOM5->SPI.INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC) == 0);
6
SERCOM5->SPI.DATA.reg = counter;
7
while((SERCOM5->SPI.INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC) == 0);
8
PORT->Group[1].OUTSET.reg = PORT_PB01;
9
counter++;

Um das DATA Register nach dem Transfer nicht lesen zu müssen habe ich 
den Empfänger abgeschaltet.
Die Lücken zwischen den Bytes und zum CS bekomme ich so nicht mehr 
kleiner.
Das wird jetzt auch nicht mehr alle 150ms ausgeführt, sondern alle 2ms.

Zusätzlich habe ich den SPI-Clock am Master von 1MHz auf 25MHz erhöht.
SERCOM5->SPI.BAUD.reg = 1;

Und erstaunlicherweise läuft das immer noch.

Beim Slave habe ich nicht nur den Takt auf 100MHz reduziert, ich 
compiliere das auch gerade ohne Optimierung und mit spi_data und counter 
als globale volatile Variablen.
Der Grund ist das ich die Variablen mit dem SEGGER J-Scope V6.11 über 
die SWD Schnittstelle nebenbei lese.
Und da kommen die 2ms ins Spiel, mein SEGGER J-Link edu mini schafft 
"nur" 1000 Messungen pro Sekunde.

Ich will gar nicht wissen, wie das mit dem Oszi aussieht, aber im 
J-Scope schnurrt das einfach so ohne Aussetzer durch.

von Michael M. (sortland)


Lesenswert?

Ursprünglich hatte ich die beiden SPI-Slaves auch mal auf 96 MHz laufen, 
das hat für das Ergebnis keinen Unterschied gemacht. Auf die 120 MHz bin 
ich nur hochgegangen, um mit deinem Beispielcode von oben identisch zu 
sein (die beiden SPI-Master, die ebenfalls vorhanden sind, habe ich auch 
mal mit 120 MHz probiert - klappt problemlos und das Ausgabetempo ist 
echt enorm).

Als Optimierung verwende ich nur -Og um im Debugger noch was erkennen zu 
können, da sehe ich also auch keinen Unterschied.

: Bearbeitet durch User
von Michael M. (sortland)


Angehängte Dateien:

Lesenswert?

Einen großen Unterschied gibt es in meinem Projekt allerdings schon 
noch: die Erzeugung der Clocks. Die ist zugegebenermaßen etwas wirr, 
aber der einzige Weg, den ich gefunden habe, um intern getaktet auf 120 
Mhz zu kommen (und nicht irgend einen krummen Takt von rund aber eben 
nicht exakt 120 MHz zu erhalten):

32 kHz Low Power internal Oscillator ->
GCG3 ->
DFLL48M mit 48 Mhz ->
GCG1 mit Teiler 15 auf 3,2 Mhz (mehr lässt die folgende Stufe nicht zu) 
->
DPLL0 mit Faktor 36 und 16 auf 120 MHz ->
GCG0 mit 120 Mhz ->
Core

Vom GCG1 zweigt auch noch der DPLL1 mit den 90 MHz für die SPIs ab. ICh 
hätte gerne was einfacheres, aber grundsätzlich sollte das ja nicht so 
seltsame Auswirkungen haben?

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.