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  virtual ~SPI();
135 
136  /** Configure the data transmission format.
137  *
138  * @param bits Number of bits per SPI frame (4 - 16).
139  * @param mode Clock polarity and phase mode (0 - 3).
140  *
141  * @code
142  * mode | POL PHA
143  * -----+--------
144  * 0 | 0 0
145  * 1 | 0 1
146  * 2 | 1 0
147  * 3 | 1 1
148  * @endcode
149  */
150  void format(int bits, int mode = 0);
151 
152  /** Set the SPI bus clock frequency.
153  *
154  * @param hz Clock frequency in Hz (default = 1MHz).
155  */
156  void frequency(int hz = 1000000);
157 
158  /** Write to the SPI Slave and return the response.
159  *
160  * @param value Data to be sent to the SPI slave.
161  *
162  * @return Response from the SPI slave.
163  */
164  virtual int write(int value);
165 
166  /** Write to the SPI Slave and obtain the response.
167  *
168  * The total number of bytes sent and received will be the maximum of
169  * tx_length and rx_length. The bytes written will be padded with the
170  * value 0xff.
171  *
172  * @param tx_buffer Pointer to the byte-array of data to write to the device.
173  * @param tx_length Number of bytes to write, may be zero.
174  * @param rx_buffer Pointer to the byte-array of data to read from the device.
175  * @param rx_length Number of bytes to read, may be zero.
176  * @return
177  * The number of bytes written and read from the device. This is
178  * maximum of tx_length and rx_length.
179  */
180  virtual int write(const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length);
181 
182  /** Acquire exclusive access to this SPI bus.
183  */
184  virtual void lock(void);
185 
186  /** Release exclusive access to this SPI bus.
187  */
188  virtual void unlock(void);
189 
190  /** Assert the Slave Select line, acquiring exclusive access to this SPI bus.
191  *
192  * If use_gpio_ssel was not passed to the constructor, this only acquires
193  * exclusive access; it cannot assert the Slave Select line.
194  */
195  void select(void);
196 
197  /** Deassert the Slave Select line, releasing exclusive access to this SPI bus.
198  */
199  void deselect(void);
200 
201  /** Set default write data.
202  * SPI requires the master to send some data during a read operation.
203  * Different devices may require different default byte values.
204  * For example: A SD Card requires default bytes to be 0xFF.
205  *
206  * @param data Default character to be transmitted during a read operation.
207  */
208  void set_default_write_value(char data);
209 
210 #if DEVICE_SPI_ASYNCH
211 
212  /** Start non-blocking SPI transfer using 8bit buffers.
213  *
214  * This function locks the deep sleep until any event has occurred.
215  *
216  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
217  * the default SPI value is sent.
218  * @param tx_length The length of TX buffer in bytes.
219  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
220  * received data are ignored.
221  * @param rx_length The length of RX buffer in bytes.
222  * @param callback The event callback function.
223  * @param event The event mask of events to modify. @see spi_api.h for SPI events.
224  *
225  * @return Operation result.
226  * @retval 0 If the transfer has started.
227  * @retval -1 If SPI peripheral is busy.
228  */
229  template<typename Type>
230  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)
231  {
232  if (spi_active(&_peripheral->spi)) {
233  return queue_transfer(tx_buffer, tx_length, rx_buffer, rx_length, sizeof(Type) * 8, callback, event);
234  }
235  start_transfer(tx_buffer, tx_length, rx_buffer, rx_length, sizeof(Type) * 8, callback, event);
236  return 0;
237  }
238 
239  /** Abort the on-going SPI transfer, and continue with transfers in the queue, if any.
240  */
241  void abort_transfer();
242 
243  /** Clear the queue of transfers.
244  */
245  void clear_transfer_buffer();
246 
247  /** Clear the queue of transfers and abort the on-going transfer.
248  */
249  void abort_all_transfers();
250 
251  /** Configure DMA usage suggestion for non-blocking transfers.
252  *
253  * @param usage The usage DMA hint for peripheral.
254  *
255  * @return Result of the operation.
256  * @retval 0 The usage was set.
257  * @retval -1 Usage cannot be set as there is an ongoing transaction.
258  */
259  int set_dma_usage(DMAUsage usage);
260 
261 #if !defined(DOXYGEN_ONLY)
262 protected:
263 
264  /** SPI interrupt handler.
265  */
266  void irq_handler_asynch(void);
267 
268  /** Start the transfer or put it on the queue.
269  *
270  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
271  * the default SPI value is sent
272  * @param tx_length The length of TX buffer in bytes.
273  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
274  * received data are ignored.
275  * @param rx_length The length of RX buffer in bytes.
276  * @param bit_width The buffers element width in bits.
277  * @param callback The event callback function.
278  * @param event The event mask of events to modify.
279  *
280  * @return Operation success.
281  * @retval 0 A transfer was started or added to the queue.
282  * @retval -1 Transfer can't be added because queue is full.
283  */
284  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);
285 
286  /** Put a transfer on the transfer queue.
287  *
288  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
289  * the default SPI value is sent.
290  * @param tx_length The length of TX buffer in bytes.
291  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
292  * received data are ignored.
293  * @param rx_length The length of RX buffer in bytes.
294  * @param bit_width The buffers element width in bits.
295  * @param callback The event callback function.
296  * @param event The event mask of events to modify.
297  *
298  * @return Operation success.
299  * @retval 0 A transfer was added to the queue.
300  * @retval -1 Transfer can't be added because queue is full.
301  */
302  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);
303 
304  /** Configure a callback, SPI peripheral, and initiate a new transfer.
305  *
306  * @param tx_buffer The TX buffer with data to be transferred. If NULL is passed,
307  * the default SPI value is sent.
308  * @param tx_length The length of TX buffer in bytes.
309  * @param rx_buffer The RX buffer which is used for received data. If NULL is passed,
310  * received data are ignored.
311  * @param rx_length The length of RX buffer in bytes.
312  * @param bit_width The buffers element width.
313  * @param callback The event callback function.
314  * @param event The event mask of events to modify.
315  */
316  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);
317 
318 private:
319  /** Lock deep sleep only if it is not yet locked */
320  void lock_deep_sleep();
321 
322  /** Unlock deep sleep in case it is locked */
323  void unlock_deep_sleep();
324 
325 
326 #if TRANSACTION_QUEUE_SIZE_SPI
327  /** Start a new transaction.
328  *
329  * @param data Transaction data.
330  */
331  void start_transaction(transaction_t *data);
332 
333  /** Dequeue a transaction and start the transfer if there was one pending.
334  */
335  void dequeue_transaction();
336 
337 #endif // TRANSACTION_QUEUE_SIZE_SPI
338 #endif // !defined(DOXYGEN_ONLY)
339 #endif // DEVICE_SPI_ASYNCH
340 
341 #if !defined(DOXYGEN_ONLY)
342 protected:
343 #ifdef DEVICE_SPI_COUNT
344  // HAL must have defined this as a global enum
345  typedef ::SPIName SPIName;
346 #else
347  // HAL may or may not have defined it - use a local definition
348  enum SPIName { GlobalSPI };
349 #endif
350 
351  // All members of spi_peripheral_s must be initialized to make the structure
352  // constant-initialized, and hence able to be omitted by the linker,
353  // as SingletonPtr now relies on C++ constant-initialization. (Previously it
354  // worked through C++ zero-initialization). And all the constants should be zero
355  // to ensure it stays in the actual zero-init part of the image if used, avoiding
356  // an initialized-data cost.
357  struct spi_peripheral_s {
358  /* Internal SPI name identifying the resources. */
359  SPIName name = SPIName(0);
360  /* Internal SPI object handling the resources' state. */
361  spi_t spi{};
362  /* Used by lock and unlock for thread safety */
364  /* Current user of the SPI */
365  SPI *owner = nullptr;
366 #if DEVICE_SPI_ASYNCH && TRANSACTION_QUEUE_SIZE_SPI
367  /* Queue of pending transfers */
368  SingletonPtr<CircularBuffer<Transaction<SPI>, TRANSACTION_QUEUE_SIZE_SPI> > transaction_buffer;
369 #endif
370  };
371 
372  // holds spi_peripheral_s per peripheral on the device.
373  // Drawback: it costs ram size even if the device is not used, however
374  // application can limit the allocation via JSON.
375  static spi_peripheral_s _peripherals[SPI_PERIPHERALS_USED];
376  static int _peripherals_used;
377 
378  // Holds the reference to the associated peripheral.
379  spi_peripheral_s *_peripheral;
380 
381 #if DEVICE_SPI_ASYNCH
382  /* Interrupt */
383  CThunk<SPI> _irq;
384  /* Interrupt handler callback */
385  event_callback_t _callback;
386  /* Current preferred DMA mode @see dma_api.h */
387  DMAUsage _usage;
388  /* Current sate of the sleep manager */
389  bool _deep_sleep_locked;
390 #endif // DEVICE_SPI_ASYNCH
391 
392  // Configuration.
393  PinName _mosi;
394  PinName _miso;
395  PinName _sclk;
396  PinName _hw_ssel;
397 
398  // The Slave Select GPIO if we're doing it ourselves.
399  DigitalOut _sw_ssel;
400 
401  /* Size of the SPI frame */
402  int _bits;
403  /* Clock polairy and phase */
404  int _mode;
405  /* Clock frequency */
406  int _hz;
407  /* Default character used for NULL transfers */
408  char _write_fill;
409  /* Select count to handle re-entrant selection */
410  int8_t _select_count;
411 
412 private:
413  void _do_construct();
414 
415  /** Private acquire function without locking/unlocking.
416  * Implemented in order to avoid duplicate locking and boost performance.
417  */
418  void _acquire(void);
419  void _set_ssel(int);
420 
421  /** Private lookup in the static _peripherals table.
422  */
423  static spi_peripheral_s *_lookup(SPIName name);
424  /** Allocate an entry in the static _peripherals table.
425  */
426  static spi_peripheral_s *_alloc();
427 
428 #endif //!defined(DOXYGEN_ONLY)
429 };
430 
431 /** @}*/
432 
433 } // namespace mbed
434 
435 #endif // DEVICE_SPI || DOXYGEN_ONLY
436 
437 #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:169
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:230
Transaction structure.
Definition: Transaction.h:33
Callback< R(ArgTs...)> callback(R(*func)(ArgTs...)=0)
Create a callback class with type inferred from the arguments.
Definition: Callback.h:709
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
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.