ChibiOS 21.11.5
hal_spi_v1.h
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006-2026 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_v1.h
19 * @brief SPI (v1) Driver macros and structures.
20 *
21 * @addtogroup SPI_V1
22 * @{
23 */
24
25#ifndef HAL_SPI_V1_H
26#define HAL_SPI_V1_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 the @p spiAcquireBus() and @p spiReleaseBus() APIs.
65 * @note Disabling this option saves both code and data space.
66 */
67#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
68#define SPI_USE_MUTUAL_EXCLUSION TRUE
69#endif
70
71/**
72 * @brief Handling method for SPI CS line.
73 * @note Disabling this option saves both code and data space.
74 */
75#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
76#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
77#endif
78/** @} */
79
80/*===========================================================================*/
81/* Derived constants and error checks. */
82/*===========================================================================*/
83
84#if (SPI_SELECT_MODE != SPI_SELECT_MODE_NONE) && \
85 (SPI_SELECT_MODE != SPI_SELECT_MODE_PAD) && \
86 (SPI_SELECT_MODE != SPI_SELECT_MODE_PORT) && \
87 (SPI_SELECT_MODE != SPI_SELECT_MODE_LINE) && \
88 (SPI_SELECT_MODE != SPI_SELECT_MODE_LLD)
89#error "invalid SPI_SELECT_MODE setting"
90#endif
91
92/* Some modes have a dependency on the PAL driver, making the required
93 checks here.*/
94#if ((SPI_SELECT_MODE == SPI_SELECT_MODE_PAD) || \
95 (SPI_SELECT_MODE == SPI_SELECT_MODE_PORT) || \
96 (SPI_SELECT_MODE == SPI_SELECT_MODE_LINE)) && \
97 (HAL_USE_PAL != TRUE)
98#error "current SPI_SELECT_MODE requires HAL_USE_PAL"
99#endif
100
101/*===========================================================================*/
102/* Driver data structures and types. */
103/*===========================================================================*/
104
105/**
106 * @brief Driver state machine possible states.
107 */
108typedef enum {
109 SPI_UNINIT = 0, /**< Not initialized. */
110 SPI_STOP = 1, /**< Stopped. */
111 SPI_READY = 2, /**< Ready. */
112 SPI_ACTIVE = 3, /**< Exchanging data. */
113 SPI_COMPLETE = 4 /**< Asynchronous operation complete. */
114} spistate_t;
115
116/**
117 * @brief Type of a structure representing an SPI driver.
118 */
119typedef struct hal_spi_driver SPIDriver;
120/**
121 * @brief Type of a SPI driver configuration structure.
122 */
123typedef struct hal_spi_config SPIConfig;
124
125/**
126 * @brief SPI notification callback type.
127 * @details The callback is invoked from ISR context.
128 *
129 * @param[in] spip pointer to the @p SPIDriver object triggering the
130 * callback
131 */
132typedef void (*spicallback_t)(SPIDriver *spip);
133
134/* Including the low level driver header, it exports information required
135 for completing types.*/
136#include "hal_spi_lld.h"
137
138/**
139 * @brief Driver configuration structure.
140 */
141struct hal_spi_config {
142#if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__)
143 /**
144 * @brief Enables the circular buffer mode.
145 */
146 bool circular;
147#endif
148 /**
149 * @brief Operation complete callback or @p NULL.
150 * @details In linear mode the callback is invoked after the driver returns
151 * to the @p SPI_READY state, this allows chaining another transfer
152 * using I-Class APIs. In circular mode the callback is invoked in
153 * either @p SPI_ACTIVE state (half buffer) or @p SPI_COMPLETE
154 * state (full buffer).
155 * @note If a synchronous API is waiting for completion then starting a
156 * follow-on transfer from this callback is undefined.
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.*/
187};
188
189/**
190 * @brief Structure representing an SPI driver.
191 */
192struct hal_spi_driver {
193 /**
194 * @brief Driver state.
195 */
197 /**
198 * @brief Current configuration data.
199 */
200 const SPIConfig *config;
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 * @note This state is only meaningful for circular transfers, where it is
232 * used to distinguish the full buffer callback from the half buffer
233 * callback.
234 *
235 * @param[in] spip pointer to the @p SPIDriver object
236 * @return The buffer state.
237 * @retval false if the driver filled/sent the first half of the
238 * buffer.
239 * @retval true if the driver filled/sent the second half of the
240 * buffer.
241 *
242 * @special
243 */
244#define spiIsBufferComplete(spip) ((bool)((spip)->state == SPI_COMPLETE))
245
246#if (SPI_SELECT_MODE == SPI_SELECT_MODE_LLD) || defined(__DOXYGEN__)
247/**
248 * @brief Asserts the slave select signal and prepares for transfers.
249 *
250 * @param[in] spip pointer to the @p SPIDriver object
251 *
252 * @iclass
253 */
254#define spiSelectI(spip) \
255do { \
256 spi_lld_select(spip); \
257} while (false)
258
259/**
260 * @brief Deasserts the slave select signal.
261 * @details The previously selected peripheral is unselected.
262 *
263 * @param[in] spip pointer to the @p SPIDriver object
264 *
265 * @iclass
266 */
267#define spiUnselectI(spip) \
268do { \
269 spi_lld_unselect(spip); \
270} while (false)
271
272#elif SPI_SELECT_MODE == SPI_SELECT_MODE_LINE
273#define spiSelectI(spip) \
274do { \
275 palClearLine((spip)->config->ssline); \
276} while (false)
277
278#define spiUnselectI(spip) \
279do { \
280 palSetLine((spip)->config->ssline); \
281} while (false)
282
283#elif SPI_SELECT_MODE == SPI_SELECT_MODE_PORT
284#define spiSelectI(spip) \
285do { \
286 palClearPort((spip)->config->ssport, (spip)->config->ssmask); \
287} while (false)
288
289#define spiUnselectI(spip) \
290do { \
291 palSetPort((spip)->config->ssport, (spip)->config->ssmask); \
292} while (false)
293
294#elif SPI_SELECT_MODE == SPI_SELECT_MODE_PAD
295#define spiSelectI(spip) \
296do { \
297 palClearPad((spip)->config->ssport, (spip)->config->sspad); \
298} while (false)
299
300#define spiUnselectI(spip) \
301do { \
302 palSetPad((spip)->config->ssport, (spip)->config->sspad); \
303} while (false)
304
305#elif SPI_SELECT_MODE == SPI_SELECT_MODE_NONE
306#define spiSelectI(spip)
307
308#define spiUnselectI(spip)
309#endif
310
311/**
312 * @brief Ignores data on the SPI bus.
313 * @details This asynchronous function starts the transmission of a series of
314 * idle words on the SPI bus and ignores the received data.
315 * @pre A slave must have been selected using @p spiSelect() or
316 * @p spiSelectI().
317 * @post At the end of the operation the configured callback is invoked.
318 *
319 * @param[in] spip pointer to the @p SPIDriver object
320 * @param[in] n number of words to be ignored
321 *
322 * @iclass
323 */
324#define spiStartIgnoreI(spip, n) { \
325 (spip)->state = SPI_ACTIVE; \
326 spi_lld_ignore(spip, n); \
327}
328
329/**
330 * @brief Exchanges data on the SPI bus.
331 * @details This asynchronous function starts a simultaneous transmit/receive
332 * operation.
333 * @pre A slave must have been selected using @p spiSelect() or
334 * @p spiSelectI().
335 * @post At the end of the operation the configured callback is invoked.
336 * @note The buffers are organized as uint8_t arrays for data sizes below
337 * or equal to 8 bits else it is organized as uint16_t arrays.
338 *
339 * @param[in] spip pointer to the @p SPIDriver object
340 * @param[in] n number of words to be exchanged
341 * @param[in] txbuf the pointer to the transmit buffer
342 * @param[out] rxbuf the pointer to the receive buffer
343 *
344 * @iclass
345 */
346#define spiStartExchangeI(spip, n, txbuf, rxbuf) { \
347 (spip)->state = SPI_ACTIVE; \
348 spi_lld_exchange(spip, n, txbuf, rxbuf); \
349}
350
351/**
352 * @brief Sends data over the SPI bus.
353 * @details This asynchronous function starts a transmit operation.
354 * @pre A slave must have been selected using @p spiSelect() or
355 * @p spiSelectI().
356 * @post At the end of the operation the configured callback is invoked.
357 * @note The buffers are organized as uint8_t arrays for data sizes below
358 * or equal to 8 bits else it is organized as uint16_t arrays.
359 *
360 * @param[in] spip pointer to the @p SPIDriver object
361 * @param[in] n number of words to send
362 * @param[in] txbuf the pointer to the transmit buffer
363 *
364 * @iclass
365 */
366#define spiStartSendI(spip, n, txbuf) { \
367 (spip)->state = SPI_ACTIVE; \
368 spi_lld_send(spip, n, txbuf); \
369}
370
371/**
372 * @brief Receives data from the SPI bus.
373 * @details This asynchronous function starts a receive operation.
374 * @pre A slave must have been selected using @p spiSelect() or
375 * @p spiSelectI().
376 * @post At the end of the operation the configured callback is invoked.
377 * @note The buffers are organized as uint8_t arrays for data sizes below
378 * or equal to 8 bits else it is organized as uint16_t arrays.
379 *
380 * @param[in] spip pointer to the @p SPIDriver object
381 * @param[in] n number of words to receive
382 * @param[out] rxbuf the pointer to the receive buffer
383 *
384 * @iclass
385 */
386#define spiStartReceiveI(spip, n, rxbuf) { \
387 (spip)->state = SPI_ACTIVE; \
388 spi_lld_receive(spip, n, rxbuf); \
389}
390
391/**
392 * @brief Exchanges one frame using a polled wait.
393 * @details This synchronous function exchanges one frame using a polled
394 * synchronization method. This function is useful when exchanging
395 * small amount of data on high speed channels, usually in this
396 * situation is much more efficient just wait for completion using
397 * polling than suspending the thread waiting for an interrupt.
398 * @note This API is implemented as a macro in order to minimize latency.
399 *
400 * @param[in] spip pointer to the @p SPIDriver object
401 * @param[in] frame the data frame to send over the SPI bus
402 * @return The received data frame from the SPI bus.
403 */
404#define spiPolledExchange(spip, frame) spi_lld_polled_exchange(spip, frame)
405/** @} */
406
407/**
408 * @name Low level driver helper macros
409 * @{
410 */
411#if (SPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
412/**
413 * @brief Wakes up the waiting thread.
414 *
415 * @param[in] spip pointer to the @p SPIDriver object
416 *
417 * @notapi
418 */
419#define _spi_wakeup_isr(spip) { \
420 osalSysLockFromISR(); \
421 osalThreadResumeI(&(spip)->thread, MSG_OK); \
422 osalSysUnlockFromISR(); \
423}
424#else /* !SPI_USE_WAIT */
425#define _spi_wakeup_isr(spip)
426#endif /* !SPI_USE_WAIT */
427
428/**
429 * @brief Common ISR code when circular mode is not supported.
430 * @details This code handles the portable part of the ISR code:
431 * - Callback invocation.
432 * - Waiting thread wakeup, if any.
433 * - Driver state transitions.
434 * .
435 * @note This macro is meant to be used in the low level drivers
436 * implementation only.
437 *
438 * @param[in] spip pointer to the @p SPIDriver object
439 *
440 * @notapi
441 */
442#define _spi_isr_code(spip) { \
443 (spip)->state = SPI_READY; \
444 if ((spip)->config->end_cb) { \
445 (spip)->config->end_cb(spip); \
446 } \
447 _spi_wakeup_isr(spip); \
448}
449
450/**
451 * @brief Half buffer filled ISR code in circular mode.
452 * @details This code handles the portable part of the ISR code:
453 * - Callback invocation.
454 * .
455 * @note This macro is meant to be used in the low level drivers
456 * implementation only.
457 *
458 * @param[in] spip pointer to the @p SPIDriver object
459 *
460 * @notapi
461 */
462#define _spi_isr_half_code(spip) { \
463 if ((spip)->config->end_cb) { \
464 (spip)->config->end_cb(spip); \
465 } \
466}
467
468/**
469 * @brief Full buffer filled ISR code in circular mode.
470 * @details This code handles the portable part of the ISR code:
471 * - Callback invocation.
472 * - Driver state transitions.
473 * .
474 * @note This macro is meant to be used in the low level drivers
475 * implementation only.
476 *
477 * @param[in] spip pointer to the @p SPIDriver object
478 *
479 * @notapi
480 */
481#define _spi_isr_full_code(spip) { \
482 if ((spip)->config->end_cb) { \
483 (spip)->state = SPI_COMPLETE; \
484 (spip)->config->end_cb(spip); \
485 if ((spip)->state == SPI_COMPLETE) \
486 (spip)->state = SPI_ACTIVE; \
487 } \
488}
489/** @} */
490
491/*===========================================================================*/
492/* External declarations. */
493/*===========================================================================*/
494
495#ifdef __cplusplus
496extern "C" {
497#endif
498 void spiInit(void);
499 void spiObjectInit(SPIDriver *spip);
500 msg_t spiStart(SPIDriver *spip, const SPIConfig *config);
501 void spiStop(SPIDriver *spip);
502 void spiSelect(SPIDriver *spip);
503 void spiUnselect(SPIDriver *spip);
504 void spiStartIgnore(SPIDriver *spip, size_t n);
505 void spiStartExchange(SPIDriver *spip, size_t n,
506 const void *txbuf, void *rxbuf);
507 void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf);
508 void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf);
509#if SPI_SUPPORTS_CIRCULAR == TRUE
510 void spiAbortI(SPIDriver *spip);
511 void spiAbort(SPIDriver *spip);
512#endif
513#if SPI_USE_WAIT == TRUE
514 void spiIgnore(SPIDriver *spip, size_t n);
515 void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf);
516 void spiSend(SPIDriver *spip, size_t n, const void *txbuf);
517 void spiReceive(SPIDriver *spip, size_t n, void *rxbuf);
518#endif
519#if SPI_USE_MUTUAL_EXCLUSION == TRUE
520 void spiAcquireBus(SPIDriver *spip);
521 void spiReleaseBus(SPIDriver *spip);
522#endif
523#ifdef __cplusplus
524}
525#endif
526
527#endif /* HAL_USE_SPI == TRUE */
528
529#endif /* HAL_SPI_V1_H */
530
531/** @} */
uint32_t ioportid_t
Port Identifier.
uint32_t ioportmask_t
Digital I/O port sized unsigned type.
uint32_t ioline_t
Type of an I/O line.
void spiSelect(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
void spiReleaseBus(SPIDriver *spip)
Releases exclusive access to the SPI bus.
spistate_t
Driver state machine possible states.
Definition hal_spi_v1.h:106
#define spi_lld_driver_fields
Low level fields of the SPI driver structure.
Definition hal_spi_lld.h:72
void spiSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
void spiInit(void)
SPI Driver initialization.
void spiIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
struct hal_spi_config SPIConfig
Type of a SPI driver configuration structure.
Definition hal_spi_v1.h:121
#define spi_lld_config_fields
Low level fields of the SPI configuration structure.
Definition hal_spi_lld.h:79
void spiAcquireBus(SPIDriver *spip)
Gains exclusive access to the SPI bus.
void spiObjectInit(SPIDriver *spip)
Initializes the standard part of a SPIDriver structure.
void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
msg_t spiStart(SPIDriver *spip, const SPIConfig *config)
Configures and activates the SPI peripheral.
void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
void spiStartIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
struct hal_spi_driver SPIDriver
Type of a structure representing an SPI driver.
Definition hal_spi_v1.h:117
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
void spiUnselect(SPIDriver *spip)
Deasserts the slave select signal.
void(* spicallback_t)(SPIDriver *spip)
SPI notification callback type.
Definition hal_spi_v1.h:130
void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
void spiStartExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
@ SPI_ACTIVE
Definition hal_spi_v1.h:110
@ SPI_UNINIT
Definition hal_spi_v1.h:107
@ SPI_STOP
Definition hal_spi_v1.h:108
@ SPI_READY
Definition hal_spi_v1.h:109
@ SPI_COMPLETE
Definition hal_spi_v1.h:111
spistate_t
Driver state machine possible states.
Definition hal_spi_v2.h:116
struct hal_spi_config SPIConfig
Type of a SPI driver configuration structure.
Definition hal_spi_v2.h:132
#define spiAbort(spip)
Compatibility API with SPI driver v1.
Definition hal_spi_v2.h:372
#define spiAbortI(spip)
Compatibility API with SPI driver v1.
Definition hal_spi_v2.h:363
struct ch_mutex mutex_t
Type of a mutex structure.
Definition chmtx.h:51
int32_t msg_t
Definition chearly.h:87
thread_t * thread_reference_t
Type of a thread reference.
Definition chobjects.h:135
PLATFORM SPI (v1) subsystem low level driver header.
Driver configuration structure.
Definition hal_spi_v1.h:139
ioportid_t ssport
The chip select port.
Definition hal_spi_v1.h:167
spicallback_t end_cb
Operation complete callback or NULL.
Definition hal_spi_v1.h:156
ioportmask_t ssmask
The chip select port mask.
Definition hal_spi_v1.h:171
bool circular
Enables the circular buffer mode.
Definition hal_spi_v1.h:144
uint_fast8_t sspad
The chip select pad number.
Definition hal_spi_v1.h:181
ioline_t ssline
The chip select line.
Definition hal_spi_v1.h:161
Structure representing an SPI driver.
Definition hal_spi_v1.h:190
const SPIConfig * config
Current configuration data.
Definition hal_spi_v1.h:198
thread_reference_t thread
Waiting thread.
Definition hal_spi_v1.h:203
mutex_t mutex
Mutex protecting the peripheral.
Definition hal_spi_v1.h:209
spistate_t state
Driver state.
Definition hal_spi_v1.h:194