;; vim: filetype=avr
;;
;; SPI interface control and use
;;
;; I/O Port
.equ SPI_DDR = DDRB
.equ SPI_PORT = PORTB
.equ SPI_SCK = PORTB5
.equ SPI_MISO = PORTB4
.equ SPI_MOSI = PORTB3
.equ SPI_SS = PORTB2
;; Internal status flags
.equ SPI_FLAGS = GPIOR0
.equ SPI_BUSY = 0
;; Settings
.set SPI_DORD = 0 ; word order
.set SPI_CPOL = 0 ; clock polarity
.set SPI_CPHA = 0 ; clock phase
.set SPI_CLOCK = 0b01 ; clock speed
;; Internal state
; Number of in/out bytes
.set SPI_BUFLEN = 2
.dseg
; Buffer for incoming frames
spi_inbuf: .byte SPI_BUFLEN
; Buffer for outgoing frames
spi_outbuf: .byte SPI_BUFLEN
.cseg
;; Initialize SPI subsystem for master operation
SPI_Init:
; Set modes
sbi SPI_DDR, SPI_SCK ; Out
sbi SPI_DDR, SPI_MOSI ; Out
sbi SPI_DDR, SPI_SS ; Out
; Drive SS high (off)
sbi SPI_PORT, SPI_SS
; Set control mode
; XXX: Enable interrupt
; Enable SPI
; MSB first
; Master mode
; Polarity/phase: Mode 0 (sample on rising edge)
; Clock rate 1/16
ldi r16, (0 << SPIE) | (1 << SPE) | (SPI_DORD << DORD) | (1 << MSTR) | (SPI_CPOL << CPOL) | (SPI_CPHA << CPHA) | (SPI_CLOCK << SPR0)
out SPCR, r16
; Flags
clr r0
out SPI_FLAGS, r0
; Start update timer
; XXX: also used for ADC
; ldi r16, 64 ; every 64k cycles
; rcall Timer0_Start
; Done
ret
;; Triggered by Timer0, updates the spi_bufs
;; Run from timer interrupt context
SPI_Update:
; skip if updating
sbic SPI_FLAGS, SPI_BUSY
ret
;; Continue
; XXX: blocks too much?
;; Send/Recv from/to SPI buffers
SPI_SendRecv:
; Flag
sbi SPI_FLAGS, SPI_BUSY
; Start of packet
cbi SPI_PORT, SPI_SS
; Init buffers
ldi r16, SPI_BUFLEN
; send/recv in reverse order
ldi XL, low(spi_inbuf + SPI_BUFLEN)
ldi XH, high(spi_inbuf + SPI_BUFLEN)
ldi YL, low(spi_outbuf + SPI_BUFLEN)
ldi YH, high(spi_outbuf + SPI_BUFLEN)
; Write
spi_sr_next:
; load+send tail byte
ld r1, -Y
out SPDR, r1
; Wait
spi_sr_wait:
in r1, SPSR
sbrs r1, SPIF
rjmp spi_sr_wait
; Read
; XXX: wrong, should be head byte?
; read+store tail byte
in r1, SPDR
st -X, r1
; Done?
dec r16
brne spi_sr_next ; if nonzero
; End of packet
sbi SPI_PORT, SPI_SS
cbi SPI_FLAGS, SPI_BUSY
; Done
ret
;; Wait for SPI to be ready for send
SPI_Wait:
; Test
in r1, SPSR
sbrs r1, SPIF
rjmp SPI_Wait
; Read SPDR to clear SPIF
in r10, SPDR
; Done
ret
;; Service SPI interrupt
SPI_Interrupt:
; XXX: disabled
reti