blob: 722c07dc0d5a77d73ab46fd30dbd8ef54920977f [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include "SPI.h"
8
9#include "ChipObject/tSPI.h"
10#include "DigitalInput.h"
11#include "DigitalOutput.h"
12#include "NetworkCommunication/UsageReporting.h"
13#include "Synchronized.h"
14#include "WPIErrors.h"
15
16#include <math.h>
17#include <usrLib.h>
18
19SEM_ID SPI::m_semaphore = NULL;
20
21/**
22 * Constructor for input and output.
23 *
24 * @param clk The digital output for the clock signal.
25 * @param mosi The digital output for the written data to the slave
26 * (master-out slave-in).
27 * @param miso The digital input for the input data from the slave
28 * (master-in slave-out).
29 */
30SPI::SPI(DigitalOutput &clk, DigitalOutput &mosi, DigitalInput &miso)
31{
32 Init(&clk, &mosi, &miso);
33}
34
35/**
36 * Constructor for input and output.
37 *
38 * @param clk The digital output for the clock signal.
39 * @param mosi The digital output for the written data to the slave
40 * (master-out slave-in).
41 * @param miso The digital input for the input data from the slave
42 * (master-in slave-out).
43 */
44SPI::SPI(DigitalOutput *clk, DigitalOutput *mosi, DigitalInput *miso)
45{
46 Init(clk, mosi, miso);
47}
48
49/**
50 * Constructor for output only.
51 *
52 * @param clk The digital output for the clock signal.
53 * @param mosi The digital output for the written data to the slave
54 * (master-out slave-in).
55 */
56SPI::SPI(DigitalOutput &clk, DigitalOutput &mosi)
57{
58 Init(&clk, &mosi, NULL);
59}
60
61/**
62 * Constructor for output only.
63 *
64 * @param clk The digital output for the clock signal.
65 * @param mosi The digital output for the written data to the slave
66 * (master-out slave-in).
67 */
68SPI::SPI(DigitalOutput *clk, DigitalOutput *mosi)
69{
70 Init(clk, mosi, NULL);
71}
72
73/**
74 * Constructor for input only.
75 *
76 * @param clk The digital output for the clock signal.
77 * @param miso The digital input for the input data from the slave
78 * (master-in slave-out).
79 */
80SPI::SPI(DigitalOutput &clk, DigitalInput &miso)
81{
82 Init(&clk, NULL, &miso);
83}
84
85/**
86 * Constructor for input only.
87 *
88 * @param clk The digital output for the clock signal.
89 * @param miso The digital input for the input data from the slave
90 * (master-in slave-out).
91 */
92SPI::SPI(DigitalOutput *clk, DigitalInput *miso)
93{
94 Init(clk, NULL, miso);
95}
96
97/**
98 * Destructor.
99 */
100SPI::~SPI()
101{
102 delete m_spi;
103}
104
105/**
106 * Initialize SPI channel configuration.
107 *
108 * @param clk The digital output for the clock signal.
109 * @param mosi The digital output for the written data to the slave
110 * (master-out slave-in).
111 * @param miso The digital input for the input data from the slave
112 * (master-in slave-out).
113 */
114void SPI::Init(DigitalOutput *clk, DigitalOutput *mosi, DigitalInput *miso)
115{
116 if (m_semaphore == NULL)
117 {
118 m_semaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
119 }
120
121 tRioStatusCode localStatus = NiFpga_Status_Success;
122 m_spi = tSPI::create(&localStatus);
123 wpi_setError(localStatus);
124
125 m_config.BusBitWidth = 8;
126 m_config.ClockHalfPeriodDelay = 0;
127 m_config.MSBfirst = 0;
128 m_config.DataOnFalling = 0;
129 m_config.LatchFirst = 0;
130 m_config.LatchLast = 0;
131 m_config.FramePolarity = 0;
132 m_config.WriteOnly = miso ? 0 : 1;
133 m_config.ClockPolarity = 0;
134
135 m_channels.SCLK_Channel = clk->GetChannelForRouting();
136 m_channels.SCLK_Module = clk->GetModuleForRouting();
137 m_channels.SS_Channel = 0;
138 m_channels.SS_Module = 0;
139
140 if (mosi)
141 {
142 m_channels.MOSI_Channel = mosi->GetChannelForRouting();
143 m_channels.MOSI_Module = mosi->GetModuleForRouting();
144 }
145 else
146 {
147 m_channels.MOSI_Channel = 0;
148 m_channels.MOSI_Module = 0;
149 }
150
151 if (miso)
152 {
153 m_channels.MISO_Channel = miso->GetChannelForRouting();
154 m_channels.MISO_Module = miso->GetModuleForRouting();
155 }
156 else
157 {
158 m_channels.MISO_Channel = 0;
159 m_channels.MISO_Module = 0;
160 }
161
162 m_ss = NULL;
163
164 static INT32 instances = 0;
165 instances++;
166 nUsageReporting::report(nUsageReporting::kResourceType_SPI, instances);
167}
168
169/**
170 * Configure the number of bits from each word that the slave transmits
171 * or receives.
172 *
173 * @param bits The number of bits in one frame (1 to 32 bits).
174 */
175void SPI::SetBitsPerWord(UINT32 bits)
176{
177 m_config.BusBitWidth = bits;
178}
179
180/**
181 * Get the number of bits from each word that the slave transmits
182 * or receives.
183 *
184 * @return The number of bits in one frame (1 to 32 bits).
185 */
186UINT32 SPI::GetBitsPerWord()
187{
188 return m_config.BusBitWidth;
189}
190
191/**
192 * Configure the rate of the generated clock signal.
193 * The default and maximum value is 76,628.4 Hz.
194 *
195 * @param hz The clock rate in Hertz.
196 */
197void SPI::SetClockRate(double hz)
198{
199 int delay = 0;
200 // TODO: compute the appropriate values based on digital loop timing
201 if (hz <= 76628.4)
202 {
203 double v = (1.0/hz)/1.305e-5;
204 int intv = (int)v;
205 if (v-intv > 0.5)
206 delay = intv;
207 else
208 delay = intv-1;
209 }
210 if (delay > 255)
211 {
212 wpi_setWPIError(SPIClockRateTooLow);
213 delay = 255;
214 }
215 m_config.ClockHalfPeriodDelay = delay;
216}
217
218/**
219 * Configure the order that bits are sent and received on the wire
220 * to be most significant bit first.
221 */
222void SPI::SetMSBFirst()
223{
224 m_config.MSBfirst = 1;
225}
226
227/**
228 * Configure the order that bits are sent and received on the wire
229 * to be least significant bit first.
230 */
231void SPI::SetLSBFirst()
232{
233 m_config.MSBfirst = 0;
234}
235
236/**
237 * Configure that the data is stable on the falling edge and the data
238 * changes on the rising edge.
239 */
240void SPI::SetSampleDataOnFalling()
241{
242 m_config.DataOnFalling = 1;
243}
244
245/**
246 * Configure that the data is stable on the rising edge and the data
247 * changes on the falling edge.
248 */
249void SPI::SetSampleDataOnRising()
250{
251 m_config.DataOnFalling = 0;
252}
253
254/**
255 * Configure the slave select line behavior.
256 *
257 * @param ss slave select digital output.
258 * @param mode Frame mode:
259 * kChipSelect: active for the duration of the frame.
260 * kPreLatchPulse: pulses before the transfer of each frame.
261 * kPostLatchPulse: pulses after the transfer of each frame.
262 * kPreAndPostLatchPulse: pulses before and after each frame.
263 * @param activeLow True if slave select line is active low.
264 */
265void SPI::SetSlaveSelect(DigitalOutput *ss, tFrameMode mode, bool activeLow)
266{
267 if (ss)
268 {
269 m_channels.SS_Channel = ss->GetChannelForRouting();
270 m_channels.SS_Module = ss->GetModuleForRouting();
271 }
272 else
273 {
274 m_channels.SS_Channel = 0;
275 m_channels.SS_Module = 0;
276 }
277 m_ss = ss;
278
279 switch (mode)
280 {
281 case kChipSelect:
282 m_config.LatchFirst = 0;
283 m_config.LatchLast = 0;
284 break;
285 case kPreLatchPulse:
286 m_config.LatchFirst = 1;
287 m_config.LatchLast = 0;
288 break;
289 case kPostLatchPulse:
290 m_config.LatchFirst = 0;
291 m_config.LatchLast = 1;
292 break;
293 case kPreAndPostLatchPulse:
294 m_config.LatchFirst = 1;
295 m_config.LatchLast = 1;
296 break;
297 }
298
299 m_config.FramePolarity = activeLow ? 1 : 0;
300}
301
302/**
303 * Configure the slave select line behavior.
304 *
305 * @param ss slave select digital output.
306 * @param mode Frame mode:
307 * kChipSelect: active for the duration of the frame.
308 * kPreLatchPulse: pulses before the transfer of each frame.
309 * kPostLatchPulse: pulses after the transfer of each frame.
310 * kPreAndPostLatchPulse: pulses before and after each frame.
311 * @param activeLow True if slave select line is active low.
312 */
313void SPI::SetSlaveSelect(DigitalOutput &ss, tFrameMode mode, bool activeLow)
314{
315 SetSlaveSelect(&ss, mode, activeLow);
316}
317
318/**
319 * Get the slave select line behavior.
320 *
321 * @param mode Frame mode:
322 * kChipSelect: active for the duration of the frame.
323 * kPreLatchPulse: pulses before the transfer of each frame.
324 * kPostLatchPulse: pulses after the transfer of each frame.
325 * kPreAndPostLatchPulse: pulses before and after each frame.
326 * @param activeLow True if slave select line is active low.
327 * @return The slave select digital output.
328 */
329DigitalOutput *SPI::GetSlaveSelect(tFrameMode *mode, bool *activeLow)
330{
331 if (mode != NULL)
332 {
333 *mode = (tFrameMode) (m_config.LatchFirst | (m_config.LatchLast << 1));
334 }
335 if (activeLow != NULL)
336 {
337 *activeLow = m_config.FramePolarity != 0;
338 }
339 return m_ss;
340}
341
342/**
343 * Configure the clock output line to be active low.
344 * This is sometimes called clock polarity high.
345 */
346void SPI::SetClockActiveLow()
347{
348 m_config.ClockPolarity = 1;
349}
350
351/**
352 * Configure the clock output line to be active high.
353 * This is sometimes called clock polarity low.
354 */
355void SPI::SetClockActiveHigh()
356{
357 m_config.ClockPolarity = 0;
358}
359
360/**
361 * Apply configuration settings and reset the SPI logic.
362 */
363void SPI::ApplyConfig()
364{
365 Synchronized sync(m_semaphore);
366
367 tRioStatusCode localStatus = NiFpga_Status_Success;
368 m_spi->writeConfig(m_config, &localStatus);
369 m_spi->writeChannels(m_channels, &localStatus);
370 m_spi->strobeReset(&localStatus);
371 wpi_setError(localStatus);
372}
373
374/**
375 * Get the number of words that can currently be stored before being
376 * transmitted to the device.
377 *
378 * @return The number of words available to be written.
379 */
380UINT16 SPI::GetOutputFIFOAvailable()
381{
382 tRioStatusCode localStatus = NiFpga_Status_Success;
383 UINT16 result = m_spi->readAvailableToLoad(&localStatus);
384 wpi_setError(localStatus);
385 return result;
386}
387
388/**
389 * Get the number of words received and currently available to be read from
390 * the receive FIFO.
391 *
392 * @return The number of words available to read.
393 */
394UINT16 SPI::GetNumReceived()
395{
396 tRioStatusCode localStatus = NiFpga_Status_Success;
397 UINT16 result = m_spi->readReceivedElements(&localStatus);
398 wpi_setError(localStatus);
399 return result;
400}
401
402/**
403 * Have all pending transfers completed?
404 *
405 * @return True if no transfers are pending.
406 */
407bool SPI::IsDone()
408{
409 tRioStatusCode localStatus = NiFpga_Status_Success;
410 bool result = m_spi->readStatus_Idle(&localStatus);
411 wpi_setError(localStatus);
412 return result;
413}
414
415/**
416 * Determine if the receive FIFO was full when attempting to add new data at
417 * end of a transfer.
418 *
419 * @return True if the receive FIFO overflowed.
420 */
421bool SPI::HadReceiveOverflow()
422{
423 tRioStatusCode localStatus = NiFpga_Status_Success;
424 bool result = m_spi->readStatus_ReceivedDataOverflow(&localStatus);
425 wpi_setError(localStatus);
426 return result;
427}
428
429/**
430 * Write a word to the slave device. Blocks until there is space in the
431 * output FIFO.
432 *
433 * If not running in output only mode, also saves the data received
434 * on the MISO input during the transfer into the receive FIFO.
435 */
436void SPI::Write(UINT32 data)
437{
438 if (m_channels.MOSI_Channel == 0 && m_channels.MOSI_Module == 0)
439 {
440 wpi_setWPIError(SPIWriteNoMOSI);
441 return;
442 }
443
444 Synchronized sync(m_semaphore);
445
446 while (GetOutputFIFOAvailable() == 0)
447 taskDelay(NO_WAIT);
448
449 tRioStatusCode localStatus = NiFpga_Status_Success;
450 m_spi->writeDataToLoad(data, &localStatus);
451 m_spi->strobeLoad(&localStatus);
452 wpi_setError(localStatus);
453}
454
455/**
456 * Read a word from the receive FIFO.
457 *
458 * Waits for the current transfer to complete if the receive FIFO is empty.
459 *
460 * If the receive FIFO is empty, there is no active transfer, and initiate
461 * is false, errors.
462 *
463 * @param initiate If true, this function pushes "0" into the
464 * transmit buffer and initiates a transfer.
465 * If false, this function assumes that data is
466 * already in the receive FIFO from a previous write.
467 */
468UINT32 SPI::Read(bool initiate)
469{
470 if (m_channels.MISO_Channel == 0 && m_channels.MISO_Module == 0)
471 {
472 wpi_setWPIError(SPIReadNoMISO);
473 return 0;
474 }
475
476 tRioStatusCode localStatus = NiFpga_Status_Success;
477 UINT32 data;
478 {
479 Synchronized sync(m_semaphore);
480
481 if (initiate)
482 {
483 m_spi->writeDataToLoad(0, &localStatus);
484 m_spi->strobeLoad(&localStatus);
485 }
486
487 // Do we have anything ready to read?
488 if (GetNumReceived() == 0)
489 {
490 if (!initiate && IsDone() && GetOutputFIFOAvailable() == kTransmitFIFODepth)
491 {
492 // Nothing to read: error out
493 wpi_setWPIError(SPIReadNoData);
494 return 0;
495 }
496
497 // Wait for the transaction to complete
498 while (GetNumReceived() == 0)
499 taskDelay(NO_WAIT);
500 }
501
502 m_spi->strobeReadReceivedData(&localStatus);
503 data = m_spi->readReceivedData(&localStatus);
504 }
505 wpi_setError(localStatus);
506
507 return data;
508}
509
510/**
511 * Stop any transfer in progress and empty the transmit FIFO.
512 */
513void SPI::Reset()
514{
515 tRioStatusCode localStatus = NiFpga_Status_Success;
516 m_spi->strobeReset(&localStatus);
517 wpi_setError(localStatus);
518}
519
520/**
521 * Empty the receive FIFO.
522 */
523void SPI::ClearReceivedData()
524{
525 tRioStatusCode localStatus = NiFpga_Status_Success;
526 m_spi->strobeClearReceivedData(&localStatus);
527 wpi_setError(localStatus);
528}