diff --git a/libraries/SPI/README.md b/libraries/SPI/README.md index 59564fd7e5..7a0595c1cc 100644 --- a/libraries/SPI/README.md +++ b/libraries/SPI/README.md @@ -7,16 +7,18 @@ User have 2 possibilities about the management of the CS pin: * the CS pin is managed directly by the user code before to transfer the data (like the Arduino SPI library) * the user uses a hardware CS pin linked to the SPI peripheral -### New API functions +## New API functions -* `SPIClass::SPIClass(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel)`: alternative class constructor -_Params_ SPI `mosi` pin -_Params_ SPI `miso` pin -_Params_ SPI `sclk` pin -_Params_ (optional) SPI `ssel` pin. This pin must be an hardware CS pin. If you configure this pin, the chip select will be managed by the SPI peripheral. +#### Alternative class constructor +* `SPIClass::SPIClass(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel)` + +_Param_ SPI `mosi` pin - * `SPI_HandleTypeDef *getHandle(void)`: Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). **Use at your own risk.** +_Param_ SPI `miso` pin +_Param_ SPI `sclk` pin + +_Params_ (optional) SPI `ssel` pin. This pin must be an hardware CS pin. If you configure this pin, the chip select will be managed by the SPI peripheral. ##### Example @@ -35,9 +37,15 @@ void setup() { } ``` -### Extended API +#### Transfer with Tx/Rx buffer + +* `void transfer(const void *tx_buf, void *rx_buf, size_t count)` :Transfer several bytes. One constant buffer used to send and one to receive data. -* All `transfer()` API's have a new bool argument `skipReceive`. It allows to skip receive data after transmitting. Value can be `SPI_TRANSMITRECEIVE` or `SPI_TRANSMITONLY`. Default `SPI_TRANSMITRECEIVE`. + _Param_ `tx_buf`: constant array of Tx bytes that is filled by the user before starting the SPI transfer. If NULL, default dummy 0xFF bytes will be clocked out. + + _Param_ `rx_buf`: array of Rx bytes that will be filled by the slave during the SPI transfer. If NULL, the received data will be discarded. + + _Param_ `count`: number of bytes to send/receive. #### Change default `SPI` instance pins It is also possible to change the default pins used by the `SPI` instance using above API: @@ -63,3 +71,9 @@ It is also possible to change the default pins used by the `SPI` instance using SPI.setMOSI(PC2); // using pin number PYn SPI.begin(2); ``` + +* `SPI_HandleTypeDef *getHandle(void)`: Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). **Use at your own risk.** + +## Extended API + +* All defaustatndard `transfer()` API's have a new bool argument `skipReceive`. It allows to skip receive data after transmitting. Value can be `SPI_TRANSMITRECEIVE` or `SPI_TRANSMITONLY`. Default `SPI_TRANSMITRECEIVE`. diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index f42f2e88d7..2c6e798b33 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -163,7 +163,7 @@ void SPIClass::setClockDivider(uint8_t divider) */ uint8_t SPIClass::transfer(uint8_t data, bool skipReceive) { - spi_transfer(&_spi, &data, sizeof(uint8_t), SPI_TRANSFER_TIMEOUT, skipReceive); + spi_transfer(&_spi, &data, (!skipReceive) ? &data : NULL, sizeof(uint8_t)); return data; } @@ -184,8 +184,7 @@ uint16_t SPIClass::transfer16(uint16_t data, bool skipReceive) tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8); data = tmp; } - spi_transfer(&_spi, (uint8_t *)&data, sizeof(uint16_t), - SPI_TRANSFER_TIMEOUT, skipReceive); + spi_transfer(&_spi, (uint8_t *)&data, (!skipReceive) ? (uint8_t *)&data : NULL, sizeof(uint16_t)); if (_spiSettings.bitOrder) { tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8); @@ -207,12 +206,27 @@ uint16_t SPIClass::transfer16(uint16_t data, bool skipReceive) */ void SPIClass::transfer(void *buf, size_t count, bool skipReceive) { - if ((count != 0) && (buf != NULL)) { - spi_transfer(&_spi, ((uint8_t *)buf), count, - SPI_TRANSFER_TIMEOUT, skipReceive); - } + spi_transfer(&_spi, (uint8_t *)buf, (!skipReceive) ? (uint8_t *)buf : NULL, count); + +} + +/** + * @brief Transfer several bytes. One constant buffer used to send and + * one to receive data. + * begin() or beginTransaction() must be called at least once before. + * @param tx_buf: array of Tx bytes that is filled by the user before starting + * the SPI transfer. If NULL, default dummy 0xFF bytes will be + * clocked out. + * @param rx_buf: array of Rx bytes that will be filled by the slave during + * the SPI transfer. If NULL, the received data will be discarded. + * @param count: number of bytes to send/receive. + */ +void SPIClass::transfer(const void *tx_buf, void *rx_buf, size_t count) +{ + spi_transfer(&_spi, ((const uint8_t *)tx_buf), ((uint8_t *)rx_buf), count); } + /** * @brief Not implemented. */ diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 1c8bbc11cd..1105991dc7 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -41,11 +41,6 @@ extern "C" { #define SPI_TRANSMITRECEIVE false #define SPI_TRANSMITONLY true -// Defines a default timeout delay in milliseconds for the SPI transfer -#ifndef SPI_TRANSFER_TIMEOUT - #define SPI_TRANSFER_TIMEOUT 1000 -#endif - class SPISettings { public: constexpr SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) @@ -127,21 +122,26 @@ class SPIClass { _spi.pin_ssel = (ssel); }; - virtual void begin(void); + void begin(void); void end(void); /* This function should be used to configure the SPI instance in case you * don't use default parameters. */ void beginTransaction(SPISettings settings); - virtual void endTransaction(void); + void endTransaction(void); /* Transfer functions: must be called after initialization of the SPI * instance with begin() or beginTransaction(). */ - virtual uint8_t transfer(uint8_t data, bool skipReceive = SPI_TRANSMITRECEIVE); - virtual uint16_t transfer16(uint16_t data, bool skipReceive = SPI_TRANSMITRECEIVE); - virtual void transfer(void *buf, size_t count, bool skipReceive = SPI_TRANSMITRECEIVE); + uint8_t transfer(uint8_t data, bool skipReceive = SPI_TRANSMITRECEIVE); + uint16_t transfer16(uint16_t data, bool skipReceive = SPI_TRANSMITRECEIVE); + void transfer(void *buf, size_t count, bool skipReceive = SPI_TRANSMITRECEIVE); + + /* Expand SPI API + * https://github.com/arduino/ArduinoCore-API/discussions/189 + */ + void transfer(const void *tx_buf, void *rx_buf, size_t count); /* These methods are deprecated and kept for compatibility. * Use SPISettings with SPI.beginTransaction() to configure SPI parameters. diff --git a/libraries/SPI/src/utility/spi_com.c b/libraries/SPI/src/utility/spi_com.c index bd582681bd..b92b44aea9 100644 --- a/libraries/SPI/src/utility/spi_com.c +++ b/libraries/SPI/src/utility/spi_com.c @@ -500,71 +500,73 @@ void spi_deinit(spi_t *obj) * @brief This function is implemented by user to send/receive data over * SPI interface * @param obj : pointer to spi_t structure - * @param buffer : tx data to send before reception + * @param tx_buffer : tx data to send before reception + * @param rx_buffer : rx data to receive if not numm * @param len : length in byte of the data to send and receive - * @param Timeout: Timeout duration in tick - * @param skipReceive: skip receiving data after transmit or not * @retval status of the send operation (0) in case of error */ -spi_status_e spi_transfer(spi_t *obj, uint8_t *buffer, uint16_t len, - uint32_t Timeout, bool skipReceive) +spi_status_e spi_transfer(spi_t *obj, const uint8_t *tx_buffer, uint8_t *rx_buffer, + uint16_t len) { spi_status_e ret = SPI_OK; uint32_t tickstart, size = len; SPI_TypeDef *_SPI = obj->handle.Instance; - uint8_t *tx_buffer = buffer; + uint8_t *tx_buf = (uint8_t *)tx_buffer; - if ((len == 0) || (Timeout == 0U)) { - return Timeout > 0U ? SPI_ERROR : SPI_TIMEOUT; - } - tickstart = HAL_GetTick(); + if (len == 0) { + ret = SPI_ERROR; + } else { + tickstart = HAL_GetTick(); #if defined(SPI_CR2_TSIZE) - /* Start transfer */ - LL_SPI_SetTransferSize(_SPI, size); - LL_SPI_Enable(_SPI); - LL_SPI_StartMasterTransfer(_SPI); + /* Start transfer */ + LL_SPI_SetTransferSize(_SPI, size); + LL_SPI_Enable(_SPI); + LL_SPI_StartMasterTransfer(_SPI); #endif - while (size--) { + while (size--) { #if defined(SPI_SR_TXP) - while (!LL_SPI_IsActiveFlag_TXP(_SPI)); + while (!LL_SPI_IsActiveFlag_TXP(_SPI)); #else - while (!LL_SPI_IsActiveFlag_TXE(_SPI)); + while (!LL_SPI_IsActiveFlag_TXE(_SPI)); #endif - LL_SPI_TransmitData8(_SPI, *tx_buffer++); + LL_SPI_TransmitData8(_SPI, tx_buf ? *tx_buf++ : 0XFF); - if (!skipReceive) { #if defined(SPI_SR_RXP) while (!LL_SPI_IsActiveFlag_RXP(_SPI)); #else while (!LL_SPI_IsActiveFlag_RXNE(_SPI)); #endif - *buffer++ = LL_SPI_ReceiveData8(_SPI); - } - if ((Timeout != HAL_MAX_DELAY) && (HAL_GetTick() - tickstart >= Timeout)) { - ret = SPI_TIMEOUT; - break; + if (rx_buffer) { + *rx_buffer++ = LL_SPI_ReceiveData8(_SPI); + } else { + LL_SPI_ReceiveData8(_SPI); + } + if ((SPI_TRANSFER_TIMEOUT != HAL_MAX_DELAY) && + (HAL_GetTick() - tickstart >= SPI_TRANSFER_TIMEOUT)) { + ret = SPI_TIMEOUT; + break; + } } - } #if defined(SPI_IFCR_EOTC) - // Add a delay before disabling SPI otherwise last-bit/last-clock may be truncated - // See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294 - // Computed delay is half SPI clock - delayMicroseconds(obj->disable_delay); - - /* Close transfer */ - /* Clear flags */ - LL_SPI_ClearFlag_EOT(_SPI); - LL_SPI_ClearFlag_TXTF(_SPI); - /* Disable SPI peripheral */ - LL_SPI_Disable(_SPI); + // Add a delay before disabling SPI otherwise last-bit/last-clock may be truncated + // See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294 + // Computed delay is half SPI clock + delayMicroseconds(obj->disable_delay); + + /* Close transfer */ + /* Clear flags */ + LL_SPI_ClearFlag_EOT(_SPI); + LL_SPI_ClearFlag_TXTF(_SPI); + /* Disable SPI peripheral */ + LL_SPI_Disable(_SPI); #else - /* Wait for end of transfer */ - while (LL_SPI_IsActiveFlag_BSY(_SPI)); + /* Wait for end of transfer */ + while (LL_SPI_IsActiveFlag_BSY(_SPI)); #endif - + } return ret; } diff --git a/libraries/SPI/src/utility/spi_com.h b/libraries/SPI/src/utility/spi_com.h index 7562c08118..c6e287b8d5 100644 --- a/libraries/SPI/src/utility/spi_com.h +++ b/libraries/SPI/src/utility/spi_com.h @@ -78,6 +78,13 @@ typedef struct spi_s spi_t; #define SPI_SPEED_CLOCK_DIV128_MHZ ((uint32_t)128) #define SPI_SPEED_CLOCK_DIV256_MHZ ((uint32_t)256) +// Defines a default timeout delay in milliseconds for the SPI transfer +#ifndef SPI_TRANSFER_TIMEOUT +#define SPI_TRANSFER_TIMEOUT 1000 +#elif SPI_TRANSFER_TIMEOUT <= 0 +#error "SPI_TRANSFER_TIMEOUT cannot be less or equal to 0!" +#endif + ///@brief specifies the SPI mode to use //Mode Clock Polarity (CPOL) Clock Phase (CPHA) //SPI_MODE0 0 0 @@ -103,8 +110,7 @@ typedef enum { /* Exported functions ------------------------------------------------------- */ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb); void spi_deinit(spi_t *obj); -spi_status_e spi_transfer(spi_t *obj, uint8_t *buffer, uint16_t len, - uint32_t Timeout, bool skipReceive); +spi_status_e spi_transfer(spi_t *obj, const uint8_t *tx_buffer, uint8_t *rx_buffer, uint16_t len); uint32_t spi_getClkFreq(spi_t *obj); #ifdef __cplusplus