ChibiOS  21.6.0
hal_spi.c
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.c
19  * @brief SPI Driver code.
20  *
21  * @addtogroup SPI
22  * @{
23  */
24 
25 #include "hal.h"
26 
27 #if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
28 
29 /*===========================================================================*/
30 /* Driver local definitions. */
31 /*===========================================================================*/
32 
33 /*===========================================================================*/
34 /* Driver exported variables. */
35 /*===========================================================================*/
36 
37 /*===========================================================================*/
38 /* Driver local variables and types. */
39 /*===========================================================================*/
40 
41 /*===========================================================================*/
42 /* Driver local functions. */
43 /*===========================================================================*/
44 
45 /*===========================================================================*/
46 /* Driver exported functions. */
47 /*===========================================================================*/
48 
49 /**
50  * @brief SPI Driver initialization.
51  * @note This function is implicitly invoked by @p halInit(), there is
52  * no need to explicitly initialize the driver.
53  *
54  * @init
55  */
56 void spiInit(void) {
57 
58  spi_lld_init();
59 }
60 
61 /**
62  * @brief Initializes the standard part of a @p SPIDriver structure.
63  *
64  * @param[out] spip pointer to the @p SPIDriver object
65  *
66  * @init
67  */
68 void spiObjectInit(SPIDriver *spip) {
69 
70  spip->state = SPI_STOP;
71  spip->config = NULL;
72 #if SPI_USE_WAIT == TRUE
73  spip->thread = NULL;
74 #endif
75 #if SPI_USE_MUTUAL_EXCLUSION == TRUE
76  osalMutexObjectInit(&spip->mutex);
77 #endif
78 #if defined(SPI_DRIVER_EXT_INIT_HOOK)
79  SPI_DRIVER_EXT_INIT_HOOK(spip);
80 #endif
81 }
82 
83 /**
84  * @brief Configures and activates the SPI peripheral.
85  *
86  * @param[in] spip pointer to the @p SPIDriver object
87  * @param[in] config pointer to the @p SPIConfig object
88  *
89  * @api
90  */
91 void spiStart(SPIDriver *spip, const SPIConfig *config) {
92 
93  osalDbgCheck((spip != NULL) && (config != NULL));
94 
95  osalSysLock();
96  osalDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
97  "invalid state");
98  spip->config = config;
99  spi_lld_start(spip);
100  spip->state = SPI_READY;
101  osalSysUnlock();
102 }
103 
104 /**
105  * @brief Deactivates the SPI peripheral.
106  *
107  * @param[in] spip pointer to the @p SPIDriver object
108  *
109  * @api
110  */
111 void spiStop(SPIDriver *spip) {
112 
113  osalDbgCheck(spip != NULL);
114 
115  osalSysLock();
116 
117  osalDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
118  "invalid state");
119 
120  spi_lld_stop(spip);
121  spip->config = NULL;
122  spip->state = SPI_STOP;
123 
124  osalSysUnlock();
125 }
126 
127 /**
128  * @brief Asserts the slave select signal and prepares for transfers.
129  *
130  * @param[in] spip pointer to the @p SPIDriver object
131  *
132  * @api
133  */
134 void spiSelect(SPIDriver *spip) {
135 
136  osalDbgCheck(spip != NULL);
137 
138  osalSysLock();
139  osalDbgAssert(spip->state == SPI_READY, "not ready");
140  spiSelectI(spip);
141  osalSysUnlock();
142 }
143 
144 /**
145  * @brief Deasserts the slave select signal.
146  * @details The previously selected peripheral is unselected.
147  *
148  * @param[in] spip pointer to the @p SPIDriver object
149  *
150  * @api
151  */
152 void spiUnselect(SPIDriver *spip) {
153 
154  osalDbgCheck(spip != NULL);
155 
156  osalSysLock();
157  osalDbgAssert(spip->state == SPI_READY, "not ready");
158  spiUnselectI(spip);
159  osalSysUnlock();
160 }
161 
162 /**
163  * @brief Ignores data on the SPI bus.
164  * @details This asynchronous function starts the transmission of a series of
165  * idle words on the SPI bus and ignores the received data.
166  * @pre A slave must have been selected using @p spiSelect() or
167  * @p spiSelectI().
168  * @post At the end of the operation the configured callback is invoked.
169  *
170  * @param[in] spip pointer to the @p SPIDriver object
171  * @param[in] n number of words to be ignored
172  *
173  * @api
174  */
175 void spiStartIgnore(SPIDriver *spip, size_t n) {
176 
177  osalDbgCheck((spip != NULL) && (n > 0U));
178 
179  osalSysLock();
180  osalDbgAssert(spip->state == SPI_READY, "not ready");
181  spiStartIgnoreI(spip, n);
182  osalSysUnlock();
183 }
184 
185 /**
186  * @brief Exchanges data on the SPI bus.
187  * @details This asynchronous function starts a simultaneous transmit/receive
188  * operation.
189  * @pre A slave must have been selected using @p spiSelect() or
190  * @p spiSelectI().
191  * @post At the end of the operation the configured callback is invoked.
192  * @note The buffers are organized as uint8_t arrays for data sizes below
193  * or equal to 8 bits else it is organized as uint16_t arrays.
194  *
195  * @param[in] spip pointer to the @p SPIDriver object
196  * @param[in] n number of words to be exchanged
197  * @param[in] txbuf the pointer to the transmit buffer
198  * @param[out] rxbuf the pointer to the receive buffer
199  *
200  * @api
201  */
202 void spiStartExchange(SPIDriver *spip, size_t n,
203  const void *txbuf, void *rxbuf) {
204 
205  osalDbgCheck((spip != NULL) && (n > 0U) &&
206  (rxbuf != NULL) && (txbuf != NULL));
207 
208  osalSysLock();
209  osalDbgAssert(spip->state == SPI_READY, "not ready");
210  spiStartExchangeI(spip, n, txbuf, rxbuf);
211  osalSysUnlock();
212 }
213 
214 /**
215  * @brief Sends data over the SPI bus.
216  * @details This asynchronous function starts a transmit operation.
217  * @pre A slave must have been selected using @p spiSelect() or
218  * @p spiSelectI().
219  * @post At the end of the operation the configured callback is invoked.
220  * @note The buffers are organized as uint8_t arrays for data sizes below
221  * or equal to 8 bits else it is organized as uint16_t arrays.
222  *
223  * @param[in] spip pointer to the @p SPIDriver object
224  * @param[in] n number of words to send
225  * @param[in] txbuf the pointer to the transmit buffer
226  *
227  * @api
228  */
229 void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf) {
230 
231  osalDbgCheck((spip != NULL) && (n > 0U) && (txbuf != NULL));
232 
233  osalSysLock();
234  osalDbgAssert(spip->state == SPI_READY, "not ready");
235  spiStartSendI(spip, n, txbuf);
236  osalSysUnlock();
237 }
238 
239 /**
240  * @brief Receives data from the SPI bus.
241  * @details This asynchronous function starts a receive operation.
242  * @pre A slave must have been selected using @p spiSelect() or
243  * @p spiSelectI().
244  * @post At the end of the operation the configured callback is invoked.
245  * @note The buffers are organized as uint8_t arrays for data sizes below
246  * or equal to 8 bits else it is organized as uint16_t arrays.
247  *
248  * @param[in] spip pointer to the @p SPIDriver object
249  * @param[in] n number of words to receive
250  * @param[out] rxbuf the pointer to the receive buffer
251  *
252  * @api
253  */
254 void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf) {
255 
256  osalDbgCheck((spip != NULL) && (n > 0U) && (rxbuf != NULL));
257 
258  osalSysLock();
259  osalDbgAssert(spip->state == SPI_READY, "not ready");
260  spiStartReceiveI(spip, n, rxbuf);
261  osalSysUnlock();
262 }
263 
264 #if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__)
265 /**
266  * @brief Aborts the ongoing SPI operation.
267  *
268  * @param[in] spip pointer to the @p SPIDriver object
269  *
270  * @iclass
271  */
272 void spiAbortI(SPIDriver *spip) {
273 
275 
276  osalDbgCheck(spip != NULL);
277  osalDbgAssert((spip->state == SPI_ACTIVE) || (spip->state == SPI_COMPLETE),
278  "invalid state");
279 
280  spi_lld_abort(spip);
281  spip->state = SPI_READY;
282 #if SPI_USE_WAIT == TRUE
284 #endif
285 }
286 
287 /**
288  * @brief Aborts the ongoing SPI operation, if any.
289  *
290  * @param[in] spip pointer to the @p SPIDriver object
291  *
292  * @api
293  */
294 void spiAbort(SPIDriver *spip) {
295 
296  osalSysLock();
297  osalDbgAssert((spip->state == SPI_READY) || (spip->state == SPI_ACTIVE),
298  "invalid state");
299  if (spip->state == SPI_ACTIVE) {
300  spiAbortI(spip);
302  }
303  osalSysUnlock();
304 }
305 #endif
306 
307 #if (SPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
308 /**
309  * @brief Ignores data on the SPI bus.
310  * @details This synchronous function performs the transmission of a series of
311  * idle words on the SPI bus and ignores the received data.
312  * @pre In order to use this function the option @p SPI_USE_WAIT must be
313  * enabled.
314  * @pre In order to use this function the driver must have been configured
315  * without callbacks (@p end_cb = @p NULL).
316  *
317  * @param[in] spip pointer to the @p SPIDriver object
318  * @param[in] n number of words to be ignored
319  *
320  * @api
321  */
322 void spiIgnore(SPIDriver *spip, size_t n) {
323 
324  osalDbgCheck((spip != NULL) && (n > 0U));
325 #if SPI_SUPPORTS_CIRCULAR
326  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
327 #endif
328 
329  osalSysLock();
330  osalDbgAssert(spip->state == SPI_READY, "not ready");
331  spiStartIgnoreI(spip, n);
332  (void) osalThreadSuspendS(&spip->thread);
333  osalSysUnlock();
334 }
335 
336 /**
337  * @brief Exchanges data on the SPI bus.
338  * @details This synchronous function performs a simultaneous transmit/receive
339  * operation.
340  * @pre In order to use this function the option @p SPI_USE_WAIT must be
341  * enabled.
342  * @pre In order to use this function the driver must have been configured
343  * without callbacks (@p end_cb = @p NULL).
344  * @note The buffers are organized as uint8_t arrays for data sizes below
345  * or equal to 8 bits else it is organized as uint16_t arrays.
346  *
347  * @param[in] spip pointer to the @p SPIDriver object
348  * @param[in] n number of words to be exchanged
349  * @param[in] txbuf the pointer to the transmit buffer
350  * @param[out] rxbuf the pointer to the receive buffer
351  *
352  * @api
353  */
354 void spiExchange(SPIDriver *spip, size_t n,
355  const void *txbuf, void *rxbuf) {
356 
357  osalDbgCheck((spip != NULL) && (n > 0U) &&
358  (rxbuf != NULL) && (txbuf != NULL));
359 #if SPI_SUPPORTS_CIRCULAR
360  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
361 #endif
362 
363  osalSysLock();
364  osalDbgAssert(spip->state == SPI_READY, "not ready");
365  spiStartExchangeI(spip, n, txbuf, rxbuf);
366  (void) osalThreadSuspendS(&spip->thread);
367  osalSysUnlock();
368 }
369 
370 /**
371  * @brief Sends data over the SPI bus.
372  * @details This synchronous function performs a transmit operation.
373  * @pre In order to use this function the option @p SPI_USE_WAIT must be
374  * enabled.
375  * @pre In order to use this function the driver must have been configured
376  * without callbacks (@p end_cb = @p NULL).
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 send
382  * @param[in] txbuf the pointer to the transmit buffer
383  *
384  * @api
385  */
386 void spiSend(SPIDriver *spip, size_t n, const void *txbuf) {
387 
388  osalDbgCheck((spip != NULL) && (n > 0U) && (txbuf != NULL));
389 #if SPI_SUPPORTS_CIRCULAR
390  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
391 #endif
392 
393  osalSysLock();
394  osalDbgAssert(spip->state == SPI_READY, "not ready");
395  spiStartSendI(spip, n, txbuf);
396  (void) osalThreadSuspendS(&spip->thread);
397  osalSysUnlock();
398 }
399 
400 /**
401  * @brief Receives data from the SPI bus.
402  * @details This synchronous function performs a receive operation.
403  * @pre In order to use this function the option @p SPI_USE_WAIT must be
404  * enabled.
405  * @pre In order to use this function the driver must have been configured
406  * without callbacks (@p end_cb = @p NULL).
407  * @note The buffers are organized as uint8_t arrays for data sizes below
408  * or equal to 8 bits else it is organized as uint16_t arrays.
409  *
410  * @param[in] spip pointer to the @p SPIDriver object
411  * @param[in] n number of words to receive
412  * @param[out] rxbuf the pointer to the receive buffer
413  *
414  * @api
415  */
416 void spiReceive(SPIDriver *spip, size_t n, void *rxbuf) {
417 
418  osalDbgCheck((spip != NULL) && (n > 0U) && (rxbuf != NULL));
419 #if SPI_SUPPORTS_CIRCULAR
420  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
421 #endif
422 
423  osalSysLock();
424  osalDbgAssert(spip->state == SPI_READY, "not ready");
425  spiStartReceiveI(spip, n, rxbuf);
426  (void) osalThreadSuspendS(&spip->thread);
427  osalSysUnlock();
428 }
429 #endif /* SPI_USE_WAIT == TRUE */
430 
431 #if (SPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
432 /**
433  * @brief Gains exclusive access to the SPI bus.
434  * @details This function tries to gain ownership to the SPI bus, if the bus
435  * is already being used then the invoking thread is queued.
436  * @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
437  * must be enabled.
438  *
439  * @param[in] spip pointer to the @p SPIDriver object
440  *
441  * @api
442  */
444 
445  osalDbgCheck(spip != NULL);
446 
447  osalMutexLock(&spip->mutex);
448 }
449 
450 /**
451  * @brief Releases exclusive access to the SPI bus.
452  * @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
453  * must be enabled.
454  *
455  * @param[in] spip pointer to the @p SPIDriver object
456  *
457  * @api
458  */
460 
461  osalDbgCheck(spip != NULL);
462 
463  osalMutexUnlock(&spip->mutex);
464 }
465 #endif /* SPI_USE_MUTUAL_EXCLUSION == TRUE */
466 
467 #endif /* HAL_USE_SPI == TRUE */
468 
469 /** @} */
spiSelect
void spiSelect(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
Definition: hal_spi.c:134
spiStartIgnoreI
#define spiStartIgnoreI(spip, n)
Ignores data on the SPI bus.
Definition: hal_spi.h:319
hal_spi_driver::mutex
mutex_t mutex
Mutex protecting the peripheral.
Definition: hal_spi.h:209
osalThreadSuspendS
msg_t osalThreadSuspendS(thread_reference_t *trp)
Sends the current thread sleeping and sets a reference variable.
Definition: osal.c:185
hal.h
HAL subsystem header.
spi_lld_stop
void spi_lld_stop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi_lld.c:101
spiReceive
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.c:416
hal_spi_config
Driver configuration structure.
Definition: hal_spi.h:146
hal_spi_driver
Structure representing an SPI driver.
Definition: hal_spi.h:190
osalDbgCheckClassI
#define osalDbgCheckClassI()
I-Class state check.
Definition: osal.h:298
spi_lld_abort
void spi_lld_abort(SPIDriver *spip)
Aborts the ongoing SPI operation, if any.
Definition: hal_spi_lld.c:233
SPI_READY
@ SPI_READY
Definition: hal_spi.h:117
hal_spi_driver::state
spistate_t state
Driver state.
Definition: hal_spi.h:194
osalSysUnlock
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:611
spiStartSendI
#define spiStartSendI(spip, n, txbuf)
Sends data over the SPI bus.
Definition: hal_spi.h:361
spiAbort
void spiAbort(SPIDriver *spip)
Aborts the ongoing SPI operation, if any.
Definition: hal_spi.c:294
osalMutexLock
void osalMutexLock(mutex_t *mp)
Locks the specified mutex.
Definition: osal.c:384
spiInit
void spiInit(void)
SPI Driver initialization.
Definition: hal_spi.c:56
osalOsRescheduleS
void osalOsRescheduleS(void)
Checks if a reschedule is required and performs it.
Definition: osal.c:119
spiStartReceive
void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.c:254
hal_spi_config::circular
bool circular
Enables the circular buffer mode.
Definition: hal_spi.h:151
spiUnselectI
#define spiUnselectI(spip)
Deasserts the slave select signal.
Definition: hal_spi.h:262
spiStartExchangeI
#define spiStartExchangeI(spip, n, txbuf, rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.h:341
spiSelectI
#define spiSelectI(spip)
Asserts the slave select signal and prepares for transfers.
Definition: hal_spi.h:249
SPI_ACTIVE
@ SPI_ACTIVE
Definition: hal_spi.h:118
spi_lld_start
void spi_lld_start(SPIDriver *spip)
Configures and activates the SPI peripheral.
Definition: hal_spi_lld.c:80
spiStartReceiveI
#define spiStartReceiveI(spip, n, rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.h:381
spiObjectInit
void spiObjectInit(SPIDriver *spip)
Initializes the standard part of a SPIDriver structure.
Definition: hal_spi.c:68
spiStop
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi.c:111
spiReleaseBus
void spiReleaseBus(SPIDriver *spip)
Releases exclusive access to the SPI bus.
Definition: hal_spi.c:459
spiSend
void spiSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:386
spiAcquireBus
void spiAcquireBus(SPIDriver *spip)
Gains exclusive access to the SPI bus.
Definition: hal_spi.c:443
spiExchange
void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:354
spiStart
void spiStart(SPIDriver *spip, const SPIConfig *config)
Configures and activates the SPI peripheral.
Definition: hal_spi.c:91
MSG_OK
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
spiStartExchange
void spiStartExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:202
osalDbgCheck
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:284
spiIgnore
void spiIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:322
osalSysLock
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:601
spiAbortI
void spiAbortI(SPIDriver *spip)
Aborts the ongoing SPI operation.
Definition: hal_spi.c:272
osalDbgAssert
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:264
spiStartIgnore
void spiStartIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:175
osalThreadResumeI
void osalThreadResumeI(thread_reference_t *trp, msg_t msg)
Wakes up a thread waiting on a thread reference object.
Definition: osal.c:230
spiUnselect
void spiUnselect(SPIDriver *spip)
Deasserts the slave select signal.
Definition: hal_spi.c:152
SPI_STOP
@ SPI_STOP
Definition: hal_spi.h:116
SPI_COMPLETE
@ SPI_COMPLETE
Definition: hal_spi.h:119
spiStartSend
void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:229
osalMutexUnlock
void osalMutexUnlock(mutex_t *mp)
Unlocks the specified mutex.
Definition: osal.c:404
spi_lld_init
void spi_lld_init(void)
Low level SPI driver initialization.
Definition: hal_spi_lld.c:65
hal_spi_driver::config
const SPIConfig * config
Current configuration data.
Definition: hal_spi.h:198
osalMutexObjectInit
static void osalMutexObjectInit(mutex_t *mp)
Initializes s mutex_t object.
Definition: osal.h:753
hal_spi_driver::thread
thread_reference_t thread
Waiting thread.
Definition: hal_spi.h:203