1 ;; vim: filetype=avr |
|
2 ;; |
|
3 ;; SPI interface control and use |
|
4 ;; |
|
5 |
|
6 ;; I/O Port |
|
7 .equ SPI_DDR = DDRB |
|
8 .equ SPI_PORT = PORTB |
|
9 .equ SPI_SCK = PORTB5 |
|
10 .equ SPI_MISO = PORTB4 |
|
11 .equ SPI_MOSI = PORTB3 |
|
12 .equ SPI_SS = PORTB2 |
|
13 |
|
14 ;; Internal status flags |
|
15 .equ SPI_FLAGS = GPIOR0 |
|
16 .equ SPI_BUSY = 0 |
|
17 |
|
18 ;; Settings |
|
19 .set SPI_DORD = 0 ; word order |
|
20 .set SPI_CPOL = 0 ; clock polarity |
|
21 .set SPI_CPHA = 0 ; clock phase |
|
22 .set SPI_CLOCK = 0b01 ; clock speed |
|
23 |
|
24 ;; Internal state |
|
25 ; Number of in/out bytes |
|
26 .set SPI_BUFLEN = 2 |
|
27 |
|
28 .dseg |
|
29 ; Buffer for incoming frames |
|
30 spi_inbuf: .byte SPI_BUFLEN |
|
31 |
|
32 ; Buffer for outgoing frames |
|
33 spi_outbuf: .byte SPI_BUFLEN |
|
34 |
|
35 .cseg |
|
36 |
|
37 ;; Initialize SPI subsystem for master operation |
|
38 SPI_Init: |
|
39 ; Set modes |
|
40 sbi SPI_DDR, SPI_SCK ; Out |
|
41 sbi SPI_DDR, SPI_MOSI ; Out |
|
42 sbi SPI_DDR, SPI_SS ; Out |
|
43 |
|
44 ; Drive SS high (off) |
|
45 sbi SPI_PORT, SPI_SS |
|
46 |
|
47 ; Set control mode |
|
48 ; XXX: Enable interrupt |
|
49 ; Enable SPI |
|
50 ; MSB first |
|
51 ; Master mode |
|
52 ; Polarity/phase: Mode 0 (sample on rising edge) |
|
53 ; Clock rate 1/16 |
|
54 ldi r16, (0 << SPIE) | (1 << SPE) | (SPI_DORD << DORD) | (1 << MSTR) | (SPI_CPOL << CPOL) | (SPI_CPHA << CPHA) | (SPI_CLOCK << SPR0) |
|
55 out SPCR, r16 |
|
56 |
|
57 ; Flags |
|
58 clr r0 |
|
59 out SPI_FLAGS, r0 |
|
60 |
|
61 ; Start update timer |
|
62 ; XXX: also used for ADC |
|
63 ; ldi r16, 64 ; every 64k cycles |
|
64 ; rcall Timer0_Start |
|
65 |
|
66 ; Done |
|
67 ret |
|
68 |
|
69 ;; Triggered by Timer0, updates the spi_bufs |
|
70 ;; Run from timer interrupt context |
|
71 SPI_Update: |
|
72 ; skip if updating |
|
73 sbic SPI_FLAGS, SPI_BUSY |
|
74 ret |
|
75 |
|
76 ;; Continue |
|
77 ; XXX: blocks too much? |
|
78 |
|
79 ;; Send/Recv from/to SPI buffers |
|
80 SPI_SendRecv: |
|
81 ; Flag |
|
82 sbi SPI_FLAGS, SPI_BUSY |
|
83 |
|
84 ; Start of packet |
|
85 cbi SPI_PORT, SPI_SS |
|
86 |
|
87 ; Init buffers |
|
88 ldi r16, SPI_BUFLEN |
|
89 |
|
90 ; send/recv in reverse order |
|
91 ldi XL, low(spi_inbuf + SPI_BUFLEN) |
|
92 ldi XH, high(spi_inbuf + SPI_BUFLEN) |
|
93 ldi YL, low(spi_outbuf + SPI_BUFLEN) |
|
94 ldi YH, high(spi_outbuf + SPI_BUFLEN) |
|
95 |
|
96 ; Write |
|
97 spi_sr_next: |
|
98 ; load+send tail byte |
|
99 ld r1, -Y |
|
100 out SPDR, r1 |
|
101 |
|
102 ; Wait |
|
103 spi_sr_wait: |
|
104 in r1, SPSR |
|
105 sbrs r1, SPIF |
|
106 rjmp spi_sr_wait |
|
107 |
|
108 ; Read |
|
109 ; XXX: wrong, should be head byte? |
|
110 ; read+store tail byte |
|
111 in r1, SPDR |
|
112 st -X, r1 |
|
113 |
|
114 ; Done? |
|
115 dec r16 |
|
116 brne spi_sr_next ; if nonzero |
|
117 |
|
118 ; End of packet |
|
119 sbi SPI_PORT, SPI_SS |
|
120 |
|
121 cbi SPI_FLAGS, SPI_BUSY |
|
122 |
|
123 ; Done |
|
124 ret |
|
125 |
|
126 ;; Wait for SPI to be ready for send |
|
127 SPI_Wait: |
|
128 ; Test |
|
129 in r1, SPSR |
|
130 sbrs r1, SPIF |
|
131 rjmp SPI_Wait |
|
132 |
|
133 ; Read SPDR to clear SPIF |
|
134 in r10, SPDR |
|
135 |
|
136 ; Done |
|
137 ret |
|
138 |
|
139 ;; Service SPI interrupt |
|
140 SPI_Interrupt: |
|
141 ; XXX: disabled |
|
142 reti |
|
143 |
|
144 |
|