Merge branch 'feature/e-endstop' into merge-abc-with-homing
[clinton/Smoothieware.git] / src / libs / spi.cpp
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 }
134 // iprintf("\n");
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 }