Commit | Line | Data |
---|---|---|
7064cca7 MM |
1 | #include "spi.h" |
2 | ||
3 | #include "lpc17xx_clkpwr.h" | |
4 | #include "lpc17xx_pinsel.h" | |
5 | #include "lpc17xx_ssp.h" | |
6 | #include "lpc17xx_gpio.h" | |
7 | ||
8 | #include <stdio.h> | |
9 | ||
10 | SPI* SPI::isr_dispatch[N_SPI_INTERRUPT_ROUTINES]; | |
11 | ||
12 | class DMA; | |
13 | ||
14 | SPI::SPI(PinName mosi, PinName miso, PinName sclk) | |
15 | { | |
16 | this->mosi.port = (mosi >> 5) & 7; | |
17 | this->mosi.pin = mosi & 0x1F; | |
18 | ||
19 | this->miso.port = (miso >> 5) & 7; | |
20 | this->miso.pin = miso & 0x1F; | |
21 | ||
22 | this->sclk.port = (sclk >> 5) & 7; | |
23 | this->sclk.pin = sclk & 0x1F; | |
24 | ||
25 | FIO_SetDir(this->mosi.port, 1UL << this->mosi.pin, 1); | |
26 | FIO_SetDir(this->miso.port, 1UL << this->miso.pin, 0); | |
27 | FIO_SetDir(this->sclk.port, 1UL << this->sclk.pin, 1); | |
28 | ||
29 | if (mosi == P0_9 && miso == P0_8 && sclk == P0_7) | |
30 | { | |
31 | // iprintf("SPI: using 0.7,0.8,0.9 with SSP1\n"); | |
32 | // SSP1 on 0.7,0.8,0.9 | |
33 | sspr = LPC_SSP1; | |
34 | isr_dispatch[1] = this; | |
35 | ||
36 | LPC_PINCON->PINSEL0 &= ~((3 << (7*2)) | (3 << (8*2)) | (3 << (9*2))); | |
37 | LPC_PINCON->PINSEL0 |= ((2 << (7*2)) | (2 << (8*2)) | (2 << (9*2))); | |
38 | ||
39 | LPC_SC->PCLKSEL0 &= 0xFFCFFFFF; | |
40 | LPC_SC->PCLKSEL0 |= 0x00100000; | |
41 | ||
42 | LPC_SC->PCONP |= CLKPWR_PCONP_PCSSP1; | |
43 | } | |
44 | else if (mosi == P0_18 && miso == P0_17 && sclk == P0_15) | |
45 | { | |
46 | // iprintf("SPI: using 0.15,0.17,0.18 with SSP0\n"); | |
47 | // SSP0 on 0.15,0.16,0.17,0.18 | |
48 | sspr = LPC_SSP0; | |
49 | isr_dispatch[0] = this; | |
50 | ||
51 | LPC_PINCON->PINSEL0 &= ~(3 << (15*2)); | |
52 | LPC_PINCON->PINSEL0 |= (2 << (15*2)); | |
53 | LPC_PINCON->PINSEL1 &= ~( (3 << ((17*2)&30)) | (3 << ((18*2)&30)) ); | |
54 | LPC_PINCON->PINSEL1 |= ( (2 << ((17*2)&30)) | (2 << ((18*2)&30)) ); | |
55 | ||
56 | LPC_SC->PCLKSEL1 &= 0xFFFFF3FF; | |
57 | LPC_SC->PCLKSEL1 |= 0x00000400; | |
58 | ||
59 | LPC_SC->PCONP |= CLKPWR_PCONP_PCSSP0; | |
60 | } | |
61 | else if (mosi == P1_24 && miso == P1_23 && sclk == P1_20) | |
62 | { | |
63 | // iprintf("SPI: using 1.20,1.23,1.24 with SSP0\n"); | |
64 | // SSP0 on 1.20,1.23,1.24 | |
65 | sspr = LPC_SSP0; | |
66 | isr_dispatch[0] = this; | |
67 | ||
68 | // // LPC_PINCON->PINSEL3 &= 0xFFFC3CFF; | |
69 | // LPC_PINCON->PINSEL3 |= 0x0003C300; | |
70 | ||
71 | // LPC_PINCON->PINSEL3 &= ~( (3 << ((20*2)&30)) | (3 << ((23*2)&30)) | (3 << ((24*2)&30)) ); | |
72 | LPC_PINCON->PINSEL3 |= ( (3 << ((20*2)&30)) | (3 << ((23*2)&30)) | (3 << ((24*2)&30)) ); | |
73 | ||
74 | LPC_SC->PCLKSEL1 &= 0xFFFFF3FF; | |
75 | LPC_SC->PCLKSEL1 |= 0x00000400; | |
76 | ||
77 | LPC_SC->PCONP |= CLKPWR_PCONP_PCSSP0; | |
78 | } | |
79 | else | |
80 | { | |
81 | // iprintf("SPI: using soft-SPI\n"); | |
82 | sspr = (LPC_SSP_TypeDef *) 0; | |
83 | } | |
84 | ||
85 | if (sspr) { | |
86 | sspr->CR0 = SSP_DATABIT_8 | | |
87 | SSP_FRAME_SPI; | |
88 | sspr->CR1 = SSP_MASTER_MODE; | |
89 | frequency(10000); | |
90 | sspr->CR1 |= SSP_CR1_SSP_EN; | |
91 | } | |
92 | } | |
93 | ||
94 | SPI::~SPI() | |
95 | { | |
96 | if (sspr == LPC_SSP0) | |
97 | LPC_SC->PCONP &= CLKPWR_PCONP_PCSSP0; | |
98 | else if (sspr == LPC_SSP1) | |
99 | LPC_SC->PCONP &= CLKPWR_PCONP_PCSSP1; | |
100 | } | |
101 | ||
102 | void SPI::frequency(uint32_t f) | |
103 | { | |
104 | // CCLK = 25MHz | |
105 | // CPSR = 2 to 254, even only | |
106 | // CR0[8:15] (SCR, 0..255) is a further prescale | |
107 | ||
108 | // iprintf("SPI: frequency %lu:", f); | |
109 | delay = 25000000 / f; | |
110 | // f = 25MHz / (CPSR . [SCR + 1]) | |
111 | // CPSR . (SCR + 1) = 25MHz / f | |
112 | // min freq is 25MHz / (254 * 256) | |
113 | if (sspr) { | |
114 | if (f < 385) { | |
115 | sspr->CPSR = 254; | |
116 | sspr->CR0 &= 0x00FF; | |
117 | sspr->CR0 |= 255 << 8; | |
118 | } | |
119 | // max freq is 25MHz / (2 * 1) | |
120 | else if (f > 12500000) { | |
121 | sspr->CPSR = 2; | |
122 | sspr->CR0 &= 0x00FF; | |
123 | } | |
124 | else { | |
125 | sspr->CPSR = delay & 0xFE; | |
126 | // CPSR . (SCR + 1) = f; | |
127 | // (SCR + 1) = f / CPSR; | |
128 | // SCR = (f / CPSR) - 1 | |
129 | sspr->CR0 &= 0x00FF; | |
130 | sspr->CR0 |= (((delay / sspr->CPSR) - 1) & 0xFF) << 8; | |
131 | } | |
132 | // iprintf(" CPSR=%lu, CR0=%lu", sspr->CPSR, sspr->CR0); | |
133 | } | |
458ccccc | 134 | // iprintf("\n"); |
7064cca7 MM |
135 | } |
136 | ||
137 | void _delay(uint32_t ticks) { | |
138 | for (;ticks;ticks--) | |
139 | asm volatile("nop\n\t"); | |
140 | } | |
141 | ||
142 | uint8_t SPI::write(uint8_t data) | |
143 | { | |
144 | // _cs = 1; | |
145 | uint8_t r = 0; | |
146 | // iprintf("SPI: >0x%02X", data); | |
147 | if (sspr) { | |
148 | while ((sspr->SR & SSP_SR_TNF) == 0); | |
149 | sspr->DR = data; | |
150 | while ((sspr->SR & SSP_SR_RNE) == 0); | |
151 | r = sspr->DR & 255; | |
152 | } | |
153 | else { | |
154 | for (int i = 0; i < 8; i++) { | |
155 | FIO_ClearValue(sclk.port, 1UL << sclk.pin); // clock LOW | |
156 | ||
157 | if (data & 0x80) // WRITE | |
158 | FIO_SetValue(mosi.port, 1UL << mosi.pin); | |
159 | else | |
160 | FIO_ClearValue(mosi.port, 1UL << mosi.pin); | |
161 | data <<= 1; | |
162 | ||
163 | _delay(delay >> 1); // DELAY | |
164 | ||
165 | FIO_SetValue(sclk.port, 1UL << sclk.pin); // clock HIGH | |
166 | ||
167 | _delay(delay >> 1); // DELAY | |
168 | ||
169 | r <<= 1; | |
170 | if (FIO_ReadValue(miso.port) & (1UL << miso.pin)) // READ | |
171 | r |= 1; | |
172 | } | |
173 | FIO_ClearValue(sclk.port, 1UL << sclk.pin); | |
174 | } | |
175 | // iprintf(" <0x%02X\n", r); | |
176 | return r; | |
177 | } | |
178 | ||
179 | // TODO: timer feeds DMA feeds 0xFFs to card then we listen for responses using our interrupt | |
180 | // allow me to do something like: | |
181 | // disk.start_multi_write(int blocks, int blocksize, void *buffer); | |
182 | // enable_usb_isr(); | |
183 | // [...] | |
184 | // usb_isr() { | |
185 | // if (disk.buffer_in_use(void *buffer)) | |
186 | // return; | |
187 | // usb_ep_read(buffer); | |
188 | // if (buffer_full) | |
189 | // disk.validate_buffer(buffer); | |
190 | // if (disk.finished_transfer()) | |
191 | // disk.end_multi_write(); | |
192 | // }; | |
193 | ||
194 | bool SPI::can_DMA() | |
195 | { | |
196 | return (sspr != NULL); | |
197 | } | |
198 | ||
199 | // int SPI::setup_DMA_rx(DMA_REG *dma) | |
200 | // { | |
201 | // if (!sspr) | |
202 | // return -1; | |
203 | // | |
204 | // dma->DMACCControl = 0; | |
205 | // dma->DMACCConfiguration = 0; | |
206 | // if (sspr == LPC_SSP0) | |
207 | // dma->DMACCConfiguration |= (GPDMA_CONN_SSP0_Rx << 6); | |
208 | // if (sspr == LPC_SSP1) | |
209 | // dma->DMACCConfiguration |= (GPDMA_CONN_SSP1_Rx << 6); | |
210 | // | |
211 | // dma->DMACCConfiguration |= GPDMA_TRANSFERTYPE_M2P << 11; | |
212 | // return 0; | |
213 | // } | |
214 | // | |
215 | // int SPI::start_DMA_rx(DMA_REG *dma) | |
216 | // { | |
217 | // dma->DMACCConfiguration |= | |
218 | // } | |
219 | ||
220 | // int SPI::writeblock(uint8_t *block, int blocklen) | |
221 | // { | |
222 | // static DMA *d = new DMA(); | |
223 | // d.sourceaddr(block); | |
224 | // d.transferlength(blocklen); | |
225 | // d.destinationperipheral(sspr); | |
226 | // d.start(); | |
227 | // while (d.active()); | |
228 | // return blocklen; | |
229 | // return 0; | |
230 | // } | |
231 | ||
232 | void SPI::irq() | |
233 | { | |
234 | } | |
235 | ||
236 | void SSP0_IRQHandler(void) { | |
237 | if (SPI::isr_dispatch[0]) | |
238 | SPI::isr_dispatch[0]->irq(); | |
239 | } | |
240 | ||
241 | void SSP1_IRQHandler(void) { | |
242 | if (SPI::isr_dispatch[1]) | |
243 | (SPI::isr_dispatch[1])->irq(); | |
244 | } |