diff --git a/os/hal/include/hal_spi_v2.h b/os/hal/include/hal_spi_v2.h new file mode 100644 index 000000000..6754e440a --- /dev/null +++ b/os/hal/include/hal_spi_v2.h @@ -0,0 +1,482 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_spi.h + * @brief SPI (v2) Driver macros and structures. + * + * @addtogroup SPI_V2 + * @{ + */ + +#ifndef HAL_SPI_V2_H +#define HAL_SPI_V2_H + +#if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Chip Select modes + * @{ + */ +#define SPI_SELECT_MODE_NONE 0 /** @brief @p spiSelect() and + @p spiUnselect() do + nothing. */ +#define SPI_SELECT_MODE_PAD 1 /** @brief Legacy mode. */ +#define SPI_SELECT_MODE_PORT 2 /** @brief Fastest mode. */ +#define SPI_SELECT_MODE_LINE 3 /** @brief Packed mode. */ +#define SPI_SELECT_MODE_LLD 4 /** @brief LLD-defined mode.*/ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name SPI configuration options + * @{ + */ +/** + * @brief Support for thread synchronization API. + */ +#if !defined(SPI_USE_SYNCHRONIZATION) || defined(__DOXYGEN__) +#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__) +#define SPI_USE_SYNCHRONIZATION FALSE +#else +#define SPI_USE_SYNCHRONIZATION SPI_USE_WAIT +#endif +#endif + +/** + * @brief Enables circular transfers APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_CIRCULAR) || defined(__DOXYGEN__) +#define SPI_USE_CIRCULAR FALSE +#endif + +/** + * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define SPI_USE_MUTUAL_EXCLUSION TRUE +#endif + +/** + * @brief Handling method for SPI CS line. + * @note Disabling this option saves both code and data space. + */ +#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__) +#define SPI_SELECT_MODE SPI_SELECT_MODE_LINE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if (SPI_SELECT_MODE != SPI_SELECT_MODE_NONE) && \ + (SPI_SELECT_MODE != SPI_SELECT_MODE_PAD) && \ + (SPI_SELECT_MODE != SPI_SELECT_MODE_PORT) && \ + (SPI_SELECT_MODE != SPI_SELECT_MODE_LINE) && \ + (SPI_SELECT_MODE != SPI_SELECT_MODE_LLD) +#error "invalid SPI_SELECT_MODE setting" +#endif + +/* Some modes have a dependency on the PAL driver, making the required + checks here.*/ +#if ((SPI_SELECT_MODE != SPI_SELECT_MODE_PAD) || \ + (SPI_SELECT_MODE != SPI_SELECT_MODE_PORT) || \ + (SPI_SELECT_MODE != SPI_SELECT_MODE_LINE)) && \ + (HAL_USE_PAL != TRUE) +#error "current SPI_SELECT_MODE requires HAL_USE_PAL" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Driver state machine possible states. + */ +typedef enum { + SPI_UNINIT = 0, /**< Not initialized. */ + SPI_STOP = 1, /**< Stopped. */ + SPI_READY = 2, /**< Ready. */ + SPI_ACTIVE = 3, /**< Exchanging data. */ + SPI_COMPLETE = 4 /**< Asynchronous operation complete. */ +} spistate_t; + +/** + * @brief Type of a structure representing an SPI driver. + */ +typedef struct hal_spi_driver SPIDriver; + +/** + * @brief Type of a SPI driver configuration structure. + */ +typedef struct hal_spi_config SPIConfig; + +/** + * @brief SPI notification callback type. + * + * @param[in] spip pointer to the @p SPIDriver object triggering the + * callback + */ +typedef void (*spicb_t)(SPIDriver *spip); + +/* Including the low level driver header, it exports information required + for completing types.*/ +#include "hal_spi_v2_lld.h" + +#if !defined(SPI_SUPPORTS_CIRCULAR) +#error "SPI_SUPPORTS_CIRCULAR not defined in LLD" +#endif + +#if !defined(SPI_SUPPORTS_SLAVE_MODE) +#error "SPI_SUPPORTS_SLAVE_MODE not defined in LLD" +#endif + +/** + * @brief Driver configuration structure. + */ +struct hal_spi_config { +#if (SPI_SUPPORTS_CIRCULAR == TRUE) || defined(__DOXYGEN__) + /** + * @brief Enables the circular buffer mode. + */ + bool circular; +#endif +#if (SPI_SUPPORTS_SLAVE_MODE == TRUE) || defined(__DOXYGEN__) + /** + * @brief Enables the circular buffer mode. + */ + bool slave; +#endif + /** + * @brief Operation complete callback or @p NULL. + */ + spicallback_t end_cb; + /** + * @brief Operation error callback or @p NULL. + */ + spicallback_t error_cb; +#if (SPI_SELECT_MODE == SPI_SELECT_MODE_LINE) || defined(__DOXYGEN__) + /** + * @brief The chip select line. + * @note Only used in master mode. + */ + ioline_t ssline; +#elif SPI_SELECT_MODE == SPI_SELECT_MODE_PORT + /** + * @brief The chip select port. + * @note Only used in master mode. + */ + ioportid_t ssport; + /** + * @brief The chip select port mask. + * @note Only used in master mode. + */ + ioportmask_t ssmask; +#elif SPI_SELECT_MODE == SPI_SELECT_MODE_PAD + /** + * @brief The chip select port. + * @note Only used in master mode. + */ + ioportid_t ssport; + /** + * @brief The chip select pad number. + * @note Only used in master mode. + */ + uint_fast8_t sspad; +#endif + /* End of the mandatory fields.*/ + spi_lld_config_fields; +}; + +/** + * @brief Structure representing an SPI driver. + */ +struct hal_spi_driver { + /** + * @brief Driver state. + */ + spistate_t state; + /** + * @brief Current configuration data. + */ + const SPIConfig *config; +#if (SPI_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Synchronization point for transfer. + */ + thread_reference_t sync_transfer; +#endif /* SPI_USE_SYNCHRONIZATION == TRUE */ +#if (SPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* SPI_USE_MUTUAL_EXCLUSION == TRUE */ +#if defined(SPI_DRIVER_EXT_FIELDS) + SPI_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + spi_lld_driver_fields; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @name Macro Functions + * @{ + */ +/** + * @brief Buffer state. + * @note This function is meant to be called from the SPI callback only. + * + * @param[in] spip pointer to the @p SPIDriver object + * @return The buffer state. + * @retval false if the driver filled/sent the first half of the + * buffer. + * @retval true if the driver filled/sent the second half of the + * buffer. + * + * @special + */ +#define spiIsBufferComplete(spip) ((bool)((spip)->state == SPI_COMPLETE)) + +#if (SPI_SELECT_MODE == SPI_SELECT_MODE_LLD) || defined(__DOXYGEN__) +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @iclass + */ +#define spiSelectI(spip) \ +do { \ + spi_lld_select(spip); \ +} while (false) + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @iclass + */ +#define spiUnselectI(spip) \ +do { \ + spi_lld_unselect(spip); \ +} while (false) + +#elif SPI_SELECT_MODE == SPI_SELECT_MODE_LINE +#define spiSelectI(spip) \ +do { \ + palClearLine((spip)->config->ssline); \ +} while (false) + +#define spiUnselectI(spip) \ +do { \ + palSetLine((spip)->config->ssline); \ +} while (false) + +#elif SPI_SELECT_MODE == SPI_SELECT_MODE_PORT +#define spiSelectI(spip) \ +do { \ + palClearPort((spip)->config->ssport, (spip)->config->ssmask); \ +} while (false) + +#define spiUnselectI(spip) \ +do { \ + palSetPort((spip)->config->ssport, (spip)->config->ssmask); \ +} while (false) + +#elif SPI_SELECT_MODE == SPI_SELECT_MODE_PAD +#define spiSelectI(spip) \ +do { \ + palClearPad((spip)->config->ssport, (spip)->config->sspad); \ +} while (false) + +#define spiUnselectI(spip) \ +do { \ + palSetPad((spip)->config->ssport, (spip)->config->sspad); \ +} while (false) + +#elif SPI_SELECT_MODE == SPI_SELECT_MODE_NONE +#define spiSelectI(spip) + +#define spiUnselectI(spip) +#endif + +/** + * @brief Exchanges one frame using a polled wait. + * @details This synchronous function exchanges one frame using a polled + * synchronization method. This function is useful when exchanging + * small amount of data on high speed channels, usually in this + * situation is much more efficient just wait for completion using + * polling than suspending the thread waiting for an interrupt. + * @note This API is implemented as a macro in order to minimize latency. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] frame the data frame to send over the SPI bus + * @return The received data frame from the SPI bus. + */ +#define spiPolledExchange(spip, frame) spi_lld_polled_exchange(spip, frame) +/** @} */ + +/** + * @name Low level driver helper macros + * @{ + */ +#if (SPI_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) +/** + * @brief Wakes up the waiting thread. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +#define _spi_wakeup_isr(spip) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(spip)->thread, MSG_OK); \ + osalSysUnlockFromISR(); \ +} +#else /* !SPI_USE_SYNCHRONIZATION */ +#define _spi_wakeup_isr(spip) +#endif /* !SPI_USE_SYNCHRONIZATION */ + +/** + * @brief Common ISR code when circular mode is not supported. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * - Waiting thread wakeup, if any. + * - Driver state transitions. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +#define _spi_isr_code(spip) { \ + if ((spip)->config->end_cb) { \ + (spip)->state = SPI_COMPLETE; \ + (spip)->config->end_cb(spip); \ + if ((spip)->state == SPI_COMPLETE) \ + (spip)->state = SPI_READY; \ + } \ + else \ + (spip)->state = SPI_READY; \ + _spi_wakeup_isr(spip); \ +} + +/** + * @brief Half buffer filled ISR code in circular mode. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +#define _spi_isr_half_code(spip) { \ + if ((spip)->config->end_cb) { \ + (spip)->config->end_cb(spip); \ + } \ +} + +/** + * @brief Full buffer filled ISR code in circular mode. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * - Driver state transitions. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +#define _spi_isr_full_code(spip) { \ + if ((spip)->config->end_cb) { \ + (spip)->state = SPI_COMPLETE; \ + (spip)->config->end_cb(spip); \ + if ((spip)->state == SPI_COMPLETE) \ + (spip)->state = SPI_ACTIVE; \ + } \ +} +/** @} */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void spiInit(void); + void spiObjectInit(SPIDriver *spip); + msg_t spiStart(SPIDriver *spip, const SPIConfig *config); + void spiStop(SPIDriver *spip); + void spiSelect(SPIDriver *spip); + void spiUnselect(SPIDriver *spip); + bool spiStartIgnoreI(SPIDriver *spip, size_t n); + bool spiStartIgnore(SPIDriver *spip, size_t n); + bool spiStartExchangeI(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + bool spiStartExchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + bool spiStartSendI(SPIDriver *spip, size_t n, const void *txbuf); + bool spiStartSend(SPIDriver *spip, size_t n, const void *txbuf); + bool spiStartReceiveI(SPIDriver *spip, size_t n, void *rxbuf); + bool spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf); + size_t spiStopTransferI(SPIDriver *spip); + size_t spiStopTransfer(SPIDriver *spip); +#if SPI_USE_SYNCHRONIZATION == TRUE + msg_t spiSynchronizeS(SPIDriver *spip, sysinterval_t timeout); + msg_t spiSynchronize(SPIDriver *spip, sysinterval_t timeout); + void spiIgnore(SPIDriver *spip, size_t n); + void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf); + void spiSend(SPIDriver *spip, size_t n, const void *txbuf); + void spiReceive(SPIDriver *spip, size_t n, void *rxbuf); +#endif +#if SPI_USE_MUTUAL_EXCLUSION == TRUE + void spiAcquireBus(SPIDriver *spip); + void spiReleaseBus(SPIDriver *spip); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SPI == TRUE */ + +#endif /* HAL_SPI_V2_H */ + +/** @} */ diff --git a/os/hal/src/hal_sio.c b/os/hal/src/hal_sio.c index 8f0751c50..0736d5ace 100644 --- a/os/hal/src/hal_sio.c +++ b/os/hal/src/hal_sio.c @@ -447,6 +447,8 @@ size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n) { * @retval MSG_RESET operation has been stopped while waiting. * @retval SIO_MSG_IDLE if RX line went idle. * @retval SIO_MSG_ERRORS if RX errors occurred during wait. + * + * @api */ msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) { msg_t msg = MSG_OK; @@ -482,6 +484,8 @@ msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) { * @retval MSG_OK if there is space in the TX FIFO. * @retval MSG_TIMEOUT if synchronization timed out. * @retval MSG_RESET operation has been stopped while waiting. + * + * @api */ msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) { msg_t msg = MSG_OK; @@ -514,6 +518,8 @@ msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) { * @return The synchronization result. * @retval MSG_OK if TX operation finished. * @retval MSG_TIMEOUT if synchronization timed out. + * + * @api */ msg_t sioSynchronizeTXEnd(SIODriver *siop, sysinterval_t timeout) { msg_t msg; diff --git a/os/hal/src/hal_spi_v2.c b/os/hal/src/hal_spi_v2.c new file mode 100644 index 000000000..b26f5539e --- /dev/null +++ b/os/hal/src/hal_spi_v2.c @@ -0,0 +1,598 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_spi_v2.c + * @brief SPI (v2) Driver code. + * + * @addtogroup SPI_V2 + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief SPI Driver initialization. + * @note This function is implicitly invoked by @p halInit(), there is + * no need to explicitly initialize the driver. + * + * @init + */ +void spiInit(void) { + + spi_lld_init(); +} + +/** + * @brief Initializes the standard part of a @p SPIDriver structure. + * + * @param[out] spip pointer to the @p SPIDriver object + * + * @init + */ +void spiObjectInit(SPIDriver *spip) { + + spip->state = SPI_STOP; + spip->config = NULL; +#if SPI_USE_SYNCHRONIZATION == TRUE + spip->synch_transfer = NULL; +#endif +#if SPI_USE_MUTUAL_EXCLUSION == TRUE + osalMutexObjectInit(&spip->mutex); +#endif +#if defined(SPI_DRIVER_EXT_INIT_HOOK) + SPI_DRIVER_EXT_INIT_HOOK(spip); +#endif +} + +/** + * @brief Configures and activates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] config pointer to the @p SPIConfig object + * @return The operation status. + * + * @api + */ +msg_t spiStart(SPIDriver *spip, const SPIConfig *config) { + msg_t msg; + + osalDbgCheck((spip != NULL) && (config != NULL)); + + osalSysLock(); + osalDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY), + "invalid state"); + + spip->config = config; + +#if defined(SPI_LLD_ENHANCED_API) + msg = spi_lld_start(spip); +#else + spi_lld_start(spip); + msg = HAL_START_SUCCESS; +#endif + if (msg == HAL_START_SUCCESS) { + spip->state = SPI_READY; + } + else { + spip->state = SPI_STOP; + } + + osalSysUnlock(); + + return msg; +} + +/** + * @brief Deactivates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +void spiStop(SPIDriver *spip) { + + osalDbgCheck(spip != NULL); + + osalSysLock(); + + osalDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY), + "invalid state"); + + spi_lld_stop(spip); + spip->config = NULL; + spip->state = SPI_STOP; + + osalSysUnlock(); +} + +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +void spiSelect(SPIDriver *spip) { + + osalDbgCheck(spip != NULL); + + osalSysLock(); + osalDbgAssert(spip->state == SPI_READY, "not ready"); + spiSelectI(spip); + osalSysUnlock(); +} + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +void spiUnselect(SPIDriver *spip) { + + osalDbgCheck(spip != NULL); + + osalSysLock(); + osalDbgAssert(spip->state == SPI_READY, "not ready"); + spiUnselectI(spip); + osalSysUnlock(); +} + +/** + * @brief Ignores data on the SPI bus. + * @details This asynchronous function starts the transmission of a series of + * idle words on the SPI bus and ignores the received data. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @iclass + */ +bool spiStartIgnoreI(SPIDriver *spip, size_t n) { + + osalDbgCheck((spip != NULL) && (n > 0U)); +#if SPI_SUPPORTS_CIRCULAR + osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U)); +#endif + + osalDbgAssert(spip->state == SPI_READY, "not ready"); + + spip->state = SPI_ACTIVE; + return spi_lld_ignore(spip, n); +} + +/** + * @brief Ignores data on the SPI bus. + * @details This asynchronous function starts the transmission of a series of + * idle words on the SPI bus and ignores the received data. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @api + */ +bool spiStartIgnore(SPIDriver *spip, size_t n) { + bool ret; + + osalSysLock(); + ret = spiStartIgnoreI(spip, n); + osalSysUnlock(); + + return ret; +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This asynchronous function starts a simultaneous transmit/receive + * operation. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @iclass + */ +bool spiStartExchangeI(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + + osalDbgCheck((spip != NULL) && (n > 0U) && + (rxbuf != NULL) && (txbuf != NULL)); +#if SPI_SUPPORTS_CIRCULAR + osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U)); +#endif + + osalDbgAssert(spip->state == SPI_READY, "not ready"); + + spip->state = SPI_ACTIVE; + return spi_lld_exchange(spip, n, txbuf, rxbuf); +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This asynchronous function starts a simultaneous transmit/receive + * operation. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @api + */ +bool spiStartExchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + bool ret; + + osalSysLock(); + ret = spiStartExchangeI(spip, n, txbuf, rxbuf); + osalSysUnlock(); + + return ret; +} + +/** + * @brief Sends data over the SPI bus. + * @details This asynchronous function starts a transmit operation. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @iclass + */ +bool spiStartSendI(spip, n, txbuf) { + + osalDbgCheck((spip != NULL) && (n > 0U) && (txbuf != NULL)); +#if SPI_SUPPORTS_CIRCULAR + osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U)); +#endif + + osalDbgAssert(spip->state == SPI_READY, "not ready"); + + spip->state = SPI_ACTIVE; + return spi_lld_send(spip, n, txbuf); +} + +/** + * @brief Sends data over the SPI bus. + * @details This asynchronous function starts a transmit operation. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @api + */ +bool spiStartSend(SPIDriver *spip, size_t n, const void *txbuf) { + bool ret; + + osalSysLock(); + ret = spiStartSendI(spip, n, txbuf); + osalSysUnlock(); + + return ret; +} + +/** + * @brief Receives data from the SPI bus. + * @details This asynchronous function starts a receive operation. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @iclass + */ +bool spiStartReceiveI(SPIDriver *spip, size_t n, void *rxbuf) { + + osalDbgCheck((spip != NULL) && (n > 0U) && (rxbuf != NULL)); +#if SPI_SUPPORTS_CIRCULAR + osalDbgCheck((spip->config->circular == false) || ((n & 1U) == 0U)); +#endif + + osalDbgAssert(spip->state == SPI_READY, "not ready"); + + spip->state = SPI_ACTIVE; + return spi_lld_receive(spip, n, rxbuf); +} + +/** + * @brief Receives data from the SPI bus. + * @details This asynchronous function starts a receive operation. + * @pre A slave must have been selected using @p spiSelect() or + * @p spiSelectI(). + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * @return The operation status. + * @retval false if the operation started correctly. + * @retval true if the operation did not start. + * + * @api + */ +bool spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf) { + bool ret; + + osalSysLock(); + ret = spiStartReceiveI(spip, n, rxbuf); + osalSysUnlock(); + + return ret; +} + +#if (SPI_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) +/** + * @brief Synchronizes with current transfer completion. + * @note This function can only be called by a single thread at time. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] timeout synchronization timeout + * @return The synchronization result. + * @retval MSG_OK if TX operation finished. + * @retval MSG_TIMEOUT if synchronization timed out. + * + * @sclass + */ +msg_t spiSynchronizeS(SPIDriver *spip, sysinterval_t timeout) { + msg_t msg; + + osalDbgCheck((spip != NULL) && (n > 0U)); + osalDbgAssert((spip->state == SPI_ACTIVE) || (spip->state == SPI_READY), + "invalid state"); + + if (spip->state == SPI_ACTIVE) { + msg = osalThreadSuspendTimeoutS(&spip->sync_transfer, timeout); + } + else { + msg = MSG_OK; + } + + return msg; +} + +/** + * @brief Synchronizes with current transfer completion. + * @note This function can only be called by a single thread at time. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] timeout synchronization timeout + * @return The synchronization result. + * @retval MSG_OK if TX operation finished. + * @retval MSG_TIMEOUT if synchronization timed out. + * + * @api + */ +msg_t spiSynchronize(SPIDriver *spip, sysinterval_t timeout) { + msg_t msg; + + osalSysLock(); + msg = spiSynchronizeS(spip, timeout); + osalSysUnlock(); + + return msg; +} + +/** + * @brief Ignores data on the SPI bus. + * @details This synchronous function performs the transmission of a series of + * idle words on the SPI bus and ignores the received data. + * @pre In order to use this function the option @p SPI_USE_SYNCHRONIZATION + * must be enabled. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * + * @api + */ +void spiIgnore(SPIDriver *spip, size_t n) { + + osalSysLock(); + spiStartIgnoreI(spip, n); + (void) spiSynchronizeS(spip, TIME_INFINITE); + osalSysUnlock(); +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This synchronous function performs a simultaneous transmit/receive + * operation. + * @pre In order to use this function the option @p SPI_USE_SYNCHRONIZATION + * must be enabled. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * + * @api + */ +void spiExchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + + osalSysLock(); + spiStartExchangeI(spip, n, txbuf, rxbuf); + (void) spiSynchronizeS(spip, TIME_INFINITE); + osalSysUnlock(); +} + +/** + * @brief Sends data over the SPI bus. + * @details This synchronous function performs a transmit operation. + * @pre In order to use this function the option @p SPI_USE_SYNCHRONIZATION + * must be enabled. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @api + */ +void spiSend(SPIDriver *spip, size_t n, const void *txbuf) { + + osalSysLock(); + spiStartSendI(spip, n, txbuf); + (void) spiSynchronizeS(spip, TIME_INFINITE); + osalSysUnlock(); +} + +/** + * @brief Receives data from the SPI bus. + * @details This synchronous function performs a receive operation. + * @pre In order to use this function the option @p SPI_USE_SYNCHRONIZATION + * must be enabled. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * + * @api + */ +void spiReceive(SPIDriver *spip, size_t n, void *rxbuf) { + + osalSysLock(); + spiStartReceiveI(spip, n, rxbuf); + (void) spiSynchronizeS(spip, TIME_INFINITE); + osalSysUnlock(); +} +#endif /* SPI_USE_SYNCHRONIZATION == TRUE */ + +#if (SPI_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) +/** + * @brief Gains exclusive access to the SPI bus. + * @details This function tries to gain ownership to the SPI bus, if the bus + * is already being used then the invoking thread is queued. + * @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +void spiAcquireBus(SPIDriver *spip) { + + osalDbgCheck(spip != NULL); + + osalMutexLock(&spip->mutex); +} + +/** + * @brief Releases exclusive access to the SPI bus. + * @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +void spiReleaseBus(SPIDriver *spip) { + + osalDbgCheck(spip != NULL); + + osalMutexUnlock(&spip->mutex); +} +#endif /* SPI_USE_MUTUAL_EXCLUSION == TRUE */ + +#endif /* HAL_USE_SPI == TRUE */ + +/** @} */