Schönen heilig Abend,
ich bin gerade dabei, das SP-Interface des ATmega809 zu initialisieren.
Der ATmega809 soll als Slave fungieren, so sieht meine Initialisierung
aus:
1
voidinitSPI(){
2
/* Set alternative SPI pins */
3
PORTMUX.TWISPIROUTEA|=PORTMUX_SPI0_ALT1_gc;
4
/* Enable Receive Interrupt */
5
SPI0.INTCTRL|=SPI_IE_bm;
6
/* Enable SPI */
7
SPI0.CTRLA|=SPI_ENABLE_bm;
8
}
in einem anderen Code-Abschnitt hab ich den MISO-Pin entsprechend als
Ausgang definiert:
Das reinkommende SPI-Signal hab ich mit einem Logic-Analyzer gemessen
(siehe Bild) - sieht meiner Meinung nach gut aus.
Nun ist das Problem, dass ich beim Debuggen mit dem Atmel-ICE mit
Breakpoint auf die 2. Zeile der ISR nur genau 1x einen Interrupt
bekomme, wenn ich danach das Program weiterlaufen lasse, kommt kein
Interrupt mehr.
Erst wenn ich mich wieder vom Device trenne, es neu programmiere (inkl.
Reset) und dann wieder im Debug-Modus bin, kommt der Interrupt genau 1x.
Ganz offensichtlich hab ich die SPI-Peripherie genauso wenig verstanden
wie den TCA0 - mag mir hier jemand auf die Sprünge helfen?
Frohes Fest
Edit: Um sicher zu gehen, dass der ATmega nirgends stecken bleibt, hab
ich in der Endlosschleife ein einfaches "asm("nop")" mit Breakpoint
versehen, wo der ATmega dann auch fleißig reinspringt. Laufen tut er
also nach dem Interrupt weiterhin.
Moin Max,
ich kenn mich zwar mit ATmega nicht aus, aber bei vielen CPUs wenn ich
mich richtig erinnere muss man am Ende des IRQs das Interruptflag
löschen (bei manchen kann man auch einstellen das es automatisch
gelöscht wird).
Tut man das nicht wird der IRQ nicht mehr ausgelöst. Nur so als Idee.
Viele Grüße
egon
Hi egon,
egon schrieb:> muss man am Ende des IRQs das Interruptflag löschen
Laut Datenblatt wird das mit dem Lesen von SPIn.DATA mit gelöscht.
Hab's auch mal probeweise im INTFLAGS-Register gesetzt, aber hat nichts
gebracht
Hey Max.
Wer ist der Master?
Wenn der in seinem read(...) bzw. write(...) nur 1 Byte sendet/liest,
dann taktet er auch nur 1 Byte...
SCK geht ja vom Master aus.
Wenn du mit dem Logic Analyser nichts siehst (außer dem 1 Byte), dann
liegt ja auf der Datenleitung auch nichts...?
Oder verstehe ich deine Problemstellung falsch?
Bei SPI empfängst Du ja nicht nur, sondern Du musst für jedes Byte, was
Du sendest, auch eines empfangen bzw für jedes empfangene Byte auch
eines senden, und sei es ein 0-Byte.
fchk
Frank K. schrieb:> Bei SPI empfängst Du ja nicht nur, sondern Du musst für jedes Byte, was> Du sendest, auch eines empfangen bzw für jedes empfangene Byte auch> eines senden, und sei es ein 0-Byte.
Achso - tatsächlich? Das war mir nicht bewusst.
Da muss ich mal blöd fragen: Wie implementiert man denn dann ein auf
Nachrichten basiertes Protokoll?
Also ich hatte die Idee, dass ein Master per SPI den ATmega nach Daten
fragen kann. Dazu hab ich mir zwei ganz einfache Nachrichten ausgedacht,
die jeweils aus 4 bzw. 5 Byte bestehen.
Der ATmega hat aber erst nach den 4 bzw. 5 empfangenen Bytes die
vollständigen Informationen um die Anfrage adäquat beantworten zu
können, d.h. davor macht es keinen Sinn dem Master zu antworten?
Marc V. schrieb:> Master sendet 1 Byte, MEGA809 empfängt 1 Byte, wo liegt dein Problem?
Der Master sendet kontinuierlich 1 Byte jede Sekunde. Der Interrupt wird
allerdings nur beim aller ersten Byte im ATmega809 (der das Byte
empfängt) erzeugt.
Max M. schrieb:> Frank K. schrieb:>> Bei SPI empfängst Du ja nicht nur, sondern Du musst für jedes Byte, was>> Du sendest, auch eines empfangen bzw für jedes empfangene Byte auch>> eines senden, und sei es ein 0-Byte.>> Achso - tatsächlich? Das war mir nicht bewusst.> Da muss ich mal blöd fragen: Wie implementiert man denn dann ein auf> Nachrichten basiertes Protokoll?>> Also ich hatte die Idee, dass ein Master per SPI den ATmega nach Daten> fragen kann. Dazu hab ich mir zwei ganz einfache Nachrichten ausgedacht,> die jeweils aus 4 bzw. 5 Byte bestehen.> Der ATmega hat aber erst nach den 4 bzw. 5 empfangenen Bytes die> vollständigen Informationen um die Anfrage adäquat beantworten zu> können, d.h. davor macht es keinen Sinn dem Master zu antworten?
Schau Dir mal das Datenblatt eines SPI-ADCs an.
https://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf
Hardware-SPI-Slaves schalten ihr MISO erst ein, wenn sie tatsächlich
Nutzdaten haben, aber Du musst notfalls irgendwelche Dummy-Bytes senden.
fchk
Max M. schrieb:> Da muss ich mal blöd fragen: Wie implementiert man denn dann ein auf> Nachrichten basiertes Protokoll?
Zb mit multimaster, was die avrs können. Der logische master schickt
Kommandos an die logischen slaves, diese antworten dann im Nachgang.
Spi mäßig ist immer der sendende der master.
> Laut Datenblatt wird das mit dem Lesen von SPIn.DATA mit gelöscht.
Den Satz
"IF is cleared by hardware when executing the corresponding interrupt
vector"
kann ich nicht bestätigen, bei meinem Atmega4809 funktioniert nur die
zweite Methode
"Alternatively, the IF flag can be cleared by first reading the
SPIn.INTFLAGS register when IF is set, and then accessing the SPIn.DATA
register".
Hat allerdings am Verhalten nichts geändert. Probeweise hab ich nun auch
noch einen Breakpoint auf den TCA0-Interrupt gesetzt, der alle 30ms
laufen müsste, der wird nun auch nur noch genau 1x aufgerufen (d.h.
SPI0_INT_vect und TCA0_CMP0_vect laufen jeweils 1x).
Sobald ich die initSPI()-Funktion nicht mehr aufrufe, läuft der Timer
wieder wie gewohnt alle 30ms (logischerweise der SPI-Interrupt dafür
nicht, aber das ist klar).
Hab auch mal ein *sei()* an das Ende der SPI-ISR getan aber das hat auch
nichts bewirkt.
S. Landolt schrieb:> Auch Ihnen frohe Festtage!>> Ich habe von C kaum Ahnung, was bewirkt dieses const in>>> const uint8_t data = SPI0.DATA;> ?
Const sagt: Read Only
Die Variable namens data darf ihren Wert nicht ändern.
Versuchst du es, hauts dir der Compiler um die Ohren.
Auch erlaubt es weiter gehende Optimierungen.
Und weshalb wird dann so etwas der veränderliche Wert aus SPI0.DATA
zugewiesen?
(Man entschuldige die vielleicht dumme Frage eines
Assemblerprogrammierers)
SPI-Slave auf einem MC ist ein pain in the ass.
Man muß erstmal ein Protokoll aufsetzen, um dem Slave zu sagen, daß und
was er senden soll und ihm dann Zeit lassen, den Sendepuffer zu füllen.
Der Slave muß dazu zwingend einen Sendepuffer oder DMA haben, sonst
kriegt man Timingprobleme, da SPI kein Handshake unterstützt. Zumindest
die älteren ATmega/tiny sind also nicht geeignet.
I2C oder UART sind deutlich besser geeignet, am besten ist natürlich CAN
für eine Verbindung zwischen mehreren MCs.
Das SPI benutze ich ausschließlich als Master zu ADCs, DACs usw.
S. Landolt schrieb:> Und weshalb wird dann so etwas der veränderliche Wert aus> SPI0.DATA> zugewiesen?> (Man entschuldige die vielleicht dumme Frage eines> Assemblerprogrammierers)
Das ist sowas, wie ein Dialog mit dem Kompiler.
Assembler sind in der Hinsicht blöd.
Aber Kompilern kann man seine Wünsche mitteilen.
Hier:
Liebster Kompiler ich möchte mit dem Wert von SPI0.DATA unter dem Namen
data arbeiten. Stelle bitte sicher, dass der Wert für diesen
Geltungsbereich unveränderlich ist.
Wenn möglich, wirds gemacht.
Vielleicht anders, als du erwartest.
Wenn nicht möglich, haut er dir den Kram um die Ohren.
Danke für die Erklärung.
Dann ist mir erstmal nicht klar, was bei Max M. schiefläuft;
andererseits lassen die Programmbruchstücke an Aussagekraft zu wünschen
übrig.
Peter D. schrieb:> Das SPI benutze ich ausschließlich als Master zu ADCs, DACs usw.
Okay, da schickst du aber vermutlich auch Befehle an die ICs und die
schicken dir dann Daten zurück?
S. Landolt schrieb:> Wie beim letzten Mal hier ein Assemblerprogramm für den ATmega4809,> vielleicht hilft es ja beim Verständnis.
Danke.
Hat es einen konkreten Grund warum du hier virtuelle Ports verwendest?
Ich verwende SPI0 leider an anderen Pins, deswegen hab ich das Beispiel
noch etwas abgeändert:
> Hat es einen konkreten Grund warum du hier virtuelle Ports verwendest?
Faulheit - es erlaubt die Verwendung von sbi.
> Der Interrupt wird allerdings nie angesprungen.
Tja, keine Ahnung, tut mir leid.
Ahh tut mir Leid. Der Fehler war, dass die Funktionspointer des Puffers
vom "dramHandler" nicht initialisiert waren. Normalerweise hätte ich
hier erwartet, dass der Controller in einen exception-handler springt
oder allgemein einfach "abstürzt", deswegen hab ich mir nichts böses
gedacht, dass die Hauptschleife weiterhin durchlaufen wird.
Sorry wegen dem blöden Fehler meinerseits :(
Schöne Feiertage noch
Also, ich habe jetzt das von Ihnen modifizierte Programm wieder
übernommen, die bei mir nötigen Änderungen, "4809" sowie das Addieren
von $2000 bei den Adressen, durchgeführt und die Signale auf den unteren
C-Port gelegt: bei mir läuft es.
Max M. schrieb:> Okay, da schickst du aber vermutlich auch Befehle an die ICs und die> schicken dir dann Daten zurück?
Der große Unterschied zu einem MC ist jedoch, der ADC liefert mir die
32Bit in einem Stück. Ich muß den Master keine Gedenkpausen einlegen
lassen, bis der ADC das nächste Byte in das Senderegister geladen hat.
Ein MC kann jedoch in einem anderen Interrupt hängen und dann liefert
mir das SPI Lottozahlen statt Daten. Aber selbst, wenn nicht, hat ein
Slave ohne Sendepuffer/DMA nur 1/2 SPI-Clock Zeit, das nächste Byte
bereit zu stellen.
Die MC-Lösung braucht mindestens ein Protokoll mit CRC, um zwischen
Lottozahlen und Daten unterscheiden zu können.
Max M. schrieb:> Der Fehler war, dass die Funktionspointer des Puffers> vom "dramHandler" nicht initialisiert waren.
So ist das eben, der Fehler ist grundsätzlich im nicht geposteten Code.
Was soll das Rumgetue mit Code-Schnipselchen. Ist der Code so geheim,
daß Du ihn nicht als compilierfähigen Anhang senden kannst?
Peter D. schrieb:> Ist der Code so geheim,> daß Du ihn nicht als compilierfähigen Anhang senden kannst?
Ich dachte mein Code wäre fehlerfrei, deswegen. War etwas überheblich
von mir. Hab mir auch gedacht, dass alles passt da der µC in der
Mainloop weitergelaufen ist.