Skip to content

Commit 20339e8

Browse files
authored
Merge pull request stm32duino#2206 from fpistm/spi_transfer_buf
feat(spi): add transfer api with tx and rx buffer
2 parents cd3043f + 9df4fe0 commit 20339e8

File tree

5 files changed

+103
-67
lines changed

5 files changed

+103
-67
lines changed

libraries/SPI/README.md

+23-9
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ User have 2 possibilities about the management of the CS pin:
77
* the CS pin is managed directly by the user code before to transfer the data (like the Arduino SPI library)
88
* the user uses a hardware CS pin linked to the SPI peripheral
99

10-
### New API functions
10+
## New API functions
1111

12-
* `SPIClass::SPIClass(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel)`: alternative class constructor
13-
_Params_ SPI `mosi` pin
14-
_Params_ SPI `miso` pin
15-
_Params_ SPI `sclk` pin
16-
_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.
12+
#### Alternative class constructor
13+
* `SPIClass::SPIClass(uint8_t mosi, uint8_t miso, uint8_t sclk, uint8_t ssel)`
14+
15+
_Param_ SPI `mosi` pin
1716

18-
* `SPI_HandleTypeDef *getHandle(void)`: Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). **Use at your own risk.**
17+
_Param_ SPI `miso` pin
1918

19+
_Param_ SPI `sclk` pin
20+
21+
_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.
2022

2123
##### Example
2224

@@ -35,9 +37,15 @@ void setup() {
3537
}
3638
```
3739
38-
### Extended API
40+
#### Transfer with Tx/Rx buffer
41+
42+
* `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.
3943
40-
* 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`.
44+
_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.
45+
46+
_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.
47+
48+
_Param_ `count`: number of bytes to send/receive.
4149
4250
#### Change default `SPI` instance pins
4351
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
6371
SPI.setMOSI(PC2); // using pin number PYn
6472
SPI.begin(2);
6573
```
74+
75+
* `SPI_HandleTypeDef *getHandle(void)`: Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). **Use at your own risk.**
76+
77+
## Extended API
78+
79+
* 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`.

libraries/SPI/src/SPI.cpp

+21-7
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ void SPIClass::setClockDivider(uint8_t divider)
163163
*/
164164
uint8_t SPIClass::transfer(uint8_t data, bool skipReceive)
165165
{
166-
spi_transfer(&_spi, &data, sizeof(uint8_t), SPI_TRANSFER_TIMEOUT, skipReceive);
166+
spi_transfer(&_spi, &data, (!skipReceive) ? &data : NULL, sizeof(uint8_t));
167167
return data;
168168
}
169169

@@ -184,8 +184,7 @@ uint16_t SPIClass::transfer16(uint16_t data, bool skipReceive)
184184
tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8);
185185
data = tmp;
186186
}
187-
spi_transfer(&_spi, (uint8_t *)&data, sizeof(uint16_t),
188-
SPI_TRANSFER_TIMEOUT, skipReceive);
187+
spi_transfer(&_spi, (uint8_t *)&data, (!skipReceive) ? (uint8_t *)&data : NULL, sizeof(uint16_t));
189188

190189
if (_spiSettings.bitOrder) {
191190
tmp = ((data & 0xff00) >> 8) | ((data & 0xff) << 8);
@@ -207,12 +206,27 @@ uint16_t SPIClass::transfer16(uint16_t data, bool skipReceive)
207206
*/
208207
void SPIClass::transfer(void *buf, size_t count, bool skipReceive)
209208
{
210-
if ((count != 0) && (buf != NULL)) {
211-
spi_transfer(&_spi, ((uint8_t *)buf), count,
212-
SPI_TRANSFER_TIMEOUT, skipReceive);
213-
}
209+
spi_transfer(&_spi, (uint8_t *)buf, (!skipReceive) ? (uint8_t *)buf : NULL, count);
210+
211+
}
212+
213+
/**
214+
* @brief Transfer several bytes. One constant buffer used to send and
215+
* one to receive data.
216+
* begin() or beginTransaction() must be called at least once before.
217+
* @param tx_buf: array of Tx bytes that is filled by the user before starting
218+
* the SPI transfer. If NULL, default dummy 0xFF bytes will be
219+
* clocked out.
220+
* @param rx_buf: array of Rx bytes that will be filled by the slave during
221+
* the SPI transfer. If NULL, the received data will be discarded.
222+
* @param count: number of bytes to send/receive.
223+
*/
224+
void SPIClass::transfer(const void *tx_buf, void *rx_buf, size_t count)
225+
{
226+
spi_transfer(&_spi, ((const uint8_t *)tx_buf), ((uint8_t *)rx_buf), count);
214227
}
215228

229+
216230
/**
217231
* @brief Not implemented.
218232
*/

libraries/SPI/src/SPI.h

+10-10
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@ extern "C" {
4141
#define SPI_TRANSMITRECEIVE false
4242
#define SPI_TRANSMITONLY true
4343

44-
// Defines a default timeout delay in milliseconds for the SPI transfer
45-
#ifndef SPI_TRANSFER_TIMEOUT
46-
#define SPI_TRANSFER_TIMEOUT 1000
47-
#endif
48-
4944
class SPISettings {
5045
public:
5146
constexpr SPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode)
@@ -127,21 +122,26 @@ class SPIClass {
127122
_spi.pin_ssel = (ssel);
128123
};
129124

130-
virtual void begin(void);
125+
void begin(void);
131126
void end(void);
132127

133128
/* This function should be used to configure the SPI instance in case you
134129
* don't use default parameters.
135130
*/
136131
void beginTransaction(SPISettings settings);
137-
virtual void endTransaction(void);
132+
void endTransaction(void);
138133

139134
/* Transfer functions: must be called after initialization of the SPI
140135
* instance with begin() or beginTransaction().
141136
*/
142-
virtual uint8_t transfer(uint8_t data, bool skipReceive = SPI_TRANSMITRECEIVE);
143-
virtual uint16_t transfer16(uint16_t data, bool skipReceive = SPI_TRANSMITRECEIVE);
144-
virtual void transfer(void *buf, size_t count, bool skipReceive = SPI_TRANSMITRECEIVE);
137+
uint8_t transfer(uint8_t data, bool skipReceive = SPI_TRANSMITRECEIVE);
138+
uint16_t transfer16(uint16_t data, bool skipReceive = SPI_TRANSMITRECEIVE);
139+
void transfer(void *buf, size_t count, bool skipReceive = SPI_TRANSMITRECEIVE);
140+
141+
/* Expand SPI API
142+
* https://github.com/arduino/ArduinoCore-API/discussions/189
143+
*/
144+
void transfer(const void *tx_buf, void *rx_buf, size_t count);
145145

146146
/* These methods are deprecated and kept for compatibility.
147147
* Use SPISettings with SPI.beginTransaction() to configure SPI parameters.

libraries/SPI/src/utility/spi_com.c

+41-39
Original file line numberDiff line numberDiff line change
@@ -500,71 +500,73 @@ void spi_deinit(spi_t *obj)
500500
* @brief This function is implemented by user to send/receive data over
501501
* SPI interface
502502
* @param obj : pointer to spi_t structure
503-
* @param buffer : tx data to send before reception
503+
* @param tx_buffer : tx data to send before reception
504+
* @param rx_buffer : rx data to receive if not numm
504505
* @param len : length in byte of the data to send and receive
505-
* @param Timeout: Timeout duration in tick
506-
* @param skipReceive: skip receiving data after transmit or not
507506
* @retval status of the send operation (0) in case of error
508507
*/
509-
spi_status_e spi_transfer(spi_t *obj, uint8_t *buffer, uint16_t len,
510-
uint32_t Timeout, bool skipReceive)
508+
spi_status_e spi_transfer(spi_t *obj, const uint8_t *tx_buffer, uint8_t *rx_buffer,
509+
uint16_t len)
511510
{
512511
spi_status_e ret = SPI_OK;
513512
uint32_t tickstart, size = len;
514513
SPI_TypeDef *_SPI = obj->handle.Instance;
515-
uint8_t *tx_buffer = buffer;
514+
uint8_t *tx_buf = (uint8_t *)tx_buffer;
516515

517-
if ((len == 0) || (Timeout == 0U)) {
518-
return Timeout > 0U ? SPI_ERROR : SPI_TIMEOUT;
519-
}
520-
tickstart = HAL_GetTick();
516+
if (len == 0) {
517+
ret = SPI_ERROR;
518+
} else {
519+
tickstart = HAL_GetTick();
521520

522521
#if defined(SPI_CR2_TSIZE)
523-
/* Start transfer */
524-
LL_SPI_SetTransferSize(_SPI, size);
525-
LL_SPI_Enable(_SPI);
526-
LL_SPI_StartMasterTransfer(_SPI);
522+
/* Start transfer */
523+
LL_SPI_SetTransferSize(_SPI, size);
524+
LL_SPI_Enable(_SPI);
525+
LL_SPI_StartMasterTransfer(_SPI);
527526
#endif
528527

529-
while (size--) {
528+
while (size--) {
530529
#if defined(SPI_SR_TXP)
531-
while (!LL_SPI_IsActiveFlag_TXP(_SPI));
530+
while (!LL_SPI_IsActiveFlag_TXP(_SPI));
532531
#else
533-
while (!LL_SPI_IsActiveFlag_TXE(_SPI));
532+
while (!LL_SPI_IsActiveFlag_TXE(_SPI));
534533
#endif
535-
LL_SPI_TransmitData8(_SPI, *tx_buffer++);
534+
LL_SPI_TransmitData8(_SPI, tx_buf ? *tx_buf++ : 0XFF);
536535

537-
if (!skipReceive) {
538536
#if defined(SPI_SR_RXP)
539537
while (!LL_SPI_IsActiveFlag_RXP(_SPI));
540538
#else
541539
while (!LL_SPI_IsActiveFlag_RXNE(_SPI));
542540
#endif
543-
*buffer++ = LL_SPI_ReceiveData8(_SPI);
544-
}
545-
if ((Timeout != HAL_MAX_DELAY) && (HAL_GetTick() - tickstart >= Timeout)) {
546-
ret = SPI_TIMEOUT;
547-
break;
541+
if (rx_buffer) {
542+
*rx_buffer++ = LL_SPI_ReceiveData8(_SPI);
543+
} else {
544+
LL_SPI_ReceiveData8(_SPI);
545+
}
546+
if ((SPI_TRANSFER_TIMEOUT != HAL_MAX_DELAY) &&
547+
(HAL_GetTick() - tickstart >= SPI_TRANSFER_TIMEOUT)) {
548+
ret = SPI_TIMEOUT;
549+
break;
550+
}
548551
}
549-
}
550552

551553
#if defined(SPI_IFCR_EOTC)
552-
// Add a delay before disabling SPI otherwise last-bit/last-clock may be truncated
553-
// See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294
554-
// Computed delay is half SPI clock
555-
delayMicroseconds(obj->disable_delay);
556-
557-
/* Close transfer */
558-
/* Clear flags */
559-
LL_SPI_ClearFlag_EOT(_SPI);
560-
LL_SPI_ClearFlag_TXTF(_SPI);
561-
/* Disable SPI peripheral */
562-
LL_SPI_Disable(_SPI);
554+
// Add a delay before disabling SPI otherwise last-bit/last-clock may be truncated
555+
// See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294
556+
// Computed delay is half SPI clock
557+
delayMicroseconds(obj->disable_delay);
558+
559+
/* Close transfer */
560+
/* Clear flags */
561+
LL_SPI_ClearFlag_EOT(_SPI);
562+
LL_SPI_ClearFlag_TXTF(_SPI);
563+
/* Disable SPI peripheral */
564+
LL_SPI_Disable(_SPI);
563565
#else
564-
/* Wait for end of transfer */
565-
while (LL_SPI_IsActiveFlag_BSY(_SPI));
566+
/* Wait for end of transfer */
567+
while (LL_SPI_IsActiveFlag_BSY(_SPI));
566568
#endif
567-
569+
}
568570
return ret;
569571
}
570572

libraries/SPI/src/utility/spi_com.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ typedef struct spi_s spi_t;
7878
#define SPI_SPEED_CLOCK_DIV128_MHZ ((uint32_t)128)
7979
#define SPI_SPEED_CLOCK_DIV256_MHZ ((uint32_t)256)
8080

81+
// Defines a default timeout delay in milliseconds for the SPI transfer
82+
#ifndef SPI_TRANSFER_TIMEOUT
83+
#define SPI_TRANSFER_TIMEOUT 1000
84+
#elif SPI_TRANSFER_TIMEOUT <= 0
85+
#error "SPI_TRANSFER_TIMEOUT cannot be less or equal to 0!"
86+
#endif
87+
8188
///@brief specifies the SPI mode to use
8289
//Mode Clock Polarity (CPOL) Clock Phase (CPHA)
8390
//SPI_MODE0 0 0
@@ -103,8 +110,7 @@ typedef enum {
103110
/* Exported functions ------------------------------------------------------- */
104111
void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb);
105112
void spi_deinit(spi_t *obj);
106-
spi_status_e spi_transfer(spi_t *obj, uint8_t *buffer, uint16_t len,
107-
uint32_t Timeout, bool skipReceive);
113+
spi_status_e spi_transfer(spi_t *obj, const uint8_t *tx_buffer, uint8_t *rx_buffer, uint16_t len);
108114
uint32_t spi_getClkFreq(spi_t *obj);
109115

110116
#ifdef __cplusplus

0 commit comments

Comments
 (0)