ChibiOS/HAL  6.1.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
283  osalThreadResumeI(&spip->thread, MSG_OK);
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 /** @} */
static void osalMutexObjectInit(mutex_t *mp)
Initializes s mutex_t object.
Definition: osal.h:681
#define spiStartSendI(spip, n, txbuf)
Sends data over the SPI bus.
Definition: hal_spi.h:251
void spiUnselect(SPIDriver *spip)
Deasserts the slave select signal.
Definition: hal_spi.c:152
void spiStartExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:202
msg_t osalThreadSuspendS(thread_reference_t *trp)
Sends the current thread sleeping and sets a reference variable.
Definition: osal.c:185
void spiSelect(SPIDriver *spip)
Asserts the slave select signal and prepares for transfers.
Definition: hal_spi.c:134
#define spiStartIgnoreI(spip, n)
Ignores data on the SPI bus.
Definition: hal_spi.h:209
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
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
HAL subsystem header.
#define osalDbgCheckClassI()
I-Class state check.
Definition: osal.h:292
void spiAbort(SPIDriver *spip)
Aborts the ongoing SPI operation, if any.
Definition: hal_spi.c:294
void osalMutexLock(mutex_t *mp)
Locks the specified mutex.
Definition: osal.c:384
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:540
void spiReleaseBus(SPIDriver *spip)
Releases exclusive access to the SPI bus.
Definition: hal_spi.c:459
void osalOsRescheduleS(void)
Checks if a reschedule is required and performs it.
Definition: osal.c:119
void spiInit(void)
SPI Driver initialization.
Definition: hal_spi.c:56
const SPIConfig * config
Current configuration data.
Definition: hal_spi_lld.h:136
void spiAcquireBus(SPIDriver *spip)
Gains exclusive access to the SPI bus.
Definition: hal_spi.c:443
#define spiStartReceiveI(spip, n, rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.h:271
#define spiStartExchangeI(spip, n, txbuf, rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.h:231
#define spiSelectI(spip)
Asserts the slave select signal and prepares for transfers.
Definition: hal_spi.h:139
Driver configuration structure.
Definition: hal_spi_lld.h:83
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:278
void spiStartIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:175
void spi_lld_stop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi_lld.c:101
void spi_lld_abort(SPIDriver *spip)
Aborts the ongoing SPI operation, if any.
Definition: hal_spi_lld.c:233
void spi_lld_start(SPIDriver *spip)
Configures and activates the SPI peripheral.
Definition: hal_spi_lld.c:80
void spiIgnore(SPIDriver *spip, size_t n)
Ignores data on the SPI bus.
Definition: hal_spi.c:322
void spi_lld_init(void)
Low level SPI driver initialization.
Definition: hal_spi_lld.c:65
mutex_t mutex
Mutex protecting the peripheral.
Definition: hal_spi_lld.h:147
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:530
void spiSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:386
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:258
#define spiUnselectI(spip)
Deasserts the slave select signal.
Definition: hal_spi.h:152
thread_reference_t thread
Waiting thread.
Definition: hal_spi_lld.h:141
void osalMutexUnlock(mutex_t *mp)
Unlocks the specified mutex.
Definition: osal.c:404
bool circular
Enables the circular buffer mode.
Definition: hal_spi_lld.h:88
void osalThreadResumeI(thread_reference_t *trp, msg_t msg)
Wakes up a thread waiting on a thread reference object.
Definition: osal.c:230
void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf)
Sends data over the SPI bus.
Definition: hal_spi.c:229
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi.c:111
Structure representing an SPI driver.
Definition: hal_spi_lld.h:128
void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf)
Receives data from the SPI bus.
Definition: hal_spi.c:254
spistate_t state
Driver state.
Definition: hal_spi_lld.h:132