Skip to content

Commit 3ec6fbb

Browse files
committed
feat(spi): add half-duplex support
1 parent 9cc7f13 commit 3ec6fbb

File tree

4 files changed

+89
-27
lines changed

4 files changed

+89
-27
lines changed

libraries/SPI/src/SPI.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,41 +42,59 @@ SPIClass::SPIClass(uint32_t mosi, uint32_t miso, uint32_t sclk, uint32_t ssel)
4242
_spi.pin_mosi = digitalPinToPinName(mosi);
4343
_spi.pin_sclk = digitalPinToPinName(sclk);
4444
_spi.pin_ssel = digitalPinToPinName(ssel);
45+
46+
// Default configuration
47+
_spi.duplex = true;
48+
_spi.direction = SPI_DIRECTION_2LINES;
49+
_spi.mode = SPI_MODE_MASTER;
4550
}
4651

4752
/**
4853
* @brief Initialize the SPI instance.
4954
* @param device: device mode (optional), SPI_MASTER or SPI_SLAVE. Default is master.
5055
*/
51-
void SPIClass::begin(SPIDeviceMode device)
56+
bool SPIClass::begin(SPIDeviceMode device)
5257
{
5358
_spi.handle.State = HAL_SPI_STATE_RESET;
5459
_spiSettings = SPISettings();
5560
_spiSettings.deviceMode = device;
56-
spi_init(&_spi, _spiSettings.clockFreq, _spiSettings.dataMode,
61+
auto error = spi_init(&_spi, _spiSettings.clockFreq, _spiSettings.dataMode,
5762
_spiSettings.bitOrder, _spiSettings.deviceMode);
63+
if (error != SPI_OK) {
64+
Serial.printf("SPI init error: %d\n", error);
65+
}
66+
67+
init = error == SPI_OK;
68+
return init;
5869
}
5970

6071
/**
6172
* @brief This function should be used to configure the SPI instance in case you
6273
* don't use the default parameters set by the begin() function.
6374
* @param settings: SPI settings(clock speed, bit order, data mode, device mode).
6475
*/
65-
void SPIClass::beginTransaction(SPISettings settings)
76+
bool SPIClass::beginTransaction(SPISettings settings)
6677
{
6778
if (_spiSettings != settings) {
6879
_spiSettings = settings;
69-
spi_init(&_spi, _spiSettings.clockFreq, _spiSettings.dataMode,
80+
auto error = spi_init(&_spi, _spiSettings.clockFreq,
81+
_spiSettings.dataMode,
7082
_spiSettings.bitOrder, _spiSettings.deviceMode);
83+
if (error != SPI_OK) {
84+
Serial.printf("SPI init error: %d\n", error);
85+
}
86+
init = error == SPI_OK;
7187
}
88+
89+
return init;
7290
}
7391

7492
/**
7593
* @brief End the transaction after beginTransaction usage
7694
*/
7795
void SPIClass::endTransaction(void)
7896
{
79-
97+
// Nothing to do here
8098
}
8199

82100
/**
@@ -211,9 +229,9 @@ void SPIClass::transfer(void *buf, size_t count, bool skipReceive)
211229
* the SPI transfer. If NULL, the received data will be discarded.
212230
* @param count: number of bytes to send/receive.
213231
*/
214-
void SPIClass::transfer(const void *tx_buf, void *rx_buf, size_t count)
232+
spi_status_e SPIClass::transfer(const void *tx_buf, void *rx_buf, size_t count)
215233
{
216-
spi_transfer(&_spi, ((const uint8_t *)tx_buf), ((uint8_t *)rx_buf), count);
234+
return spi_transfer(&_spi, ((const uint8_t *)tx_buf), ((uint8_t *)rx_buf), count);
217235
}
218236

219237

libraries/SPI/src/SPI.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,27 @@ class SPIClass {
126126
_spi.pin_ssel = (ssel);
127127
};
128128

129-
void begin(SPIDeviceMode device = SPI_MASTER);
129+
void setDirection(uint32_t direction)
130+
{
131+
_spi.direction = direction;
132+
if (direction == SPI_DIRECTION_1LINE)
133+
_spi.duplex = false;
134+
else
135+
_spi.duplex = true;
136+
};
137+
138+
void setDeviceMode(SPIDeviceMode deviceMode)
139+
{
140+
_spiSettings.deviceMode = deviceMode;
141+
};
142+
143+
bool begin(SPIDeviceMode device = SPI_MASTER);
130144
void end(void);
131145

132146
/* This function should be used to configure the SPI instance in case you
133147
* don't use default parameters.
134148
*/
135-
void beginTransaction(SPISettings settings);
149+
bool beginTransaction(SPISettings settings);
136150
void endTransaction(void);
137151

138152
/* Transfer functions: must be called after initialization of the SPI
@@ -145,7 +159,7 @@ class SPIClass {
145159
/* Expand SPI API
146160
* https://github.com/arduino/ArduinoCore-API/discussions/189
147161
*/
148-
void transfer(const void *tx_buf, void *rx_buf, size_t count);
162+
spi_status_e transfer(const void *tx_buf, void *rx_buf, size_t count);
149163

150164
/* These methods are deprecated and kept for compatibility.
151165
* Use SPISettings with SPI.beginTransaction() to configure SPI parameters.
@@ -181,6 +195,7 @@ class SPIClass {
181195
protected:
182196
// spi instance
183197
spi_t _spi;
198+
bool init;
184199

185200
private:
186201
/* Current SPISettings */

libraries/SPI/src/utility/spi_com.c

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,15 @@ static uint32_t compute_disable_delay(spi_t *obj)
206206
* @param device : spi device mode: master or slave
207207
* @retval None
208208
*/
209-
void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device)
209+
spi_status_e spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device)
210210
{
211211
if (obj == NULL) {
212-
return;
212+
return SPI_ERROR;
213213
}
214214

215+
// Set the device mode before any other initialization
216+
obj->mode = device;
217+
215218
SPI_HandleTypeDef *handle = &(obj->handle);
216219
uint32_t spi_freq = 0;
217220
uint32_t pull = 0;
@@ -226,9 +229,11 @@ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMo
226229
SPI_TypeDef *spi_ssel = pinmap_peripheral(obj->pin_ssel, PinMap_SPI_SSEL);
227230

228231
/* Pins MOSI/MISO/SCLK must not be NP. ssel can be NP. */
229-
if (spi_mosi == NP || spi_miso == NP || spi_sclk == NP) {
230-
core_debug("ERROR: at least one SPI pin has no peripheral\n");
231-
return;
232+
if (spi_mosi == NP || spi_miso == NP || spi_sclk == NP || spi_ssel == NP) {
233+
if (spi_miso == NP && obj->duplex) {
234+
core_debug("ERROR: at least one SPI pin has no peripheral\n");
235+
return SPI_ERROR;
236+
}
232237
}
233238

234239
SPI_TypeDef *spi_data = pinmap_merge_peripheral(spi_mosi, spi_miso);
@@ -239,27 +244,31 @@ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMo
239244
// Are all pins connected to the same SPI instance?
240245
if (spi_data == NP || spi_cntl == NP || obj->spi == NP) {
241246
core_debug("ERROR: SPI pins mismatch\n");
242-
return;
247+
return SPI_ERROR;
243248
}
244249
#if defined(SUBGHZSPI_BASE)
245250
} else {
246251
if (obj->pin_mosi != NC || obj->pin_miso != NC || obj->pin_sclk != NC || obj->pin_ssel != NC) {
247252
core_debug("ERROR: SUBGHZ_SPI cannot define custom pins\n");
248-
return;
253+
return SPI_ERROR;
249254
}
250255
}
251256
#endif
252257

253258
// Configure the SPI pins
254259
if (obj->pin_ssel != NC) {
255-
handle->Init.NSS = SPI_NSS_HARD_OUTPUT;
260+
if (obj->mode == SPI_MODE_SLAVE) {
261+
handle->Init.NSS = SPI_NSS_HARD_INPUT;
262+
} else {
263+
handle->Init.NSS = SPI_NSS_HARD_OUTPUT;
264+
}
256265
} else {
257266
handle->Init.NSS = SPI_NSS_SOFT;
258267
}
259268

260269
/* Fill default value */
261-
handle->Instance = obj->spi;
262-
handle->Init.Mode = (device == SPI_MASTER) ? SPI_MODE_MASTER : SPI_MODE_SLAVE;
270+
handle->Instance = obj->spi;
271+
handle->Init.Mode = obj->mode;
263272

264273
spi_freq = spi_getClkFreqInst(obj->spi);
265274
/* For SUBGHZSPI, 'SPI_BAUDRATEPRESCALER_*' == 'SUBGHZSPI_BAUDRATEPRESCALER_*' */
@@ -290,7 +299,7 @@ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMo
290299
obj->disable_delay = compute_disable_delay(obj);
291300
#endif
292301

293-
handle->Init.Direction = SPI_DIRECTION_2LINES;
302+
handle->Init.Direction = obj->direction;
294303

295304
if ((mode == SPI_MODE0) || (mode == SPI_MODE2)) {
296305
handle->Init.CLKPhase = SPI_PHASE_1EDGE;
@@ -325,9 +334,22 @@ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMo
325334
#if defined(SUBGHZSPI_BASE)
326335
if (handle->Instance != SUBGHZSPI) {
327336
#endif
328-
/* Configure SPI GPIO pins */
329-
pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI);
330-
pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO);
337+
/* Configure SPI GPIO pins based on device mode and duplex setting */
338+
if (obj->mode == SPI_MODE_MASTER) {
339+
/* Master mode: configure MOSI for output */
340+
pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI);
341+
/* Configure MISO for input if duplex is enabled */
342+
if (obj->duplex) {
343+
pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO);
344+
}
345+
} else {
346+
/* Slave mode: configure MISO for output */
347+
pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO);
348+
/* Configure MOSI for input if duplex is enabled */
349+
if (obj->duplex) {
350+
pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI);
351+
}
352+
}
331353
pinmap_pinout(obj->pin_sclk, PinMap_SPI_SCLK);
332354
/*
333355
* According the STM32 Datasheet for SPI peripheral we need to PULLDOWN
@@ -396,10 +418,15 @@ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMo
396418
}
397419
#endif
398420

399-
HAL_SPI_Init(handle);
421+
HAL_StatusTypeDef status = HAL_SPI_Init(handle);
422+
if (status != HAL_OK) {
423+
core_debug("ERROR: HAL_SPI_Init failed\n");
424+
return SPI_ERROR;
425+
}
400426

401427
/* In order to set correctly the SPI polarity we need to enable the peripheral */
402428
__HAL_SPI_ENABLE(handle);
429+
return SPI_OK;
403430
}
404431

405432
/**

libraries/SPI/src/utility/spi_com.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ struct spi_s {
3030
PinName pin_mosi;
3131
PinName pin_sclk;
3232
PinName pin_ssel;
33+
bool duplex;
34+
uint32_t direction;
35+
uint32_t mode;
3336
#if defined(SPI_IFCR_EOTC)
3437
// Delay before disabling SPI.
3538
// See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294
@@ -39,7 +42,6 @@ struct spi_s {
3942

4043
typedef struct spi_s spi_t;
4144

42-
4345
///@brief specifies the SPI speed bus in HZ.
4446
#define SPI_SPEED_CLOCK_DEFAULT 4000000
4547

@@ -88,7 +90,7 @@ typedef enum {
8890
} spi_status_e;
8991

9092
/* Exported functions ------------------------------------------------------- */
91-
void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device);
93+
spi_status_e spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device_mode);
9294
void spi_deinit(spi_t *obj);
9395
spi_status_e spi_transfer(spi_t *obj, const uint8_t *tx_buffer, uint8_t *rx_buffer, uint16_t len);
9496
uint32_t spi_getClkFreq(spi_t *obj);

0 commit comments

Comments
 (0)