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!
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.
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:
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. :-)
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!!!
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.
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.
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.
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...
>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,
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
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.
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.
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.
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!?
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:
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.
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.
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?