RF22
SoftwareSPI.h
1 // SoftwareSPI.h
2 // Author: Chris Lapa (chris@lapa.com.au)
3 // Copyright (C) 2014 Chris Lapa
4 
5 #ifndef SOFTWARESPI_H
6 #define SOFTWARESPI_H
7 
8 #include "GenericSPI.h"
9 
10 // These defs cause trouble on some versions of Arduino
11 #undef round
12 #undef double
13 
14 #include <SPI.h>
15 
16 
17 /////////////////////////////////////////////////////////////////////
18 /// \class SoftwareSPIClass SoftwareSPI.h <SoftwareSPI.h>
19 /// \brief Encapsulate a software SPI interface
20 ///
21 /// This concrete subclass of GenericSPIClass enapsulates the SPI interface
23 {
24 
25 public:
26 
27  /// Transfer a single octet to and from the SPI interface
28  /// \param[in] data The octet to send
29  /// \return The octet read from SPI while the data octet was sent
30  uint8_t transfer(uint8_t data)
31  {
32  uint8_t readData;
33  uint8_t writeData;
34  uint8_t builtReturn;
35  uint8_t mask;
36 
37  if (_bitOrder == MSBFIRST)
38  {
39  mask = 0x80;
40  }
41  else
42  {
43  mask = 0x01;
44  }
45  builtReturn = 0;
46  readData = 0;
47 
48  for (uint8_t count=0; count<8; count++)
49  {
50  if (data & mask)
51  {
52  writeData = HIGH;
53  }
54  else
55  {
56  writeData = LOW;
57  }
58 
59 
60  if (_clockPhase == 1)
61  {
62  // CPHA=1, miso/mosi changing state now
63  digitalWrite(_mosi, writeData);
64  digitalWrite(_sck, ~_clockPolarity);
65  delayPeriod();
66 
67  // CPHA=1, miso/mosi stable now
68  readData = digitalRead(_miso);
69  digitalWrite(_sck, _clockPolarity);
70  delayPeriod();
71  }
72  else
73  {
74  // CPHA=0, miso/mosi changing state now
75  digitalWrite(_mosi, writeData);
76  digitalWrite(_sck, _clockPolarity);
77  delayPeriod();
78 
79  // CPHA=0, miso/mosi stable now
80  readData = digitalRead(_miso);
81  digitalWrite(_sck, ~_clockPolarity);
82  delayPeriod();
83  }
84 
85 
86  if (_bitOrder == MSBFIRST)
87  {
88  mask >>= 1;
89  builtReturn |= (readData << (7 - count));
90  }
91  else
92  {
93  mask <<= 1;
94  builtReturn |= (readData << count);
95  }
96  }
97 
98  digitalWrite(_sck, _clockPolarity);
99 
100  return builtReturn;
101  }
102 
103  /// Not implemented.
104  void attachInterrupt() { }
105 
106  /// Not implemented.
107  void detachInterrupt() { }
108 
109  /// Initialise the SPI library
110  void begin()
111  {
112  _clockPolarity = LOW;
113  _clockPhase = 0;
114  _bitOrder = MSBFIRST;
115  _delayCounts = SPI_CLOCK_DIV4;
116  }
117 
118  /// Disables the SPI bus usually, in this case
119  /// there is no hardware controller to disable.
120  void end() { }
121 
122  /// Sets the pins used by this SoftwareSPIClass instance.
123  /// \param[in] miso master in slave out pin used
124  /// \param[in] mosi master out slave in pin used
125  /// \param[in] sck clock pin used
126  void setPins(uint8_t miso, uint8_t mosi, uint8_t sck)
127  {
128  _miso = miso;
129  _mosi = mosi;
130  _sck = sck;
131 
132  pinMode(_miso, INPUT);
133  pinMode(_mosi, OUTPUT);
134  pinMode(_sck, OUTPUT);
135  digitalWrite(_sck, _clockPolarity);
136  }
137 
138  /// Sets the bit order the SPI interface will use
139  /// Sets the order of the bits shifted out of and into the SPI bus, either
140  /// LSBFIRST (least-significant bit first) or MSBFIRST (most-significant bit first).
141  /// \param[in] bitOrder Bit order to be used: LSBFIRST or MSBFIRST
142  void setBitOrder(uint8_t bitOrder)
143  {
144  _bitOrder = bitOrder;
145  }
146 
147  /// Sets the SPI data mode: that is, clock polarity and phase.
148  /// See the Wikipedia article on SPI for details.
149  /// \param[in] mode The mode to use: SPI_MODE0 SPI_MODE1 SPI_MODE2 SPI_MODE3
150  void setDataMode(uint8_t mode)
151  {
152  if (mode == SPI_MODE0 ||
153  mode == SPI_MODE1)
154  {
155  _clockPolarity = LOW;
156  }
157  else
158  {
159  _clockPhase = HIGH;
160  }
161 
162  if (mode == SPI_MODE0 ||
163  mode == SPI_MODE2)
164  {
165  _clockPhase = 0;
166  }
167  else
168  {
169  _clockPhase = 1;
170  }
171 
172  digitalWrite(_sck, _clockPolarity);
173  }
174 
175  /// Sets the SPI clock divider relative to the system clock.
176  /// In this case, the software SPI does a basic delay in a loop for whatever
177  /// rate is set too.
178  /// The default setting is SPI_CLOCK_DIV4,
179  /// \param[in] rate The data rate to use: one of SPI_CLOCK_
180  void setClockDivider(uint8_t rate)
181  {
182  _delayCounts = rate;
183  }
184 
185 private:
186 
187  /// Delay routine for bus timing.
188  void delayPeriod()
189  {
190  for (volatile uint8_t count; count<_delayCounts; count++)
191  {
192  __asm__ __volatile__ ("nop");
193  }
194  }
195 
196 
197 private:
198  uint8_t _miso;
199  uint8_t _mosi;
200  uint8_t _sck;
201  uint8_t _bitOrder;
202  uint8_t _delayCounts;
203  uint8_t _clockPolarity;
204  uint8_t _clockPhase;
205 };
206 
207 #endif
void setDataMode(uint8_t mode)
Definition: SoftwareSPI.h:150
void setPins(uint8_t miso, uint8_t mosi, uint8_t sck)
Definition: SoftwareSPI.h:126
void begin()
Initialise the SPI library.
Definition: SoftwareSPI.h:110
void setClockDivider(uint8_t rate)
Definition: SoftwareSPI.h:180
Encapsulate a software SPI interface.
Definition: SoftwareSPI.h:22
uint8_t transfer(uint8_t data)
Definition: SoftwareSPI.h:30
void detachInterrupt()
Not implemented.
Definition: SoftwareSPI.h:107
void setBitOrder(uint8_t bitOrder)
Definition: SoftwareSPI.h:142
Base class for SPI interfaces.
Definition: GenericSPI.h:28
void end()
there is no hardware controller to disable.
Definition: SoftwareSPI.h:120
void attachInterrupt()
Not implemented.
Definition: SoftwareSPI.h:104