Hallo Sebastian!
Ich bin gerade dabei, 2 AVR (AT90S8515)per SPI miteinander kommunizieren
zu lassen. Wenn man es einmal gebacken gekriegt hat, ist es gar nicht so
schwer... :)
Bei meiner Methode läuft das im Polling, da ich (noch) keine
Zeitkritischen Abläufe habe und zwischendurch nix anderes machen muß.
Eleganter wäre es sicher per Interrupt...
So funktioniert es programmtechnisch (bei mir):
1. Ein AVR muß als Master koviguriert werden, der andere als Slave. das
geschieht über das SPCR (SPI-Control-Register). In diesem register
stellt man auch noch ne ganze Menge anderer Sachen ein, wie:
Taktfrequenz, Interrupt en-/dis-able, Phasenlage, etc.
2. Pro Durchgang wird ein Byte zwischen Master und Slave "getauscht". Du
schreibst also ein Byte in das SPDR (SPI-Data-Register) des Slaves.
Dieser Wartet dann einfach. (SPI muß natürlich enabled sein, das kann
mit Schritt 1 zusammen machen, da gleiches Control-Register).
Theoretisch kannst Du während des Wartens auch was anderes machen, kommt
bei mir aber nicht vor. Achso, wichtig ist noch, daß der Eingang /SS am
Slave-µC auf low gehalten wird, sonst is nix mit SPI...
3. Beim Master wird ebenfalls das SPI enabled (geschieht bei mir mit
Schritt 1 zusammen) und anschließend in das SPDR-Register das zu
übertragende Byte geschrieben. das schreiben des zu übertragenden Bytes
startet automatisch die Hardware SPI. Nun kann man auch hier entweder
abwarten (Schleife) oder was anderes machen.
4. Die Hardware überträgt jetzt im Hintergrund mit der in Schritt 1
eingestellten Geschwindigkeit. Nachdem die Übertragung zu Ende ist, wird
entweder ein Interrupt aufgerufen (wenn enabled) oder man kann das
Status-Bit im SPSR (Status-Register) abrufen um zu prüfen, ob die
Übertragung fertig ist. Letztere Methode benutze ich beim Warten, um zu
prüfen, ob die Übertragung fertig ist.
5. Wenn alles erledigt ist, hast Du im Master SPDR (Data-Register)das
Byte, welches vorher im Slave war, und im Slave jenes aus dem Master.
Die Bytes wurden also in einem Rutsch zwischen master und Slave
getauscht.
Hier ist meine Subroutine des Slaves. Wird sie aufgerufen, wartet sie
eine gewisse Zeit lang darauf, daß der Master sendet. Sendet er nicht
bis die Warteschleife abgelaufen ist, wird zu "Carderror" verzweigt. In
dem Register "tosend" muß beim Aufrufen das Byte stehen, welches der
Master bekommen soll. Nach der Übertragung und Beendigung der Subroutine
steht im Register "recvd" das Byte, welches vom Master kommt. Klingt
alles ganz kompliziert, ist aber ganz einfach:
; ************************* SPI Slave Routine ************************
spislave:
ldi temp, 0b01001001 ; SPI enable, Interrupt disable, Slave,
Clock= f/16
out SPCR, temp ; Init-werte ins SPI-Control-Register
out SPDR, tosend ; das Byte für den Master bereit stellen
ldi r17, $FF
waitSPI1: ; **** warten, bis die Daten übertragen wurden
dec r17 ; Zähler einen erniedrigen
breq carderror ; wenn zulange gedauert hat, dann fehler
in temp, SPSR ; Hole werte vom SPI Status Register
sbrs temp, 7 ; Wenn SPIF gesetzt (=Übertragung Ende), dann...
rjmp waitSPI1 ; ...verlasse schleife, sonst nochmal
in recvd, SPDR ; Byte vom SPI in "recvd" speichern
ldi temp, 0b00001001 ; SPI disable
out SPCR, temp ; ->
ret
; **********
(sorry, daß die Formatierung beim Posten verloren gehen... ich hoffe,
man kann es lesen)
Hardware-technisch habe ich hierbei die SPI-Pins
"Mosi(PB5)"/"Miso(PB6)"/"SCK(PB7)" beider µCs 1zu1 miteinander
verbunden, Pin "/SS(PB4)" liegt auf Masse (wichtig!).
Der Code des Masters sieht genauso aus, nur ist hier die erste Zeile
etwas anders, damit das Ding als Master arbeitet. Außerdem entfällt die
Verzweigung zur Fehlerroutine, wenns zu lange dauert.
ldi temp, 0b01011001 ; SPI enable, Interrupt disable, Master,
Clock= f/16
So, ich hoffe, ich konnte die SPI-Geschichte etwas erklären. Keine
Haftung/Garantie für Richtigkeit und Vollständigkeit ;)
ciao,
Andi