diff --git a/os/hal/include/spi.h b/os/hal/include/spi.h index 7ff92a04d..b09e33089 100644 --- a/os/hal/include/spi.h +++ b/os/hal/include/spi.h @@ -64,7 +64,9 @@ typedef enum { SPI_UNINIT = 0, /**< @brief Not initialized. */ SPI_STOP = 1, /**< @brief Stopped. */ SPI_READY = 2, /**< @brief Ready. */ - SPI_ACTIVE = 3 /**< @brief Slave selected. */ + SPI_SYNC= 3, /**< @brief Synchronizing. */ + SPI_SELECTED = 4, /**< @brief Slave selected. */ + SPI_ACTIVE = 5 /**< @brief Exchanging data. */ } spistate_t; #include "spi_lld.h" @@ -73,6 +75,131 @@ typedef enum { /* Driver macros. */ /*===========================================================================*/ +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +#define spiSelectI(spip) { \ + (spip)->spd_state = SPI_SELECTED; \ + spi_lld_select(spip); \ +} + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @api + */ +#define spiUnselectI(spip) { \ + (spip)->spd_state = SPI_READY; \ + spi_lld_unselect(spip); \ +} + +/** + * @brief Emits a train of clock pulses on the SPI bus. + * @details This asynchronous function starts the emission of a train of + * clock pulses without asserting any slave, while this is not + * usually required by the SPI protocol it is required by + * initialization procedure of MMC/SD cards in SPI mode. + * @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 clocked. The number of pulses + * is equal to the number of words multiplied to the + * configured word size in bits. + * + * @api + */ +#define spiSynchronizeI(spip, n) { \ + (spip)->spd_state = SPI_SYNC; \ + 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 + * + * @api + */ +#define spiIgnoreI(spip, n) { \ + (spip)->spd_state = SPI_ACTIVE; \ + spi_lld_ignore(spip, n); \ +} + +/** + * @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 + * + * @api + */ +#define spiExchangeI(spip, n, txbuf, rxbuf) { \ + (spip)->spd_state = SPI_ACTIVE; \ + spi_lld_exchange(spip, n, txbuf, rxbuf); \ +} + +/** + * @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 + * + * @api + */ +#define spiSendI(spip, n, txbuf) { \ + (spip)->spd_state = SPI_ACTIVE; \ + spi_lld_send(spip, n, txbuf); \ +} + +/** + * @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 + * + * @api + */ +#define spiReceiveI(spip, n, rxbuf) { \ + (spip)->spd_state = SPI_ACTIVE; \ + spi_lld_receive(spip, n, rxbuf); \ +} + /*===========================================================================*/ /* External declarations. */ /*===========================================================================*/ @@ -86,6 +213,7 @@ extern "C" { void spiStop(SPIDriver *spip); void spiSelect(SPIDriver *spip); void spiUnselect(SPIDriver *spip); + void spiSynchronize(SPIDriver *spip, size_t n); 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); diff --git a/os/hal/src/spi.c b/os/hal/src/spi.c index 85cb941e6..cd5857aa9 100644 --- a/os/hal/src/spi.c +++ b/os/hal/src/spi.c @@ -129,11 +129,10 @@ void spiSelect(SPIDriver *spip) { chSysLock(); chDbgAssert((spip->spd_state == SPI_READY) || - (spip->spd_state == SPI_ACTIVE), + (spip->spd_state == SPI_SELECTED), "spiSelect(), #1", "not idle"); - spi_lld_select(spip); - spip->spd_state = SPI_ACTIVE; + spiSelectI(spip); chSysUnlock(); } @@ -151,19 +150,48 @@ void spiUnselect(SPIDriver *spip) { chSysLock(); chDbgAssert((spip->spd_state == SPI_READY) || - (spip->spd_state == SPI_ACTIVE), + (spip->spd_state == SPI_SELECTED), "spiUnselect(), #1", "not locked"); - spi_lld_unselect(spip); - spip->spd_state = SPI_READY; + spiUnselectI(spip); + chSysUnlock(); +} + +/** + * @brief Emits a train of clock pulses on the SPI bus. + * @details This asynchronous function starts the emission of a train of + * clock pulses without asserting any slave, while this is not + * usually required by the SPI protocol it is required by + * initialization procedure of MMC/SD cards in SPI mode. + * @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 clocked. The number of pulses + * is equal to the number of words multiplied to the + * configured word size in bits. + * + * @api + */ +void spiSynchronize(SPIDriver *spip, size_t n) { + + chDbgCheck((spip != NULL) && (n > 0), "spiIgnore"); + + chSysLock(); + chDbgAssert(spip->spd_state == SPI_READY, + "spiSynchronize(), #1", + "not ready"); + + spiSynchronizeI(spip, n); chSysUnlock(); } /** * @brief Ignores data on the SPI bus. - * @details This function transmits a series of idle words on the SPI bus and - * ignores the received data. This function can be invoked even - * when a slave select signal has not been yet asserted. + * @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 @@ -173,16 +201,22 @@ void spiUnselect(SPIDriver *spip) { void spiIgnore(SPIDriver *spip, size_t n) { chDbgCheck((spip != NULL) && (n > 0), "spiIgnore"); - chDbgAssert((spip->spd_state == SPI_READY) || (spip->spd_state == SPI_ACTIVE), - "spiIgnore(), #1", - "not active"); - spi_lld_ignore(spip, n); + chSysLock(); + chDbgAssert(spip->spd_state == SPI_SELECTED, + "spiIgnore(), #1", + "not selected"); + spiIgnoreI(spip, n); + chSysUnlock(); } /** * @brief Exchanges data on the SPI bus. - * @details This function performs a simultaneous transmit/receive operation. + * @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. * @@ -197,15 +231,21 @@ void spiExchange(SPIDriver *spip, size_t n, const void *txbuf, void *rxbuf) { chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL), "spiExchange"); - chDbgAssert(spip->spd_state == SPI_ACTIVE, + + chSysLock(); + chDbgAssert(spip->spd_state == SPI_SELECTED, "spiExchange(), #1", "not active"); - - spi_lld_exchange(spip, n, txbuf, rxbuf); + spiExchangeI(spip, n, txbuf, rxbuf); + chSysUnlock(); } /** - * @brief Sends data ever the SPI bus. + * @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. * @@ -219,15 +259,21 @@ void spiSend(SPIDriver *spip, size_t n, const void *txbuf) { chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL), "spiSend"); - chDbgAssert(spip->spd_state == SPI_ACTIVE, + + chSysLock(); + chDbgAssert(spip->spd_state == SPI_SELECTED, "spiSend(), #1", "not active"); - - spi_lld_send(spip, n, txbuf); + spiSendI(spip, n, txbuf); + chSysUnlock(); } /** * @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. * @@ -241,11 +287,13 @@ void spiReceive(SPIDriver *spip, size_t n, void *rxbuf) { chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL), "spiReceive"); - chDbgAssert(spip->spd_state == SPI_ACTIVE, + + chSysLock(); + chDbgAssert(spip->spd_state == SPI_SELECTED, "spiReceive(), #1", "not active"); - - spi_lld_receive(spip, n, rxbuf); + spiReceiveI(spip, n, rxbuf); + chSysUnlock(); } #if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) diff --git a/os/hal/templates/spi_lld.h b/os/hal/templates/spi_lld.h index 67ca25b02..152784701 100644 --- a/os/hal/templates/spi_lld.h +++ b/os/hal/templates/spi_lld.h @@ -46,13 +46,25 @@ /* Driver data structures and types. */ /*===========================================================================*/ + +/** + * @brief SPI notification callback type. + * + * @param[in] spip pointer to the @p SPIDriver object triggering the + * callback + */ +typedef void (*spicallback_t)(SPIDriver *spip); + /** * @brief Driver configuration structure. * @note Implementations may extend this structure to contain more, * architecture dependent, fields. */ typedef struct { - + /** + * @brief Operation complete callback. + */ + spicallback_t spc_endcb; } SPIConfig; /**