ChibiOS  0.0.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  * @note Deactivating the peripheral also enforces a release of the slave
107  * select line.
108  *
109  * @param[in] spip pointer to the @p SPIDriver object
110  *
111  * @api
112  */
113 void spiStop(SPIDriver *spip) {
114 
115  osalDbgCheck(spip != NULL);
116 
117  osalSysLock();
118 
119  osalDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
120  "invalid state");
121 
122  spi_lld_stop(spip);
123  spip->config = NULL;
124  spip->state = SPI_STOP;
125 
126  osalSysUnlock();
127 }
128 
129 /**
130  * @brief Asserts the slave select signal and prepares for transfers.
131  *
132  * @param[in] spip pointer to the @p SPIDriver object
133  *
134  * @api
135  */
136 void spiSelect(SPIDriver *spip) {
137 
138  osalDbgCheck(spip != NULL);
139 
140  osalSysLock();
141  osalDbgAssert(spip->state == SPI_READY, "not ready");
142  spiSelectI(spip);
143  osalSysUnlock();
144 }
145 
146 /**
147  * @brief Deasserts the slave select signal.
148  * @details The previously selected peripheral is unselected.
149  *
150  * @param[in] spip pointer to the @p SPIDriver object
151  *
152  * @api
153  */
154 void spiUnselect(SPIDriver *spip) {
155 
156  osalDbgCheck(spip != NULL);
157 
158  osalSysLock();
159  osalDbgAssert(spip->state == SPI_READY, "not ready");
160  spiUnselectI(spip);
161  osalSysUnlock();
162 }
163 
164 /**
165  * @brief Ignores data on the SPI bus.
166  * @details This asynchronous function starts the transmission of a series of
167  * idle words on the SPI bus and ignores the received data.
168  * @pre A slave must have been selected using @p spiSelect() or
169  * @p spiSelectI().
170  * @post At the end of the operation the configured callback is invoked.
171  *
172  * @param[in] spip pointer to the @p SPIDriver object
173  * @param[in] n number of words to be ignored
174  *
175  * @api
176  */
177 void spiStartIgnore(SPIDriver *spip, size_t n) {
178 
179  osalDbgCheck((spip != NULL) && (n > 0U));
180 
181  osalSysLock();
182  osalDbgAssert(spip->state == SPI_READY, "not ready");
183  spiStartIgnoreI(spip, n);
184  osalSysUnlock();
185 }
186 
187 /**
188  * @brief Exchanges data on the SPI bus.
189  * @details This asynchronous function starts a simultaneous transmit/receive
190  * operation.
191  * @pre A slave must have been selected using @p spiSelect() or
192  * @p spiSelectI().
193  * @post At the end of the operation the configured callback is invoked.
194  * @note The buffers are organized as uint8_t arrays for data sizes below
195  * or equal to 8 bits else it is organized as uint16_t arrays.
196  *
197  * @param[in] spip pointer to the @p SPIDriver object
198  * @param[in] n number of words to be exchanged
199  * @param[in] txbuf the pointer to the transmit buffer
200  * @param[out] rxbuf the pointer to the receive buffer
201  *
202  * @api
203  */
204 void spiStartExchange(SPIDriver *spip, size_t n,
205  const void *txbuf, void *rxbuf) {
206 
207  osalDbgCheck((spip != NULL) && (n > 0U) &&
208  (rxbuf != NULL) && (txbuf != NULL));
209 
210  osalSysLock();
211  osalDbgAssert(spip->state == SPI_READY, "not ready");
212  spiStartExchangeI(spip, n, txbuf, rxbuf);
213  osalSysUnlock();
214 }
215 
216 /**
217  * @brief Sends data over the SPI bus.
218  * @details This asynchronous function starts a transmit operation.
219  * @pre A slave must have been selected using @p spiSelect() or
220  * @p spiSelectI().
221  * @post At the end of the operation the configured callback is invoked.
222  * @note The buffers are organized as uint8_t arrays for data sizes below
223  * or equal to 8 bits else it is organized as uint16_t arrays.
224  *
225  * @param[in] spip pointer to the @p SPIDriver object
226  * @param[in] n number of words to send
227  * @param[in] txbuf the pointer to the transmit buffer
228  *
229  * @api
230  */
231 void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf) {
232 
233  osalDbgCheck((spip != NULL) && (n > 0U) && (txbuf != NULL));
234 
235  osalSysLock();
236  osalDbgAssert(spip->state == SPI_READY, "not ready");
237  spiStartSendI(spip, n, txbuf);
238  osalSysUnlock();
239 }
240 
241 /**
242  * @brief Receives data from the SPI bus.
243  * @details This asynchronous function starts a receive operation.
244  * @pre A slave must have been selected using @p spiSelect() or
245  * @p spiSelectI().
246  * @post At the end of the operation the configured callback is invoked.
247  * @note The buffers are organized as uint8_t arrays for data sizes below
248  * or equal to 8 bits else it is organized as uint16_t arrays.
249  *
250  * @param[in] spip pointer to the @p SPIDriver object
251  * @param[in] n number of words to receive
252  * @param[out] rxbuf the pointer to the receive buffer
253  *
254  * @api
255  */
256 void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf) {
257 
258  osalDbgCheck((spip != NULL) && (n > 0U) && (rxbuf != NULL));
259 
260  osalSysLock();
261  osalDbgAssert(spip->state == SPI_READY, "not ready");
262  spiStartReceiveI(spip, n, rxbuf);
263  osalSysUnlock();
264 }
265 
266 #if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__)
267 /**
268  * @brief Aborts the ongoing SPI operation.
269  *
270  * @param[in] spip pointer to the @p SPIDriver object
271  *
272  * @iclass
273  */
274 void spiAbortI(SPIDriver *spip) {
275 
277 
278  osalDbgCheck(spip != NULL);
279  osalDbgAssert((spip->state == SPI_ACTIVE) || (spip->state == SPI_COMPLETE),
280  "invalid state");
281 
282  spi_lld_abort(spip);
283  spip->state = SPI_READY;
284 #if SPI_USE_WAIT == TRUE
286 #endif
287 }
288 
289 /**
290  * @brief Aborts the ongoing SPI operation, if any.
291  *
292  * @param[in] spip pointer to the @p SPIDriver object
293  *
294  * @api
295  */
296 void spiAbort(SPIDriver *spip) {
297 
298  osalSysLock();
299  osalDbgAssert((spip->state == SPI_READY) || (spip->state == SPI_ACTIVE),
300  "invalid state");
301  if (spip->state == SPI_ACTIVE) {
302  spiAbortI(spip);
304  }
305  osalSysUnlock();
306 }
307 #endif
308 
309 #if (SPI_USE_WAIT == TRUE) || defined(__DOXYGEN__)
310 /**
311  * @brief Ignores data on the SPI bus.
312  * @details This synchronous function performs the transmission of a series of
313  * idle words on the SPI bus and ignores the received data.
314  * @pre In order to use this function the option @p SPI_USE_WAIT must be
315  * enabled.
316  * @pre In order to use this function the driver must have been configured
317  * without callbacks (@p end_cb = @p NULL).
318  *
319  * @param[in] spip pointer to the @p SPIDriver object
320  * @param[in] n number of words to be ignored
321  *
322  * @api
323  */
324 void spiIgnore(SPIDriver *spip, size_t n) {
325 
326  osalDbgCheck((spip != NULL) && (n > 0U));
327 #if SPI_SUPPORTS_CIRCULAR
328  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
329 #endif
330 
331  osalSysLock();
332  osalDbgAssert(spip->state == SPI_READY, "not ready");
333  spiStartIgnoreI(spip, n);
334  (void) osalThreadSuspendS(&spip->thread);
335  osalSysUnlock();
336 }
337 
338 /**
339  * @brief Exchanges data on the SPI bus.
340  * @details This synchronous function performs a simultaneous transmit/receive
341  * operation.
342  * @pre In order to use this function the option @p SPI_USE_WAIT must be
343  * enabled.
344  * @pre In order to use this function the driver must have been configured
345  * without callbacks (@p end_cb = @p NULL).
346  * @note The buffers are organized as uint8_t arrays for data sizes below
347  * or equal to 8 bits else it is organized as uint16_t arrays.
348  *
349  * @param[in] spip pointer to the @p SPIDriver object
350  * @param[in] n number of words to be exchanged
351  * @param[in] txbuf the pointer to the transmit buffer
352  * @param[out] rxbuf the pointer to the receive buffer
353  *
354  * @api
355  */
356 void spiExchange(SPIDriver *spip, size_t n,
357  const void *txbuf, void *rxbuf) {
358 
359  osalDbgCheck((spip != NULL) && (n > 0U) &&
360  (rxbuf != NULL) && (txbuf != NULL));
361 #if SPI_SUPPORTS_CIRCULAR
362  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
363 #endif
364 
365  osalSysLock();
366  osalDbgAssert(spip->state == SPI_READY, "not ready");
367  spiStartExchangeI(spip, n, txbuf, rxbuf);
368  (void) osalThreadSuspendS(&spip->thread);
369  osalSysUnlock();
370 }
371 
372 /**
373  * @brief Sends data over the SPI bus.
374  * @details This synchronous function performs a transmit operation.
375  * @pre In order to use this function the option @p SPI_USE_WAIT must be
376  * enabled.
377  * @pre In order to use this function the driver must have been configured
378  * without callbacks (@p end_cb = @p NULL).
379  * @note The buffers are organized as uint8_t arrays for data sizes below
380  * or equal to 8 bits else it is organized as uint16_t arrays.
381  *
382  * @param[in] spip pointer to the @p SPIDriver object
383  * @param[in] n number of words to send
384  * @param[in] txbuf the pointer to the transmit buffer
385  *
386  * @api
387  */
388 void spiSend(SPIDriver *spip, size_t n, const void *txbuf) {
389 
390  osalDbgCheck((spip != NULL) && (n > 0U) && (txbuf != NULL));
391 #if SPI_SUPPORTS_CIRCULAR
392  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
393 #endif
394 
395  osalSysLock();
396  osalDbgAssert(spip->state == SPI_READY, "not ready");
397  spiStartSendI(spip, n, txbuf);
398  (void) osalThreadSuspendS(&spip->thread);
399  osalSysUnlock();
400 }
401 
402 /**
403  * @brief Receives data from the SPI bus.
404  * @details This synchronous function performs a receive operation.
405  * @pre In order to use this function the option @p SPI_USE_WAIT must be
406  * enabled.
407  * @pre In order to use this function the driver must have been configured
408  * without callbacks (@p end_cb = @p NULL).
409  * @note The buffers are organized as uint8_t arrays for data sizes below
410  * or equal to 8 bits else it is organized as uint16_t arrays.
411  *
412  * @param[in] spip pointer to the @p SPIDriver object
413  * @param[in] n number of words to receive
414  * @param[out] rxbuf the pointer to the receive buffer
415  *
416  * @api
417  */
418 void spiReceive(SPIDriver *spip, size_t n, void *rxbuf) {
419 
420  osalDbgCheck((spip != NULL) && (n > 0U) && (rxbuf != NULL));
421 #if SPI_SUPPORTS_CIRCULAR
422  osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U));
423 #endif
424 
425  osalSysLock();
426  osalDbgAssert(spip->state == SPI_READY, "not ready");
427  spiStartReceiveI(spip, n, rxbuf);
428  (void) osalThreadSuspendS(&spip->thread);
429  osalSysUnlock();
430 }
431 #endif /* SPI_USE_WAIT == TRUE */
432 
433 #if (SPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
434 /**
435  * @brief Gains exclusive access to the SPI bus.
436  * @details This function tries to gain ownership to the SPI bus, if the bus
437  * is already being used then the invoking thread is queued.
438  * @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
439  * must be enabled.
440  *
441  * @param[in] spip pointer to the @p SPIDriver object
442  *
443  * @api
444  */
446 
447  osalDbgCheck(spip != NULL);
448 
449  osalMutexLock(&spip->mutex);
450 }
451 
452 /**
453  * @brief Releases exclusive access to the SPI bus.
454  * @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
455  * must be enabled.
456  *
457  * @param[in] spip pointer to the @p SPIDriver object
458  *
459  * @api
460  */
462 
463  osalDbgCheck(spip != NULL);
464 
465  osalMutexUnlock(&spip->mutex);
466 }
467 #endif /* SPI_USE_MUTUAL_EXCLUSION == TRUE */
468 
469 #endif /* HAL_USE_SPI == TRUE */
470 
471 /** @} */
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:154
void spiStartExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:204
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:136
#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:418
void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf)
Exchanges data on the SPI bus.
Definition: hal_spi.c:356
void spiAbortI(SPIDriver *spip)
Aborts the ongoing SPI operation.
Definition: hal_spi.c:274
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:296
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:461
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:445
#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:177
void spi_lld_stop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi_lld.c:101
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
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:324
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:388
#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:231
void spiStop(SPIDriver *spip)
Deactivates the SPI peripheral.
Definition: hal_spi.c:113
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:256
spistate_t state
Driver state.
Definition: hal_spi_lld.h:132