Hallo werte µC-Gemeinde
Einen tiny2313 für eine vollständige SPI Kommunikation zu verwenden ist
keine so gute Idee. Wenn man allerdings auf das zurücksenden von Daten
per 'DO' verzichten kann, dann hab ich da was für euch.
Ich verwende es in einer Positionsanzeige mit 3x8 7-Segmentanzeigen. Der
tiny2313 arbeitet hierbei als Slave und nimmt die Daten vom SPI
entgegen.
Momentan läuft der SPI Takt des Masters mit 16Mhz/64 = 250kHz. Der SPI
Slave ala tiny2313 hat einen /CS Eingang (PC_INT). Die SPI Auswertung
erfolgt im Interrupt. Und wenn alle Bytes empfangen wurden, wird ein
Flag im GPIOR0-Register gesetzt welches man in der Hauptschleife
abfragen kann.
Der Vorteil der Interruptbehandlung des SPI liegt darin, dass man wärend
des SPI-Transfers weiterhin andere Aufgaben erledigen kann.
Hier die Defines:
1
;***** Register-Variablen
2
.def SREG_SAVE = r0 ; Sicherungskopie des SREG-Registers im Interrupt
3
4
; Konstanten
5
.equ SPI_RD_BYTES = 12 ; zu empangene Bytes per SPI
6
7
; Flags in GPOIR0
8
.equ SPI_ON = 2 ; 0= SPI Off | 1= SPI On
9
.equ SPI_OVERRUN = 3 ; 0= kein SPI Data OverRun | 1= SPI Data OverRun
10
.equ SPI_UNDERRUN = 4 ; 0= kein SPI Data UnderRun | 1= SPI Data UnderRun
11
.equ NEW_SPI_DATA = 7 ; Flag zeigt an, dass neue Daten von dem USI-SPI zur Verfuegung stehen
12
13
; Port defines
14
.equ USI_SPI_PORT = PORTB ; fest vorgeschrieben
15
.equ usi_SPI_SCK = 7 ; fest vorgeschrieben
16
.equ usi_SPI_DO = 6 ; fest vorgeschrieben
17
.equ usi_SPI_DI = 5 ; fest vorgeschrieben
18
.equ usi_SPI_CS = 4 ; 0..4 von PORTB frei waehlbar
Wenn die /CS Leitung vom Master auf Low gezogen wurde wird dies erkannt
und dem SPI Interrupt das abspeichern der empfangenen Bytes angewiesen.
Im SPI Interrupt wird geprüft, dass nicht zuviel Bytes gesendet werden.
(festgelegte Anzahl zu empfangender Bytes per Frame in 'SPI_RD_BYTES')
Andernfalls wird das 'OVERRUN'-Flag gesetzt und das SPI Frame verworfen.
Sollte nun vom Master die /CS Leitung wieder auf High gezogen, dann wird
dies wieder im Pin-Change-Interrupt erkannt und geprüft, ob auch schon
alle Bytes eines SPI-Frames empfangen wurde. Sollte das der Fall sein,
wird das 'NEW_SPI_DATA'-Flag im GPOIR0 gesetzt und damit angezeigt, dass
ein neuer Datensatz im 'SPI_RD_BUFFER' im SRAM liegt. Darauf kann dann
die Hauptschleife reagieren.
Hier der Pin Change Interrupt der /CS Leitung:
Jetzt noch das Unterprogramm 'memcpy' zur Speicherkopie. Die sollte man
machen, da ja der 'SPI_RD_BUFFER' in einem weiteren SPI-Interrupt wieder
verändert werden kann. So kann man mit der Kopie erstmal ganz beruhigt
arbeiten.
1
memcpy:
2
ldi r17,SPI_RD_BYTES
3
load_p X,(SPI_RD_BUFFER)
4
load_p Y,(MEM_BUFFER)
5
cli ; Atomarer Zugriff begin
6
memcpy_loop:
7
ld r16,X+
8
st Y+,r16
9
dec r17
10
brne memcpy_loop
11
sei ; Atomarer Zugriff ende
12
cbi GPIOR0,NEW_SPI_DATA
13
ret
Und so kommt man nun an die SPI Daten ran:
1
; RESET = Programmbeginn
2
RESET:
3
rcall init_usi_spi ; USI SPI initialisieren (PORTs, INTs und SRAM)
4
; Hauptschleife
5
main:
6
sbic GPIOR0,NEW_SPI_DATA
7
rcall new_spi_data_receive
8
; .....
9
; ..... tue irgendwas in der Hauptschleife
10
; .....
11
rjmp main
12
13
new_spi_data_receive:
14
rcall memcpy ; ab hier stehen die SPI Data Bytes im 'MEM_BUFFER'
15
; ...... ; zur weiteren Verarbeitung bereit
16
17
ret
Ich glaub dass ist jetzt ganz schön lang geworden. Wenn noch Fragen sind
oder Ihr Anregungen/Verbesserungen habt, nur her damit.
Grüße Steffen