COSA
An Object-Oriented Platform for Arduino Programming
SPI.cpp
Go to the documentation of this file.
1 
21 #include "Cosa/SPI.hh"
22 #include "Cosa/Power.hh"
23 
24 // Configuration: Allow SPI transfer interleaving
25 #if !defined(BOARD_ATTINY)
26 #define USE_SPI_PREFETCH
27 #endif
28 
29 SPI spi __attribute__ ((weak));
30 
31 #if defined(SPDR)
32 
34  Clock rate, uint8_t mode, Order order,
35  Interrupt::Handler* irq) :
36  m_next(NULL),
37  m_irq(irq),
38  m_cs(cs, ((pulse & 0x01) == 0)),
39  m_pulse(pulse),
40  // SPI Control Register for master mode
41  m_spcr(_BV(SPE)
42  | ((order & 0x1) << DORD)
43  | _BV(MSTR)
44  | ((mode & 0x3) << CPHA)
45  | ((rate & 0x3) << SPR0)),
46  // SPI Clock control in Status Register
47  m_spsr(((rate & 0x04) != 0) << SPI2X)
48 {
49 }
50 
51 SPI::SPI() :
52  m_list(NULL),
53  m_dev(NULL),
54  m_busy(false)
55 {
56  // Initiate the SPI data direction for master mode
57  // The SPI/SS pin must be an output pin in master mode
58  synchronized {
59  bit_mask_set(DDRB, _BV(Board::MOSI) | _BV(Board::SCK) | _BV(Board::SS));
60  bit_clear(DDRB, Board::MISO);
61  bit_mask_clear(PORTB, _BV(Board::SCK) | _BV(Board::MOSI));
62  bit_set(PORTB, Board::MISO);
63  }
64  // Other the SPI setup is done by the SPI::Driver::begin()
65 }
66 
67 #elif defined(USIDR)
68 
69 // Create mapping to USI data direction register and port for ATtiny variants
70 #if defined(BOARD_ATTINYX4) || defined(BOARD_ATTINYX61)
71 #define DDR DDRA
72 #define PORT PORTA
73 #elif defined(BOARD_ATTINYX5)
74 #define DDR DDRB
75 #define PORT PORTB
76 #endif
77 
79  Clock rate, uint8_t mode, Order order,
80  Interrupt::Handler* irq) :
81  m_next(NULL),
82  m_irq(irq),
83  m_cs(cs, ((pulse & 0x01) == 0)),
84  m_pulse(pulse),
85  m_cpol(mode)
86 {
87  UNUSED(rate);
88  UNUSED(order);
89 
90  // USI command for hardware supported bit banging
91  m_usicr = (_BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC));
92  if (mode == 1 || mode == 2) m_usicr |= _BV(USICS0);
93 
94  // Set ports
95  synchronized {
96  bit_mask_set(DDR, _BV(Board::MOSI) | _BV(Board::SCK));
99  USICR = m_usicr;
100  }
101 }
102 
103 SPI::SPI() :
104  m_list(NULL),
105  m_dev(NULL),
106  m_busy(false)
107 {
108  // Set port data direction. Note ATtiny MOSI/MISO are DI/DO.
109  // Do not confuse with SPI chip programming pins
110  synchronized {
111  bit_mask_set(DDR, _BV(Board::MOSI) | _BV(Board::SCK));
114  }
115 }
116 #endif
117 
118 /*
119  * Prefetch optimized variants of the block transfer member functions.
120  * These implementation allow higher transfer speed for block by using
121  * the available cycles while a byte transfer is in progress for data
122  * prefetch and store. There are at least 16 instruction cycles available
123  * (CLOCK_DIV_2).
124  */
125 #if defined(USE_SPI_PREFETCH)
126 void
127 SPI::transfer(void* buf, size_t count)
128 {
129  if (UNLIKELY(count == 0)) return;
130  uint8_t* dp = (uint8_t*) buf;
131  uint8_t data = *dp;
132  transfer_start(data);
133  while (--count) {
134  uint8_t* tp = dp + 1;
135  data = *tp;
136  *dp = transfer_next(data);
137  dp = tp;
138  }
139  *dp = transfer_await();
140 }
141 
142 void
143 SPI::transfer(void* dst, const void* src, size_t count)
144 {
145  if (UNLIKELY(count == 0)) return;
146  uint8_t* dp = (uint8_t*) dst;
147  const uint8_t* sp = (const uint8_t*) src;
148  uint8_t data = *sp++;
149  transfer_start(data);
150  while (--count) {
151  uint8_t* tp = dp + 1;
152  data = *sp++;
153  *dp = transfer_next(data);
154  dp = tp;
155  }
156  *dp = transfer_await();
157 }
158 
159 void
160 SPI::read(void* buf, size_t count)
161 {
162  if (UNLIKELY(count == 0)) return;
163  uint8_t* dp = (uint8_t*) buf;
164  transfer_start(0xff);
165  while (--count) *dp++ = transfer_next(0xff);
166  *dp = transfer_await();
167 }
168 
169 void
170 SPI::write(const void* buf, size_t count)
171 {
172  if (UNLIKELY(count == 0)) return;
173  const uint8_t* sp = (const uint8_t*) buf;
174  uint8_t data = *sp++;
175  transfer_start(data);
176  while (--count) {
177  data = *sp++;
178  transfer_next(data);
179  }
180  transfer_await();
181 }
182 
183 void
184 SPI::write_P(const void* buf, size_t count)
185 {
186  if (UNLIKELY(count == 0)) return;
187  const uint8_t* sp = (const uint8_t*) buf;
188  uint8_t data = pgm_read_byte(sp++);
189  transfer_start(data);
190  while (--count) {
191  data = pgm_read_byte(sp++);
192  transfer_next(data);
193  }
194  transfer_await();
195 }
196 
197 #else
198 
199 void
200 SPI::transfer(void* buf, size_t count)
201 {
202  if (UNLIKELY(count == 0)) return;
203  uint8_t* bp = (uint8_t*) buf;
204  do {
205  *bp = transfer(*bp);
206  bp += 1;
207  } while (--count);
208 }
209 
210 void
211 SPI::transfer(void* dst, const void* src, size_t count)
212 {
213  if (UNLIKELY(count == 0)) return;
214  uint8_t* dp = (uint8_t*) dst;
215  const uint8_t* sp = (const uint8_t*) src;
216  do *dp++ = transfer(*sp++); while (--count);
217 }
218 
219 void
220 SPI::read(void* buf, size_t count)
221 {
222  if (UNLIKELY(count == 0)) return;
223  uint8_t* bp = (uint8_t*) buf;
224  do *bp++ = transfer(0); while (--count);
225 }
226 
227 void
228 SPI::write(const void* buf, size_t count)
229 {
230  if (UNLIKELY(count == 0)) return;
231  const uint8_t* bp = (const uint8_t*) buf;
232  do transfer(*bp++); while (--count);
233 }
234 
235 void
236 SPI::write_P(const void* buf, size_t count)
237 {
238  if (UNLIKELY(count == 0)) return;
239  const uint8_t* bp = (const uint8_t*) buf;
240  do transfer(pgm_read_byte(bp++)); while (--count);
241 }
242 
243 #endif
244 
245 bool
247 {
248  if (UNLIKELY(dev->m_next != NULL)) return (false);
249  dev->m_next = m_list;
250  m_list = dev;
251  return (true);
252 }
253 
254 void
256 {
257  // Acquire the device driver. Wait if busy. Synchronized update
258  uint8_t key = lock(m_busy);
259 
260  // Power up
261  SPI::powerup();
262 
263  // Set current device driver
264  m_dev = dev;
265 
266 #if defined(SPDR)
267  // Initiate SPI hardware with device settings
268  SPCR = dev->m_spcr;
269  SPSR = dev->m_spsr;
270 #else
271  // Set clock polarity
272  bit_write(dev->m_cpol & 0x02, PORT, Board::SCK);
273 #endif
274  // Disable all interrupt sources on SPI bus
275  for (SPI::Driver* dev = m_list; dev != NULL; dev = dev->m_next)
276  if (dev->m_irq != NULL) dev->m_irq->disable();
277  unlock(key);
278 }
279 
280 void
282 {
283  synchronized {
284  // Power down
285  SPI::powerdown();
286 
287  // Release the device driver
288  m_busy = false;
289  m_dev = NULL;
290  // Enable all interrupt sources on SPI bus
291  for (SPI::Driver* dev = m_list; dev != NULL; dev = dev->m_next)
292  if (dev->m_irq != NULL) dev->m_irq->enable();
293  }
294 }
295 
296 void
298 {
299 #if defined(SPDR)
300  m_spcr = (m_spcr & ~(0x3 << SPR0)) | ((rate & 0x3) << SPR0);
301  m_spsr = (m_spsr & ~(1 << SPI2X)) | (((rate & 0x04) != 0) << SPI2X);
302 #else
303  UNUSED(rate);
304 #endif
305 }
306 
308 {
309  switch (rate) {
310  case SPI::DIV2_CLOCK:
311  outs << PSTR("SPI::DIV2_CLOCK(") << F_CPU / 2000000.0;
312  break;
313  case SPI::DIV4_CLOCK:
314  outs << PSTR("SPI::DIV4_CLOCK(") << F_CPU / 4000000.0;
315  break;
316  case SPI::DIV8_CLOCK:
317  outs << PSTR("SPI::DIV8_CLOCK(") << F_CPU / 8000000.0;
318  break;
319  case SPI::DIV16_CLOCK:
320  outs << PSTR("SPI::DIV16_CLOCK(") << F_CPU / 16000000.0;
321  break;
322  case SPI::DIV32_CLOCK:
323  outs << PSTR("SPI::DIV32_CLOCK(") << F_CPU / 32000000.0;
324  break;
325  case SPI::DIV64_CLOCK:
326  outs << PSTR("SPI::DIV64_CLOCK(") << F_CPU / 64000000.0;
327  break;
328  case SPI::DIV128_CLOCK:
329  outs << PSTR("SPI::DIV128_CLOCK(") << F_CPU / 128000000.0;
330  break;
331  };
332  outs << PSTR(" MHz)");
333  return (outs);
334 }
uint8_t m_spsr
SPI/SPSR hardware status register.
Definition: SPI.hh:174
#define DDR
Definition: Tone.cpp:50
#define bit_mask_clear(p, m)
Definition: Bits.h:30
Clock
Definition: SPI.hh:60
Divide system clock by 4.
Definition: SPI.hh:62
Definition: Clock.hh:33
void acquire(Driver *dev)
Definition: SPI.cpp:255
uint8_t transfer_next(uint8_t data)
Definition: SPI.hh:363
Definition: SPI.hh:57
Interrupt::Handler * m_irq
Interrupt handler.
Definition: SPI.hh:165
#define bit_set(p, b)
Definition: Bits.h:35
void set_clock(Clock rate)
Definition: SPI.cpp:297
#define NULL
Definition: Types.h:101
Driver * m_next
List of drivers.
Definition: SPI.hh:164
#define PSTR(s)
Definition: Types.h:202
#define bit_clear(p, b)
Definition: Bits.h:36
Divide system clock by 128.
Definition: SPI.hh:67
static void powerup()
Definition: SPI.hh:375
virtual void disable()
Definition: Interrupt.hh:53
void release()
Definition: SPI.cpp:281
IOStream & operator<<(IOStream &outs, SPI::Clock rate)
Definition: SPI.cpp:307
Divide system clock by 8.
Definition: SPI.hh:63
SPI spi
Definition: SPI.cpp:29
uint8_t lock()
Definition: Types.h:319
bool attach(Driver *dev)
Definition: SPI.cpp:246
void transfer_start(uint8_t data)
Definition: SPI.hh:339
Divide system clock by 16.
Definition: SPI.hh:64
uint8_t transfer_await()
Definition: SPI.hh:350
uint8_t transfer(uint8_t data)
Definition: SPI.hh:326
uint8_t m_spcr
SPI/SPCR hardware control register setting.
Definition: SPI.hh:173
#define UNUSED(x)
Definition: ATmega328P.hh:31
#define bit_write(c, p, b)
Definition: Bits.h:38
Divide system clock by 64.
Definition: SPI.hh:66
#define PORT
Definition: Tone.cpp:51
Divide system clock by 32.
Definition: SPI.hh:65
void write_P(const void *buf, size_t count)
Definition: SPI.cpp:184
#define bit_mask_set(p, m)
Definition: Bits.h:29
void unlock(uint8_t key)
Definition: Types.h:331
Divide system clock by 2.
Definition: SPI.hh:61
static void powerdown()
Definition: SPI.hh:383
#define UNLIKELY(x)
Definition: Types.h:153
void read(void *buf, size_t count)
Definition: SPI.cpp:160
Driver(Board::DigitalPin cs, Pulse pulse=DEFAULT_PULSE, Clock rate=DEFAULT_CLOCK, uint8_t mode=0, Order order=MSB_ORDER, Interrupt::Handler *irq=NULL)
Definition: SOFT_SPI.cpp:25
void write(const void *buf, size_t count)
Definition: SPI.cpp:170