ChibiOS/HAL  7.0.3
hal_spi.h
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 /**
18  * @file hal_spi.h
19  * @brief SPI Driver macros and structures.
20  *
21  * @addtogroup SPI
22  * @{
23  */
24 
25 #ifndef HAL_SPI_H
26 #define HAL_SPI_H
27 
28 #if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
29 
30 /*===========================================================================*/
31 /* Driver constants. */
32 /*===========================================================================*/
33 
34 /**
35  * @name Chip Select modes
36  * @{
37  */
38 #define SPI_SELECT_MODE_NONE 0 /** @brief @p spiSelect() and
39  @p spiUnselect() do
40  nothing. */
41 #define SPI_SELECT_MODE_PAD 1 /** @brief Legacy mode. */
42 #define SPI_SELECT_MODE_PORT 2 /** @brief Fastest mode. */
43 #define SPI_SELECT_MODE_LINE 3 /** @brief Packed mode. */
44 #define SPI_SELECT_MODE_LLD 4 /** @brief LLD-defined mode.*/
45 /** @} */
46 
47 /*===========================================================================*/
48 /* Driver pre-compile time settings. */
49 /*===========================================================================*/
50 
51 /**
52  * @name SPI configuration options
53  * @{
54  */
55 /**
56  * @brief Enables synchronous APIs.
57  * @note Disabling this option saves both code and data space.
58  */
59 #if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
60 #define SPI_USE_WAIT TRUE
61 #endif
62 
63 /**
64  * @brief Enables circular transfers APIs.
65  * @note Disabling this option saves both code and data space.
66  */
67 #if !defined(SPI_USE_CIRCULAR) || defined(__DOXYGEN__)
68 #define SPI_USE_CIRCULAR FALSE
69 #endif
70 
71 /**
72  * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
73  * @note Disabling this option saves both code and data space.
74  */
75 #if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
76 #define SPI_USE_MUTUAL_EXCLUSION TRUE
77 #endif
78 
79 /**
80  * @brief Handling method for SPI CS line.
81  * @note Disabling this option saves both code and data space.
82  */
83 #if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
84 #define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
85 #endif
86 /** @} */
87 
88 /*===========================================================================*/
89 /* Derived constants and error checks. */
90 /*===========================================================================*/
91 
92 #if (SPI_SELECT_MODE != SPI_SELECT_MODE_NONE) && \
93  (SPI_SELECT_MODE != SPI_SELECT_MODE_PAD) && \
94  (SPI_SELECT_MODE != SPI_SELECT_MODE_PORT) && \
95  (SPI_SELECT_MODE != SPI_SELECT_MODE_LINE) && \
96  (SPI_SELECT_MODE != SPI_SELECT_MODE_LLD)
97 #error "invalid SPI_SELECT_MODE setting"
98 #endif
99 
100 /* Some modes have a dependency on the PAL driver, making the required
101  checks here.*/
102 #if ((SPI_SELECT_MODE != SPI_SELECT_MODE_PAD) || \
103  (SPI_SELECT_MODE != SPI_SELECT_MODE_PORT) || \
104  (SPI_SELECT_MODE != SPI_SELECT_MODE_LINE)) && \
105  (HAL_USE_PAL != TRUE)
106 #error "current SPI_SELECT_MODE requires HAL_USE_PAL"
107 #endif
108 
109 /*===========================================================================*/
110 /* Driver data structures and types. */
111 /*===========================================================================*/
112 
113 /**
114  * @brief Driver state machine possible states.
115  */
116 typedef enum {
117  SPI_UNINIT = 0, /**< Not initialized. */
118  SPI_STOP = 1, /**< Stopped. */
119  SPI_READY = 2, /**< Ready. */
120  SPI_ACTIVE = 3, /**< Exchanging data. */
121  SPI_COMPLETE = 4 /**< Asynchronous operation complete. */
122 } spistate_t;
123 
124 /**
125  * @brief Type of a structure representing an SPI driver.
126  */
127 typedef struct hal_spi_driver SPIDriver;
128 /**
129  * @brief Type of a SPI driver configuration structure.
130  */
131 typedef struct hal_spi_config SPIConfig;
132 
133 /**
134  * @brief SPI notification callback type.
135  *
136  * @param[in] spip pointer to the @p SPIDriver object triggering the
137  * callback
138  */
139 typedef void (*spicallback_t)(SPIDriver *spip);
140 
141 /* Including the low level driver header, it exports information required
142  for completing types.*/
143 #include "hal_spi_lld.h"
144 
145 /**
146  * @brief Driver configuration structure.
147  */
149 #if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__)
150  /**
151  * @brief Enables the circular buffer mode.
152  */
153  bool circular;
154 #endif
155  /**
156  * @brief Operation complete callback or @p NULL.
157  */
159 #if (SPI_SELECT_MODE == SPI_SELECT_MODE_LINE) || defined(__DOXYGEN__)
160  /**
161  * @brief The chip select line.
162  */
164 #endif
165 #if (SPI_SELECT_MODE == SPI_SELECT_MODE_PORT) || defined(__DOXYGEN__)
166  /**
167  * @brief The chip select port.
168  */
170  /**
171  * @brief The chip select port mask.
172  */
174 #endif
175 #if (SPI_SELECT_MODE == SPI_SELECT_MODE_PAD) || defined(__DOXYGEN__)
176  /**
177  * @brief The chip select port.
178  */
180  /**
181  * @brief The chip select pad number.
182  */
183  uint_fast8_t sspad;
184 #endif
185  /* End of the mandatory fields.*/
186  spi_lld_config_fields;
187 };
188 
189 /**
190  * @brief Structure representing an SPI driver.
191  */
193  /**
194  * @brief Driver state.
195  */
197  /**
198  * @brief Current configuration data.
199  */
201 #if (SPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
202  /**
203  * @brief Waiting thread.
204  */
206 #endif /* SPI_USE_WAIT == TRUE */
207 #if (SPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
208  /**
209  * @brief Mutex protecting the peripheral.
210  */
212 #endif /* SPI_USE_MUTUAL_EXCLUSION == TRUE */
213 #if defined(SPI_DRIVER_EXT_FIELDS)
214  SPI_DRIVER_EXT_FIELDS
215 #endif
216  /* End of the mandatory fields.*/
218 };
219 
220 /*===========================================================================*/
221 /* Driver macros. */
222 /*===========================================================================*/
223 
224 /**
225  * @name Macro Functions
226  * @{
227  */
228 /**
229  * @brief Buffer state.
230  * @note This function is meant to be called from the SPI callback only.
231  *
232  * @param[in] spip pointer to the @p SPIDriver object
233  * @return The buffer state.
234  * @retval false if the driver filled/sent the first half of the
235  * buffer.
236  * @retval true if the driver filled/sent the second half of the
237  * buffer.
238  *
239  * @special
240  */
241 #define spiIsBufferComplete(spip) ((bool)((spip)->state == SPI_COMPLETE))
242 
243 #if (SPI_SELECT_MODE == SPI_SELECT_MODE_LLD) || defined(__DOXYGEN__)
244 /**
245  * @brief Asserts the slave select signal and prepares for transfers.
246  *
247  * @param[in] spip pointer to the @p SPIDriver object
248  *
249  * @iclass
250  */
251 #define spiSelectI(spip) \
252 do { \
253  spi_lld_select(spip); \
254 } while (false)
255 
256 /**
257  * @brief Deasserts the slave select signal.
258  * @details The previously selected peripheral is unselected.
259  *
260  * @param[in] spip pointer to the @p SPIDriver object
261  *
262  * @iclass
263  */
264 #define spiUnselectI(spip) \
265 do { \
266  spi_lld_unselect(spip); \
267 } while (false)
268 
269 #elif SPI_SELECT_MODE == SPI_SELECT_MODE_LINE
270 #define spiSelectI(spip) \
271 do { \
272  palClearLine((spip)->config->ssline); \
273 } while (false)
274 
275 #define spiUnselectI(spip) \
276 do { \
277  palSetLine((spip)->config->ssline); \
278 } while (false)
279 
280 #elif SPI_SELECT_MODE == SPI_SELECT_MODE_PORT
281 #define spiSelectI(spip) \
282 do { \
283  palClearPort((spip)->config->ssport, (spip)->config->ssmask); \
284 } while (false)
285 
286 #define spiUnselectI(spip) \
287 do { \
288  palSetPort((spip)->config->ssport, (spip)->config->ssmask); \
289 } while (false)
290 
291 #elif SPI_SELECT_MODE == SPI_SELECT_MODE_PAD
292 #define spiSelectI(spip) \
293 do { \
294  palClearPad((spip)->config->ssport, (spip)->config->sspad); \
295 } while (false)
296 
297 #define spiUnselectI(spip) \
298 do { \
299  palSetPad((spip)->config->ssport, (spip)->config->sspad); \
300 } while (false)
301 
302 #elif SPI_SELECT_MODE == SPI_SELECT_MODE_NONE
303 #define spiSelectI(spip)
304 
305 #define spiUnselectI(spip)
306 #endif
307 
308 /**
309  * @brief Ignores data on the SPI bus.
310  * @details This asynchronous function starts the transmission of a series of
311  * idle words on the SPI bus and ignores the received data.
312  * @pre A slave must have been selected using @p spiSelect() or
313  * @p spiSelectI().
314  * @post At the end of the operation the configured callback is invoked.
315  *
316  * @param[in] spip pointer to the @p SPIDriver object
317  * @param[in] n number of words to be ignored
318  *
319  * @iclass
320  */
321 #define spiStartIgnoreI(spip, n) { \
322  (spip)->state = SPI_ACTIVE; \
323  spi_lld_ignore(spip, n); \
324 }
325 
326 /**
327  * @brief Exchanges data on the SPI bus.
328  * @details This asynchronous function starts a simultaneous transmit/receive
329  * operation.
330  * @pre A slave must have been selected using @p spiSelect() or
331  * @p spiSelectI().
332  * @post At the end of the operation the configured callback is invoked.
333  * @note The buffers are organized as uint8_t arrays for data sizes below
334  * or equal to 8 bits else it is organized as uint16_t arrays.
335  *
336  * @param[in] spip pointer to the @p SPIDriver object
337  * @param[in] n number of words to be exchanged
338  * @param[in] txbuf the pointer to the transmit buffer
339  * @param[out] rxbuf the pointer to the receive buffer
340  *
341  * @iclass
342  */
343 #define spiStartExchangeI(spip, n, txbuf, rxbuf) { \
344  (spip)->state = SPI_ACTIVE; \
345  spi_lld_exchange(spip, n, txbuf, rxbuf); \
346 }
347 
348 /**
349  * @brief Sends data over the SPI bus.
350  * @details This asynchronous function starts a transmit operation.
351  * @pre A slave must have been selected using @p spiSelect() or
352  * @p spiSelectI().
353  * @post At the end of the operation the configured callback is invoked.
354  * @note The buffers are organized as uint8_t arrays for data sizes below
355  * or equal to 8 bits else it is organized as uint16_t arrays.
356  *
357  * @param[in] spip pointer to the @p SPIDriver object
358  * @param[in] n number of words to send
359  * @param[in] txbuf the pointer to the transmit buffer
360  *
361  * @iclass
362  */
363 #define spiStartSendI(spip, n, txbuf) { \
364  (spip)->state = SPI_ACTIVE; \
365  spi_lld_send(spip, n, txbuf); \
366 }
367 
368 /**
369  * @brief Receives data from the SPI bus.
370  * @details This asynchronous function starts a receive operation.
371  * @pre A slave must have been selected using @p spiSelect() or
372  * @p spiSelectI().
373  * @post At the end of the operation the configured callback is invoked.
374  * @note The buffers are organized as uint8_t arrays for data sizes below
375  * or equal to 8 bits else it is organized as uint16_t arrays.
376  *
377  * @param[in] spip pointer to the @p SPIDriver object
378  * @param[in] n number of words to receive
379  * @param[out] rxbuf the pointer to the receive buffer
380  *
381  * @iclass
382  */
383 #define spiStartReceiveI(spip, n, rxbuf) { \
384  (spip)->state = SPI_ACTIVE; \
385  spi_lld_receive(spip, n, rxbuf); \
386 }
387 
388 /**
389  * @brief Exchanges one frame using a polled wait.
390  * @details This synchronous function exchanges one frame using a polled
391  * synchronization method. This function is useful when exchanging
392  * small amount of data on high speed channels, usually in this
393  * situation is much more efficient just wait for completion using
394  * polling than suspending the thread waiting for an interrupt.
395  * @note This API is implemented as a macro in order to minimize latency.
396  *
397  * @param[in] spip pointer to the @p SPIDriver object
398  * @param[in] frame the data frame to send over the SPI bus
399  * @return The received data frame from the SPI bus.
400  */
401 #define spiPolledExchange(spip, frame) spi_lld_polled_exchange(spip, frame)
402 /** @} */
403 
404 /**
405  * @name Low level driver helper macros
406  * @{
407  */
408 #if (SPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
409 /**
410  * @brief Wakes up the waiting thread.
411  *
412  * @param[in] spip pointer to the @p SPIDriver object
413  *
414  * @notapi
415  */
416 #define _spi_wakeup_isr(spip) { \
417  osalSysLockFromISR(); \
418  osalThreadResumeI(&(spip)->thread, MSG_OK); \
419  osalSysUnlockFromISR(); \
420 }
421 #else /* !SPI_USE_WAIT */
422 #define _spi_wakeup_isr(spip)
423 #endif /* !SPI_USE_WAIT */
424 
425 /**
426  * @brief Common ISR code when circular mode is not supported.
427  * @details This code handles the portable part of the ISR code:
428  * - Callback invocation.
429  * - Waiting thread wakeup, if any.
430  * - Driver state transitions.
431  * .
432  * @note This macro is meant to be used in the low level drivers
433  * implementation only.
434  *
435  * @param[in] spip pointer to the @p SPIDriver object
436  *
437  * @notapi
438  */
439 #define _spi_isr_code(spip) { \
440  if ((spip)->config->end_cb) { \
441  (spip)->state = SPI_COMPLETE; \
442  (spip)->config->end_cb(spip); \
443  if ((spip)->state == SPI_COMPLETE) \
444  (spip)->state = SPI_READY; \
445  } \
446  else \
447  (spip)->state = SPI_READY; \
448  _spi_wakeup_isr(spip); \
449 }
450 
451 /**
452  * @brief Half buffer filled ISR code in circular mode.
453  * @details This code handles the portable part of the ISR code:
454  * - Callback invocation.
455  * .
456  * @note This macro is meant to be used in the low level drivers
457  * implementation only.
458  *
459  * @param[in] spip pointer to the @p SPIDriver object
460  *
461  * @notapi
462  */
463 #define _spi_isr_half_code(spip) { \
464  if ((spip)->config->end_cb) { \
465  (spip)->config->end_cb(spip); \
466  } \
467 }
468 
469 /**
470  * @brief Full buffer filled ISR code in circular mode.
471  * @details This code handles the portable part of the ISR code:
472  * - Callback invocation.
473  * - Driver state transitions.
474  * .
475  * @note This macro is meant to be used in the low level drivers
476  * implementation only.
477  *
478  * @param[in] spip pointer to the @p SPIDriver object
479  *
480  * @notapi
481  */
482 #define _spi_isr_full_code(spip) { \
483  if ((spip)->config->end_cb) { \
484  (spip)->state = SPI_COMPLETE; \
485  (spip)->config->end_cb(spip); \
486  if ((spip)->state == SPI_COMPLETE) \
487  (spip)->state = SPI_ACTIVE; \
488  } \
489 }
490 /** @} */
491 
492 /*===========================================================================*/
493 /* External declarations. */
494 /*===========================================================================*/
495 
496 #ifdef __cplusplus
497 extern "C" {
498 #endif
499  void spiInit(void);
500  void spiObjectInit(SPIDriver *spip);
501  void spiStart(SPIDriver *spip, const SPIConfig *config);
502  void spiStop(SPIDriver *spip);
503  void spiSelect(SPIDriver *spip);
504  void spiUnselect(SPIDriver *spip);
505  void spiStartIgnore(SPIDriver *spip, size_t n);
506  void spiStartExchange(SPIDriver *spip, size_t n,
507  const void *txbuf, void *rxbuf);
508  void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf);
509  void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf);
510 #if SPI_SUPPORTS_CIRCULAR == TRUE
511  void spiAbortI(SPIDriver *spip);
512  void spiAbort(SPIDriver *spip);
513 #endif
514 #if SPI_USE_WAIT == TRUE
515  void spiIgnore(SPIDriver *spip, size_t n);
516  void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf);
517  void spiSend(SPIDriver *spip, size_t n, const void *txbuf);
518  void spiReceive(SPIDriver *spip, size_t n, void *rxbuf);
519 #endif
520 #if SPI_USE_MUTUAL_EXCLUSION == TRUE
521  void spiAcquireBus(SPIDriver *spip);
522  void spiReleaseBus(SPIDriver *spip);
523 #endif
524 #ifdef __cplusplus
525 }
526 #endif
527 
528 #endif /* HAL_USE_SPI == TRUE */
529 
530 #endif /* HAL_SPI_H */
531 
532 /** @} */
spistate_t state
Driver state.
Definition: hal_spi.h:196
uint32_t ioportmask_t
Digital I/O port sized unsigned type.
Definition: hal_pal_lld.h:104
void spiUnselect(SPIDriver *spip)
Deasserts the slave select signal.
Definition: hal_spi.c:152
spistate_t
Driver state machine possible states.
Definition: hal_spi.h:116
void spiStartExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:202
void spiSelect(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
Definition: hal_spi.c:134
#define spi_lld_driver_fields
Low level fields of the SPI driver structure.
Definition: hal_spi_lld.h:72
void spiObjectInit(SPIDriver *spip)
Initializes the standard part of a SPIDriver structure.
Definition: hal_spi.c:68
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.c:416
void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:354
uint32_t ioline_t
Type of an I/O line.
Definition: hal_pal_lld.h:114
void spiAbortI(SPIDriver *spip)
Aborts the ongoing SPI operation.
Definition: hal_spi.c:272
void spiStart(SPIDriver *spip, const SPIConfig *config)
Configures and activates the SPI peripheral.
Definition: hal_spi.c:91
ioportid_t ssport
The chip select port.
Definition: hal_spi.h:169
void spiAbort(SPIDriver *spip)
Aborts the ongoing SPI operation, if any.
Definition: hal_spi.c:294
spicallback_t end_cb
Operation complete callback or NULL.
Definition: hal_spi.h:158
uint_fast8_t sspad
The chip select pad number.
Definition: hal_spi.h:183
void spiReleaseBus(SPIDriver *spip)
Releases exclusive access to the SPI bus.
Definition: hal_spi.c:459
void spiInit(void)
SPI Driver initialization.
Definition: hal_spi.c:56
void spiAcquireBus(SPIDriver *spip)
Gains exclusive access to the SPI bus.
Definition: hal_spi.c:443
bool circular
Enables the circular buffer mode.
Definition: hal_spi.h:153
uint32_t ioportid_t
Port Identifier.
Definition: hal_pal_lld.h:122
void(* spicallback_t)(SPIDriver *spip)
SPI notification callback type.
Definition: hal_spi.h:139
Structure representing an SPI driver.
Definition: hal_spi.h:192
void spiStartIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:175
ioline_t ssline
The chip select line.
Definition: hal_spi.h:163
ioportmask_t ssmask
The chip select port mask.
Definition: hal_spi.h:173
void spiIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:322
void * thread_reference_t
Type of a thread reference.
Definition: osal.h:180
Driver configuration structure.
Definition: hal_spi.h:148
const SPIConfig * config
Current configuration data.
Definition: hal_spi.h:200
PLATFORM SPI subsystem low level driver header.
void spiSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:386
thread_reference_t thread
Waiting thread.
Definition: hal_spi.h:205
uint32_t mutex_t
Type of a mutex.
Definition: osal.h:223
void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:229
mutex_t mutex
Mutex protecting the peripheral.
Definition: hal_spi.h:211
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi.c:111
void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.c:254