Mistake on this page?
Report an issue in GitHub or email us
SPI.h
1 /* mbed Microcontroller Library
2  * Copyright (c) 2006-2019 ARM Limited
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #ifndef MBED_SPI_H
18 #define MBED_SPI_H
19 
20 #include "platform/platform.h"
21 
22 #if DEVICE_SPI || defined(DOXYGEN_ONLY)
23 
24 #include "platform/PlatformMutex.h"
25 #include "hal/spi_api.h"
26 #include "drivers/DigitalOut.h"
27 #include "platform/SingletonPtr.h"
28 #include "platform/NonCopyable.h"
29 
30 #if defined MBED_CONF_DRIVERS_SPI_COUNT_MAX && DEVICE_SPI_COUNT > MBED_CONF_DRIVERS_SPI_COUNT_MAX
31 #define SPI_PERIPHERALS_USED MBED_CONF_DRIVERS_SPI_COUNT_MAX
32 #elif defined DEVICE_SPI_COUNT
33 #define SPI_PERIPHERALS_USED DEVICE_SPI_COUNT
34 #else
35 /* Backwards compatibility with HALs not providing DEVICE_SPI_COUNT */
36 #define SPI_PERIPHERALS_USED 1
37 #endif
38 
39 #if DEVICE_SPI_ASYNCH
40 #include "platform/CThunk.h"
41 #include "hal/dma_api.h"
42 #include "platform/CircularBuffer.h"
43 #include "platform/Callback.h"
44 #include "platform/Transaction.h"
45 #endif
46 
47 namespace mbed {
48 /**
49  * \defgroup drivers_SPI SPI class
50  * \ingroup drivers-public-api-spi
51  * @{
52  */
53 
54 struct use_gpio_ssel_t { };
55 const use_gpio_ssel_t use_gpio_ssel;
56 
57 /** A SPI Master, used for communicating with SPI slave devices.
58  *
59  * The default format is set to 8-bits, mode 0, and a clock frequency of 1MHz.
60  *
61  * Most SPI devices will also require Chip Select and Reset signals. These
62  * can be controlled using DigitalOut pins.
63  *
64  * @note Synchronization level: Thread safe
65  *
66  * Example of how to send a byte to a SPI slave and record the response:
67  * @code
68  * #include "mbed.h"
69  *
70  * SPI device(SPI_MOSI, SPI_MISO, SPI_SCLK)
71  *
72  * DigitalOut chip_select(SPI_CS);
73  *
74  * int main() {
75  * device.lock();
76  * chip_select = 0;
77  *
78  * int response = device.write(0xFF);
79  *
80  * chip_select = 1;
81  * device.unlock();
82  * }
83  * @endcode
84  *
85  * Example using hardware Chip Select line:
86  * @code
87  * #include "mbed.h"
88  *
89  * SPI device(SPI_MOSI, SPI_MISO, SPI_SCLK, SPI_CS)
90  *
91  * int main() {
92  * device.lock();
93  * int response = device.write(0xFF);
94  * device.unlock();
95  * }
96  * @endcode
97  */
98 class SPI : private NonCopyable<SPI> {
99 
100 public:
101 
102  /** Create a SPI master connected to the specified pins.
103  *
104  * @note This constructor passes the SSEL pin selection to the target HAL.
105  * Not all targets support SSEL, so this cannot be relied on in portable code.
106  * Portable code should use the alternative constructor that uses GPIO
107  * for SSEL.
108  *
109  * @note You can specify mosi or miso as NC if not used.
110  *
111  * @param mosi SPI Master Out, Slave In pin.
112  * @param miso SPI Master In, Slave Out pin.
113  * @param sclk SPI Clock pin.
114  * @param ssel SPI Chip Select pin.
115  */
116  SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel = NC);
117 
118  /** Create a SPI master connected to the specified pins.
119  *
120  * @note This constructor manipulates the SSEL pin as a GPIO output
121  * using a DigitalOut object. This should work on any target, and permits
122  * the use of select() and deselect() methods to keep the pin asserted
123  * between transfers.
124  *
125  * @note You can specify mosi or miso as NC if not used.
126  *
127  * @param mosi SPI Master Out, Slave In pin.
128  * @param miso SPI Master In, Slave Out pin.
129  * @param sclk SPI Clock pin.
130  * @param ssel SPI Chip Select pin.
131  */
132  SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel, use_gpio_ssel_t);
133 
134  /** Create a SPI master connected to the specified pins.
135  *
136  * @note This constructor passes the SSEL pin selection to the target HAL.
137  * Not all targets support SSEL, so this cannot be relied on in portable code.
138  * Portable code should use the alternative constructor that uses GPIO
139  * for SSEL.
140  *
141  * @note You can specify mosi or miso as NC if not used.
142  *
143  * @param static_pinmap reference to structure which holds static pinmap.
144  */
145  SPI(const spi_pinmap_t &static_pinmap);
146  SPI(const spi_pinmap_t &&) = delete; // prevent passing of temporary objects
147 
148  /** Create a SPI master connected to the specified pins.
149  *
150  * @note This constructor manipulates the SSEL pin as a GPIO output
151  * using a DigitalOut object. This should work on any target, and permits
152  * the use of select() and deselect() methods to keep the pin asserted
153  * between transfers.
154  *
155  * @note You can specify mosi or miso as NC if not used.
156  *
157  * @param static_pinmap reference to structure which holds static pinmap.
158  * @param ssel SPI Chip Select pin.
159  */
160  SPI(const spi_pinmap_t &static_pinmap, PinName ssel);
161  SPI(const spi_pinmap_t &&, PinName) = delete; // prevent passing of temporary objects
162 
163  virtual ~SPI();
164 
165  /** Configure the data transmission format.
166  *
167  * @param bits Number of bits per SPI frame (4 - 16).
168  * @param mode Clock polarity and phase mode (0 - 3).
169  *
170  * @code
171  * mode | POL PHA
172  * -----+--------
173  * 0 | 0 0
174  * 1 | 0 1
175  * 2 | 1 0
176  * 3 | 1 1
177  * @endcode
178  */
179  void format(int bits, int mode = 0);
180 
181  /** Set the SPI bus clock frequency.
182  *
183  * @param hz Clock frequency in Hz (default = 1MHz).
184  */
185  void frequency(int hz = 1000000);
186 
187  /** Write to the SPI Slave and return the response.
188  *
189  * @param value Data to be sent to the SPI slave.
190  *
191  * @return Response from the SPI slave.
192  */
193  virtual int write(int value);
194 
195  /** Write to the SPI Slave and obtain the response.
196  *
197  * The total number of bytes sent and received will be the maximum of
198  * tx_length and rx_length. The bytes written will be padded with the
199  * value 0xff.
200  *
201  * @param tx_buffer Pointer to the byte-array of data to write to the device.
202  * @param tx_length Number of bytes to write, may be zero.
203  * @param rx_buffer Pointer to the byte-array of data to read from the device.
204  * @param rx_length Number of bytes to read, may be zero.
205  * @return
206  * The number of bytes written and read from the device. This is
207  * maximum of tx_length and rx_length.
208  */
209  virtual int write(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length);
210 
211  /** Acquire exclusive access to this SPI bus.
212  */
213  virtual void lock(void);
214 
215  /** Release exclusive access to this SPI bus.
216  */
217  virtual void unlock(void);
218 
219  /** Assert the Slave Select line, acquiring exclusive access to this SPI bus.
220  *
221  * If use_gpio_ssel was not passed to the constructor, this only acquires
222  * exclusive access; it cannot assert the Slave Select line.
223  */
224  void select(void);
225 
226  /** Deassert the Slave Select line, releasing exclusive access to this SPI bus.
227  */
228  void deselect(void);
229 
230  /** Set default write data.
231  * SPI requires the master to send some data during a read operation.
232  * Different devices may require different default byte values.
233  * For example: A SD Card requires default bytes to be 0xFF.
234  *
235  * @param data Default character to be transmitted during a read operation.
236  */
237  void set_default_write_value(char data);
238 
239 #if DEVICE_SPI_ASYNCH
240 
241  /** Start non-blocking SPI transfer using 8bit buffers.
242  *
243  * This function locks the deep sleep until any event has occurred.
244  *
245  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
246  * the default SPI value is sent.
247  * @param tx_length The length of TX buffer in bytes.
248  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
249  * received data are ignored.
250  * @param rx_length The length of RX buffer in bytes.
251  * @param callback The event callback function.
252  * @param event The event mask of events to modify. @see spi_api.h for SPI events.
253  *
254  * @return Operation result.
255  * @retval 0 If the transfer has started.
256  * @retval -1 If SPI peripheral is busy.
257  */
258  template<typename Type>
259  int transfer(const Type *tx_buffer, int tx_length, Type *rx_buffer, int rx_length, const event_callback_t &callback, int event = SPI_EVENT_COMPLETE)
260  {
261  if (spi_active(&_peripheral->spi)) {
262  return queue_transfer(tx_buffer, tx_length, rx_buffer, rx_length, sizeof(Type) * 8, callback, event);
263  }
264  start_transfer(tx_buffer, tx_length, rx_buffer, rx_length, sizeof(Type) * 8, callback, event);
265  return 0;
266  }
267 
268  /** Abort the on-going SPI transfer, and continue with transfers in the queue, if any.
269  */
270  void abort_transfer();
271 
272  /** Clear the queue of transfers.
273  */
274  void clear_transfer_buffer();
275 
276  /** Clear the queue of transfers and abort the on-going transfer.
277  */
278  void abort_all_transfers();
279 
280  /** Configure DMA usage suggestion for non-blocking transfers.
281  *
282  * @param usage The usage DMA hint for peripheral.
283  *
284  * @return Result of the operation.
285  * @retval 0 The usage was set.
286  * @retval -1 Usage cannot be set as there is an ongoing transaction.
287  */
288  int set_dma_usage(DMAUsage usage);
289 
290 #if !defined(DOXYGEN_ONLY)
291 protected:
292 
293  /** SPI interrupt handler.
294  */
295  void irq_handler_asynch(void);
296 
297  /** Start the transfer or put it on the queue.
298  *
299  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
300  * the default SPI value is sent
301  * @param tx_length The length of TX buffer in bytes.
302  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
303  * received data are ignored.
304  * @param rx_length The length of RX buffer in bytes.
305  * @param bit_width The buffers element width in bits.
306  * @param callback The event callback function.
307  * @param event The event mask of events to modify.
308  *
309  * @return Operation success.
310  * @retval 0 A transfer was started or added to the queue.
311  * @retval -1 Transfer can't be added because queue is full.
312  */
313  int transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t &callback, int event);
314 
315  /** Put a transfer on the transfer queue.
316  *
317  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
318  * the default SPI value is sent.
319  * @param tx_length The length of TX buffer in bytes.
320  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
321  * received data are ignored.
322  * @param rx_length The length of RX buffer in bytes.
323  * @param bit_width The buffers element width in bits.
324  * @param callback The event callback function.
325  * @param event The event mask of events to modify.
326  *
327  * @return Operation success.
328  * @retval 0 A transfer was added to the queue.
329  * @retval -1 Transfer can't be added because queue is full.
330  */
331  int queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t &callback, int event);
332 
333  /** Configure a callback, SPI peripheral, and initiate a new transfer.
334  *
335  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
336  * the default SPI value is sent.
337  * @param tx_length The length of TX buffer in bytes.
338  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
339  * received data are ignored.
340  * @param rx_length The length of RX buffer in bytes.
341  * @param bit_width The buffers element width.
342  * @param callback The event callback function.
343  * @param event The event mask of events to modify.
344  */
345  void start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, unsigned char bit_width, const event_callback_t &callback, int event);
346 
347 private:
348  /** Lock deep sleep only if it is not yet locked */
349  void lock_deep_sleep();
350 
351  /** Unlock deep sleep in case it is locked */
352  void unlock_deep_sleep();
353 
354 
355 #if TRANSACTION_QUEUE_SIZE_SPI
356  /** Start a new transaction.
357  *
358  * @param data Transaction data.
359  */
360  void start_transaction(transaction_t *data);
361 
362  /** Dequeue a transaction and start the transfer if there was one pending.
363  */
364  void dequeue_transaction();
365 
366 #endif // TRANSACTION_QUEUE_SIZE_SPI
367 #endif // !defined(DOXYGEN_ONLY)
368 #endif // DEVICE_SPI_ASYNCH
369 
370 #if !defined(DOXYGEN_ONLY)
371 protected:
372 #ifdef DEVICE_SPI_COUNT
373  // HAL must have defined this as a global enum
374  typedef ::SPIName SPIName;
375 #else
376  // HAL may or may not have defined it - use a local definition
377  enum SPIName { GlobalSPI };
378 #endif
379 
380  // All members of spi_peripheral_s must be initialized to make the structure
381  // constant-initialized, and hence able to be omitted by the linker,
382  // as SingletonPtr now relies on C++ constant-initialization. (Previously it
383  // worked through C++ zero-initialization). And all the constants should be zero
384  // to ensure it stays in the actual zero-init part of the image if used, avoiding
385  // an initialized-data cost.
386  struct spi_peripheral_s {
387  /* Internal SPI name identifying the resources. */
388  SPIName name = SPIName(0);
389  /* Internal SPI object handling the resources' state. */
390  spi_t spi{};
391  /* Used by lock and unlock for thread safety */
393  /* Current user of the SPI */
394  SPI *owner = nullptr;
395 #if DEVICE_SPI_ASYNCH && TRANSACTION_QUEUE_SIZE_SPI
396  /* Queue of pending transfers */
397  SingletonPtr<CircularBuffer<Transaction<SPI>, TRANSACTION_QUEUE_SIZE_SPI> > transaction_buffer;
398 #endif
399  };
400 
401  // holds spi_peripheral_s per peripheral on the device.
402  // Drawback: it costs ram size even if the device is not used, however
403  // application can limit the allocation via JSON.
404  static spi_peripheral_s _peripherals[SPI_PERIPHERALS_USED];
405  static int _peripherals_used;
406 
407  // Holds the reference to the associated peripheral.
408  spi_peripheral_s *_peripheral;
409 
410 #if DEVICE_SPI_ASYNCH
411  /* Interrupt */
412  CThunk<SPI> _irq;
413  /* Interrupt handler callback */
414  event_callback_t _callback;
415  /* Current preferred DMA mode @see dma_api.h */
416  DMAUsage _usage;
417  /* Current sate of the sleep manager */
418  bool _deep_sleep_locked;
419 #endif // DEVICE_SPI_ASYNCH
420 
421  // Configuration.
422  PinName _mosi;
423  PinName _miso;
424  PinName _sclk;
425  PinName _hw_ssel;
426 
427  // The Slave Select GPIO if we're doing it ourselves.
428  DigitalOut _sw_ssel;
429 
430  /* Size of the SPI frame */
431  int _bits;
432  /* Clock polairy and phase */
433  int _mode;
434  /* Clock frequency */
435  int _hz;
436  /* Default character used for NULL transfers */
437  char _write_fill;
438  /* Select count to handle re-entrant selection */
439  int8_t _select_count;
440  /* Static pinmap data */
441  const spi_pinmap_t *_static_pinmap;
442  /* SPI peripheral name */
443  SPIName _peripheral_name;
444  /* Pointer to spi init function */
445  void (*_init_func)(SPI *);
446 
447 private:
448  void _do_construct();
449 
450  /** Private acquire function without locking/unlocking.
451  * Implemented in order to avoid duplicate locking and boost performance.
452  */
453  void _acquire(void);
454  void _set_ssel(int);
455 
456  /** Private lookup in the static _peripherals table.
457  */
458  static spi_peripheral_s *_lookup(SPIName name);
459  /** Allocate an entry in the static _peripherals table.
460  */
461  static spi_peripheral_s *_alloc();
462 
463  static void _do_init(SPI *obj);
464  static void _do_init_direct(SPI *obj);
465 
466 
467 #endif //!defined(DOXYGEN_ONLY)
468 };
469 
470 /** @}*/
471 
472 } // namespace mbed
473 
474 #endif // DEVICE_SPI || DOXYGEN_ONLY
475 
476 #endif // MBED_SPI_H
uint8_t spi_active(spi_t *obj)
Attempts to determine if the SPI peripheral is already in use.
Prevents generation of copy constructor and copy assignment operator in derived classes.
Definition: NonCopyable.h:162
int transfer(const Type *tx_buffer, int tx_length, Type *rx_buffer, int rx_length, const event_callback_t &callback, int event=SPI_EVENT_COMPLETE)
Start non-blocking SPI transfer using 8bit buffers.
Definition: SPI.h:259
Callback< R(ArgTs...)> callback(R(*func)(ArgTs...)=nullptr) noexcept
Create a callback class with type inferred from the arguments.
Definition: Callback.h:678
Transaction structure.
Definition: Transaction.h:33
A digital output, used for setting the state of a pin.
Definition: DigitalOut.h:49
A SPI Master, used for communicating with SPI slave devices.
Definition: SPI.h:98
Class for created a pointer with data bound to it.
Definition: CThunk.h:45
Definition: ATHandler.h:46
Asynch SPI HAL structure.
Definition: spi_api.h:43
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.