Skip to content

Commit

Permalink
Merge pull request #2206 from fpistm/spi_transfer_buf
Browse files Browse the repository at this point in the history
feat(spi): add transfer api with tx and rx buffer
  • Loading branch information
fpistm committed Nov 28, 2023
2 parents cd3043f + 9df4fe0 commit 20339e8
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 67 deletions.
32 changes: 23 additions & 9 deletions libraries/SPI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand All @@ -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`.
28 changes: 21 additions & 7 deletions libraries/SPI/src/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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);
Expand All @@ -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.
*/
Expand Down
20 changes: 10 additions & 10 deletions libraries/SPI/src/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
80 changes: 41 additions & 39 deletions libraries/SPI/src/utility/spi_com.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
10 changes: 8 additions & 2 deletions libraries/SPI/src/utility/spi_com.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 20339e8

Please sign in to comment.