PropWare  3.0.0.229
C++ objects and CMake build system for Parallax Propeller
spi.h
Go to the documentation of this file.
1 
26 #pragma once
27 
28 #include <PropWare/PropWare.h>
29 #include <PropWare/gpio/pin.h>
32 
33 namespace PropWare {
34 
43 class SPI : public PrintCapable,
44  public ScanCapable {
45  public:
58  /* Raw text version of the above HTML table
59  *
60  * SPI Mode CPOL CPHA
61  * 0 0 0
62  * 1 0 1
63  * 2 1 0
64  * 3 1 1
65  */
71  };
72 
78  enum class BitMode {
79  LSB_FIRST,
80  MSB_FIRST
81  };
82 
91  } ErrorCode;
92 
93  public:
94  static const int32_t DEFAULT_FREQUENCY = 100000;
95 
96  public:
103  static SPI &get_instance () {
104  static SPI defaultInstance;
105  return defaultInstance;
106  }
107 
108  public:
119  SPI (const Pin::Mask mosi = Pin::Mask::NULL_PIN, const Pin::Mask miso = Pin::Mask::NULL_PIN,
120  const Pin::Mask sclk = Pin::Mask::NULL_PIN, const uint32_t frequency = DEFAULT_FREQUENCY,
121  const Mode mode = Mode::MODE_0, const BitMode bitmode = BitMode::MSB_FIRST)
122  : m_mode(mode),
123  m_bitmode(bitmode) {
124  this->set_mosi(mosi);
125  this->set_miso(miso);
126  this->set_sclk(sclk);
127  this->set_clock(frequency);
128  }
129 
133  ~SPI () {
134  this->m_mosi.set_dir_in();
135  this->m_sclk.set_dir_in();
136  }
137 
141  void set_mosi (const Port::Mask mask) {
142  SPI::reset_pin_mask(this->m_mosi, mask);
143  }
144 
148  void set_miso (const Port::Mask mask) {
149  SPI::reset_pin_mask(this->m_miso, mask);
150  this->m_miso.set_dir_in();
151  }
152 
156  void set_sclk (const Port::Mask mask) {
157  SPI::reset_pin_mask(this->m_sclk, mask);
158  this->set_mode(this->m_mode);
159  }
160 
166  void set_mode (const Mode mode) {
167  this->m_mode = mode;
168 
169  if (0x02U & static_cast<unsigned int>(mode))
170  this->m_sclk.set();
171  else
172  this->m_sclk.clear();
173  }
174 
181  void set_bit_mode (const BitMode bitmode) {
182  this->m_bitmode = bitmode;
183  }
184 
193  PropWare::ErrorCode set_clock (const uint32_t frequency) {
194  const uint32_t MAX_CLOCK = CLKFREQ / 20;
195  if (MAX_CLOCK < frequency)
196  return INVALID_FREQ;
197  else {
198  this->m_clkDelay = (CLKFREQ / frequency) >> 1U;
199  return NO_ERROR;
200  }
201  }
202 
208  int32_t get_clock () const {
209  return CLKFREQ / (this->m_clkDelay << 1U);
210  }
211 
224  void shift_out (uint8_t bits, uint32_t value) const {
225  switch (this->m_bitmode) {
226  case BitMode::MSB_FIRST:
227  this->shift_out_msb_first(bits, value);
228  break;
229  case BitMode::LSB_FIRST:
230  this->shift_out_lsb_first(bits, value);
231  break;
232  }
233  }
234 
242  uint32_t shift_in (const unsigned int bits) const {
243  const bool clockPhase = static_cast<bool>(static_cast<unsigned int>(this->m_mode) & 0x01U);
244  if (clockPhase) {
245  switch (this->m_bitmode) {
246  case BitMode::MSB_FIRST:
247  return this->shift_in_msb_phs1(bits);
248  case BitMode::LSB_FIRST:
249  return this->shift_in_lsb_phs1(bits);
250  }
251  } else {
252  switch (this->m_bitmode) {
253  case BitMode::MSB_FIRST:
254  return this->shift_in_msb_phs0(bits);
255  case BitMode::LSB_FIRST:
256  return this->shift_in_lsb_phs0(bits);
257  }
258  }
259  return 0;
260  }
261 
270  void shift_out_block_msb_first_fast (const uint8_t buffer[], size_t numberOfBytes) const {
271  __asm__ volatile (
272 #define ASMVAR(name) FC_ADDR(#name "%=", "SpiBlockWriteStart%=")
273  FC_START("SpiBlockWriteStart%=", "SpiBlockWriteEnd%=")
274  " jmp #" FC_ADDR("loopOverBytes%=", "SpiBlockWriteStart%=") " \n\t"
275 
276  // Temporary variables
277  "bitIdx%=: \n\t"
278  " nop \n\t"
279  "data%=: \n\t"
280  " nop \n\t"
281 
282 
283  "loopOverBytes%=: \n\t"
284  " rdbyte " ASMVAR(data) ", %[_bufAdr] \n\t"
285  " mov " ASMVAR(bitIdx) ", #8 \n\t"
286  " ror " ASMVAR(data) ", " ASMVAR(bitIdx) " \n\t"
287 
288  "loopOverBits%=: \n\t"
289  " rol " ASMVAR(data) ", #1 wc \n\t"
290  " muxc outa, %[_mosi] \n\t"
291  " xor outa, %[_sclk] \n\t"
292  " xor outa, %[_sclk] \n\t"
293  " djnz " ASMVAR(bitIdx) ", #" FC_ADDR("loopOverBits%=", "SpiBlockWriteStart%=") " \n\t"
294 
295  // Write the word back to the buffer in HUB memory
296  " add %[_bufAdr], #1 \n\t"
297 
298  " djnz %[_numberOfBytes], #" FC_ADDR("loopOverBytes%=", "SpiBlockWriteStart%=") " \n\t"
299 
300  " or outa, %[_mosi] \n\t"
301  FC_END("SpiBlockWriteEnd%=")
302 #undef ASMVAR
303  : [_bufAdr] "+r"(buffer),
304  [_numberOfBytes] "+r"(numberOfBytes)
305  :[_mosi] "r"(this->m_mosi.get_mask()),
306  [_sclk] "r"(this->m_sclk.get_mask())
307  );
308  }
309 
317  void shift_in_block_mode0_msb_first_fast (const uint8_t *buffer, size_t numberOfBytes) const {
318  __asm__ volatile (
319 #define ASMVAR(name) FC_ADDR(#name "%=", "SpiBlockReadStart%=")
320  FC_START("SpiBlockReadStart%=", "SpiBlockReadEnd%=")
321  " jmp #" FC_ADDR("outerLoop%=", "SpiBlockReadStart%=") " \n\t"
322 
323  // Temporary variables
324  "bitIdx%=: \n\t"
325  " nop \n\t"
326  "data%=: \n\t"
327  " nop \n\t"
328 
329 
330  "outerLoop%=: \n\t"
331  " mov " ASMVAR(data) ", #0 \n\t"
332  " mov " ASMVAR(bitIdx) ", #8 \n\t"
333 
334  "loop%=: \n\t"
335  " test %[_miso], ina wc \n\t"
336  " xor outa, %[_sclk] \n\t"
337  " rcl " ASMVAR(data) ", #1 \n\t"
338  " xor outa, %[_sclk] \n\t"
339  " djnz " ASMVAR(bitIdx) ", #" FC_ADDR("loop%=", "SpiBlockReadStart%=") " \n\t"
340 
341  // Write the word back to the buffer in HUB memory
342  " wrbyte " ASMVAR(data) ", %[_bufAdr] \n\t"
343  " add %[_bufAdr], #1 \n\t"
344 
345  " djnz %[_numberOfBytes], #" FC_ADDR("outerLoop%=", "SpiBlockReadStart%=") " \n\t"
346  FC_END("SpiBlockReadEnd%=")
347  : [_bufAdr] "+r"(buffer),
348  [_numberOfBytes] "+r"(numberOfBytes)
349  :[_miso] "r"(this->m_miso.get_mask()),
350  [_sclk] "r"(this->m_sclk.get_mask())
351  );
352 #undef ASMVAR
353  }
354 
355  virtual void put_char (const char c) {
356  this->shift_out(8, (uint32_t) c);
357  }
358 
359  virtual void puts (const char string[]) {
360  char *sPtr = (char *) string;
361  while (sPtr)
362  this->put_char(*sPtr++);
363  }
364 
365  virtual char get_char () {
366  char c = 0;
367  return c;
368  }
369 
376  void print_error_str (const Printer &printer, const ErrorCode err) const {
377  const char str[] = "SPI Error ";
378  const int relativeErr = err - BEG_ERROR;
379 
380  switch (err) {
381  case INVALID_FREQ:
382  printer << str << relativeErr << ": Frequency set too high";
383  break;
384  default:
385  // Is the error an SPI error?
386  if (err > BEG_ERROR && err < (BEG_ERROR + END_ERROR))
387  printer << "Unknown SPI error " << relativeErr << '\n';
388  else
389  printer << "Unknown error " << err << '\n';
390  }
391  }
392 
393  protected:
394 
395  void shift_out_msb_first (uint32_t bits, uint32_t data) const {
396 #pragma GCC diagnostic push
397 #pragma GCC diagnostic ignored "-Wuninitialized"
398  unsigned int clock;
399  __asm__ volatile (
400  FC_START("SpiSendMsbFirstStart%=", "SpiSendMsbFirstEnd%=")
401  " ror %[_data], %[_bitCount] \n\t"
402  " mov %[_clock], %[_clkDelay] \n\t"
403  " add %[_clock], CNT \n\t"
404 
405  "loop%=: \n\t"
406  " rol %[_data], #1 wc \n\t"
407  " muxc outa, %[_mosi] \n\t"
408  " waitcnt %[_clock], %[_clkDelay] \n\t"
409  " xor outa, %[_sclk] \n\t"
410  " waitcnt %[_clock], %[_clkDelay] \n\t"
411  " xor outa, %[_sclk] \n\t"
412  " djnz %[_bitCount], #" FC_ADDR("loop%=", "SpiSendMsbFirstStart%=") " \n\t"
413 
414  " or outa, %[_mosi] \n\t"
415  FC_END("SpiSendMsbFirstEnd%=")
416  : [_bitCount] "+r"(bits),
417  [_data] "+r"(data),
418  [_clock] "+r"(clock)
419  : [_mosi] "r"(this->m_mosi.get_mask()),
420  [_sclk] "r"(this->m_sclk.get_mask()),
421  [_clkDelay] "r"(this->m_clkDelay)
422  );
423 #pragma GCC diagnostic pop
424  }
425 
426  void shift_out_lsb_first (uint32_t bits, uint32_t data) const {
427 #pragma GCC diagnostic push
428 #pragma GCC diagnostic ignored "-Wuninitialized"
429  unsigned int clock;
430  __asm__ volatile (
431  FC_START("SpiSendLsbFirstStart%=", "SpiSendLsbFirstEnd%=")
432  // Local variable declaration
433  " mov %[_clock], CNT \n\t"
434  " add %[_clock], %[_clkDelay] \n\t"
435 
436  "loop%=: \n\t"
437  " ror %[_data], #1 wc '' move LSB into carry \n\t"
438  " muxc OUTA, %[_mosi] \n\t"
439  " waitcnt %[_clock], %[_clkDelay] \n\t"
440  " xor OUTA, %[_sclk] \n\t"
441  " waitcnt %[_clock], %[_clkDelay] \n\t"
442  " xor OUTA, %[_sclk] \n\t"
443  " djnz %[_bitCount], #" FC_ADDR("loop%=", "SpiSendLsbFirstStart%=") " \n\t"
444 
445  " or outa, %[_mosi] \n\t"
446  FC_END("SpiSendLsbFirstEnd%=")
447  : [_bitCount] "+r"(bits),
448  [_data] "+r"(data),
449  [_clock] "+r"(clock)
450  : [_mosi] "r"(this->m_mosi.get_mask()),
451  [_sclk] "r"(this->m_sclk.get_mask()),
452  [_clkDelay] "r"(this->m_clkDelay)
453  );
454 #pragma GCC diagnostic pop
455  }
456 
457  uint32_t shift_in_msb_phs0 (unsigned int bits) const {
458 #pragma GCC diagnostic push
459 #pragma GCC diagnostic ignored "-Wuninitialized"
460  unsigned int clock;
461  unsigned int tempData;
462  __asm__ volatile (
463  FC_START("SpiReadMsbPhs0Start%=", "SpiReadMsbPhs0End%=")
464  " ror %[_data], %[_bitCount] '' move MSB into bit 31 \n\t"
465  " mov %[_clock], %[_clkDelay] \n\t"
466  " add %[_clock], CNT \n\t"
467 
468  "loop%=: \n\t"
469  " test %[_miso], ina wc \n\t"
470  " waitcnt %[_clock], %[_clkDelay] \n\t"
471  " xor outa, %[_sclk] \n\t"
472  " rcl %[_data], #1 \n\t"
473  " waitcnt %[_clock], %[_clkDelay] \n\t"
474  " xor outa, %[_sclk] \n\t"
475  " djnz %[_bitCount], #" FC_ADDR("loop%=", "SpiReadMsbPhs0Start%=") " \n\t"
476  FC_END("SpiReadMsbPhs0End%=")
477  : [_bitCount] "+r"(bits),
478  [_clock] "+r"(clock),
479  [_data] "+r"(tempData)
480  :[_miso] "r"(this->m_miso.get_mask()),
481  [_sclk] "r"(this->m_sclk.get_mask()),
482  [_clkDelay] "r"(this->m_clkDelay)
483  );
484 #pragma GCC diagnostic pop
485  return tempData;
486  }
487 
488  uint32_t shift_in_lsb_phs0 (const unsigned int bits) const {
489 #pragma GCC diagnostic push
490 #pragma GCC diagnostic ignored "-Wuninitialized"
491  unsigned int clock;
492  unsigned int tempData;
493  unsigned int modifiableBits = bits;
494  __asm__ volatile (
495  FC_START("SpiReadLsbPhs0Start%=", "SpiReadLsbPhs0End%=")
496  " ror %[_data], %[_bitCount] '' move MSB into bit 31 \n\t"
497  " mov %[_clock], %[_clkDelay] \n\t"
498  " add %[_clock], CNT \n\t"
499 
500  "loop%=: \n\t"
501  " test %[_miso], ina wc \n\t"
502  " waitcnt %[_clock], %[_clkDelay] \n\t"
503  " xor outa, %[_sclk] \n\t"
504  " rcr %[_data], #1 \n\t"
505  " waitcnt %[_clock], %[_clkDelay] \n\t"
506  " xor outa, %[_sclk] \n\t"
507  " djnz %[_bitCount], #" FC_ADDR("loop%=", "SpiReadLsbPhs0Start%=") " \n\t"
508  FC_END("SpiReadLsbPhs0End%=")
509  : [_bitCount] "+r"(modifiableBits),
510  [_clock] "+r"(clock),
511  [_data] "+r"(tempData)
512  :[_miso] "r"(this->m_miso.get_mask()),
513  [_sclk] "r"(this->m_sclk.get_mask()),
514  [_clkDelay] "r"(this->m_clkDelay)
515  );
516 #pragma GCC diagnostic pop
517  return tempData;
518  }
519 
520  uint32_t shift_in_msb_phs1 (unsigned int bits) const {
521 #pragma GCC diagnostic push
522 #pragma GCC diagnostic ignored "-Wuninitialized"
523  unsigned int clock;
524  unsigned int tempData;
525  __asm__ volatile (
526  FC_START("SpiReadMsbPhs1Start%=", "SpiReadMsbPhs1End%=")
527  " ror %[_data], %[_bitCount] '' move MSB into bit 31 \n\t"
528  " mov %[_clock], %[_clkDelay] \n\t"
529  " add %[_clock], CNT \n\t"
530 
531  "loop%=: \n\t"
532  " xor outa, %[_sclk] \n\t"
533  " waitcnt %[_clock], %[_clkDelay] \n\t"
534  " test %[_miso], ina wc \n\t"
535  " xor outa, %[_sclk] \n\t"
536  " waitcnt %[_clock], %[_clkDelay] \n\t"
537  " rcl %[_data], #1 \n\t"
538  " djnz %[_bitCount], #" FC_ADDR("loop%=", "SpiReadMsbPhs1Start%=") " \n\t"
539  FC_END("SpiReadMsbPhs1End%=")
540  : [_bitCount] "+r"(bits),
541  [_clock] "+r"(clock),
542  [_data] "+r"(tempData)
543  :[_miso] "r"(this->m_miso.get_mask()),
544  [_sclk] "r"(this->m_sclk.get_mask()),
545  [_clkDelay] "r"(this->m_clkDelay)
546  );
547 #pragma GCC diagnostic pop
548  return tempData;
549  }
550 
551  uint32_t shift_in_lsb_phs1 (unsigned int bits) const {
552 #pragma GCC diagnostic push
553 #pragma GCC diagnostic ignored "-Wuninitialized"
554  unsigned int clock;
555  unsigned int tempData;
556  __asm__ volatile (
557  FC_START("SpiReadLsbPhs1Start%=", "SpiReadLsbPhs1End%=")
558  " ror %[_data], %[_bitCount] '' move MSB into bit 31 \n\t"
559  " mov %[_clock], %[_clkDelay] \n\t"
560  " add %[_clock], CNT \n\t"
561 
562  "loop%=: \n\t"
563  " xor outa, %[_sclk] \n\t"
564  " waitcnt %[_clock], %[_clkDelay] \n\t"
565  " test %[_miso], ina wc \n\t"
566  " xor outa, %[_sclk] \n\t"
567  " waitcnt %[_clock], %[_clkDelay] \n\t"
568  " rcr %[_data], #1 \n\t"
569  " djnz %[_bitCount], #" FC_ADDR("loop%=", "SpiReadLsbPhs1Start%=") " \n\t"
570  FC_END("SpiReadLsbPhs1End%=")
571  : [_bitCount] "+r"(bits),
572  [_clock] "+r"(clock),
573  [_data] "+r"(tempData)
574  :[_miso] "r"(this->m_miso.get_mask()),
575  [_sclk] "r"(this->m_sclk.get_mask()),
576  [_clkDelay] "r"(this->m_clkDelay)
577  );
578 #pragma GCC diagnostic pop
579  return tempData;
580  }
581 
582  private:
583 
584  static void reset_pin_mask (Pin &pin, const Port::Mask mask) {
585  pin.set_dir_in();
586  pin.set_mask(mask);
587  pin.set();
588  pin.set_dir_out();
589  }
590 
591  protected:
592  PropWare::Pin m_mosi;
593  PropWare::Pin m_miso;
594  PropWare::Pin m_sclk;
595  unsigned int m_clkDelay;
596  Mode m_mode;
597  BitMode m_bitmode;
598 };
599 
600 }
PropWare::SPI::Mode::MODE_0
@ MODE_0
PropWare::SPI::set_bit_mode
void set_bit_mode(const BitMode bitmode)
Set the bitmode of SPI communication.
Definition: spi.h:181
PropWare::SPI::puts
virtual void puts(const char string[])
Send a null-terminated character array. Though this method could be created using put_char,...
Definition: spi.h:359
printer.h
PropWare::SPI::END_ERROR
@ END_ERROR
Definition: spi.h:90
PropWare::SPI::shift_in_block_mode0_msb_first_fast
void shift_in_block_mode0_msb_first_fast(const uint8_t *buffer, size_t numberOfBytes) const
Receive an array of data at max transmit speed. Mode is always MODE_0 and data is always MSB first.
Definition: spi.h:317
PropWare::SPI::set_sclk
void set_sclk(const Port::Mask mask)
Release the current SCLK pin as a floating input and set the new one as output.
Definition: spi.h:156
PropWare::SPI
SPI serial communications library; Core functionality comes from a dedicated assembly cog.
Definition: spi.h:43
PropWare::SPI::ErrorCode
ErrorCode
Definition: spi.h:86
PropWare::SPI::shift_in
uint32_t shift_in(const unsigned int bits) const
Read a value from the MISO line.
Definition: spi.h:242
PropWare::Port::Mask
Mask
Definition: port.h:43
PropWare::ScanCapable
Interface for all classes capable of printing.
Definition: scancapable.h:38
PropWare::SPI::get_instance
static SPI & get_instance()
Best way to access an SPI instance is through here, where you can get a shared instance of the SPI mo...
Definition: spi.h:103
PropWare::Port::set_dir_in
void set_dir_in() const
Set the port for input.
Definition: port.h:217
PropWare::Pin
Utility class to handle general purpose I/O pins.
Definition: pin.h:36
PropWare::SPI::print_error_str
void print_error_str(const Printer &printer, const ErrorCode err) const
Print an error string through the provided PropWare::Printer interface.
Definition: spi.h:376
PropWare::SPI::Mode::MODE_2
@ MODE_2
PropWare::PrintCapable
Interface for all classes capable of printing.
Definition: printcapable.h:38
PropWare::SPI::set_clock
PropWare::ErrorCode set_clock(const uint32_t frequency)
Change the SPI module's clock frequency.
Definition: spi.h:193
PropWare::SPI::INVALID_FREQ
@ INVALID_FREQ
Definition: spi.h:89
PropWare.h
PropWare::SPI::shift_out
void shift_out(uint8_t bits, uint32_t value) const
Send a value out to a peripheral device.
Definition: spi.h:224
CLKFREQ
#define CLKFREQ
Returns the current clock frequency.
Definition: propeller.h:46
PropWare::SPI::Mode::MODE_3
@ MODE_3
PropWare::SPI::put_char
virtual void put_char(const char c)
Print a single character.
Definition: spi.h:355
PropWare::Port::set
void set() const
Set selected output port high (set all pins to 1)
Definition: port.h:226
PropWare::SPI::set_mosi
void set_mosi(const Port::Mask mask)
Release the current MOSI pin as a floating input and set the new one as output.
Definition: spi.h:141
PropWare::SPI::set_mode
void set_mode(const Mode mode)
Set the mode of SPI communication.
Definition: spi.h:166
PropWare::SPI::shift_out_block_msb_first_fast
void shift_out_block_msb_first_fast(const uint8_t buffer[], size_t numberOfBytes) const
Send an array of data at max transmit speed. Mode is always MODE_0 and data is always MSB first.
Definition: spi.h:270
PropWare::SPI::NO_ERROR
@ NO_ERROR
Definition: spi.h:87
PropWare::SPI::SPI
SPI(const Pin::Mask mosi=Pin::Mask::NULL_PIN, const Pin::Mask miso=Pin::Mask::NULL_PIN, const Pin::Mask sclk=Pin::Mask::NULL_PIN, const uint32_t frequency=DEFAULT_FREQUENCY, const Mode mode=Mode::MODE_0, const BitMode bitmode=BitMode::MSB_FIRST)
Construct an SPI bus on the given pins with the given settings.
Definition: spi.h:119
PropWare::SPI::BEG_ERROR
@ BEG_ERROR
Definition: spi.h:88
PropWare::SPI::get_char
virtual char get_char()
Read and return a single character. Whether the method is blocking or not depends entirely on the imp...
Definition: spi.h:365
PropWare::SPI::BitMode
BitMode
Determine if data is communicated with the LSB or MSB sent/received first.
Definition: spi.h:78
PropWare::SPI::get_clock
int32_t get_clock() const
Retrieve the SPI module's clock frequency.
Definition: spi.h:208
pin.h
PropWare::Printer
Container class that has formatting methods for human-readable output. This class can be constructed ...
Definition: printer.h:76
PropWare::SPI::Mode::MODE_1
@ MODE_1
PropWare::SPI::~SPI
~SPI()
Release the pins to floating inputs.
Definition: spi.h:133
PropWare
Generic definitions and functions for the Parallax Propeller.
Definition: runnable.h:33
PropWare::Port::clear
void clear() const
Clear selected output port (set it to 0)
Definition: port.h:249
PropWare::SPI::set_miso
void set_miso(const Port::Mask mask)
Set the new pin as input.
Definition: spi.h:148
PropWare::SPI::Mode
Mode
Descriptor for SPI signal as defined by Motorola modes.
Definition: spi.h:66
scancapable.h