From 3ec6fbbc3a02dae6e94b737470debfc7fd727370 Mon Sep 17 00:00:00 2001 From: Nishanth Samala Date: Sat, 11 Oct 2025 06:22:15 -0500 Subject: [PATCH] feat(spi): add half-duplex support --- libraries/SPI/src/SPI.cpp | 32 ++++++++++++---- libraries/SPI/src/SPI.h | 21 +++++++++-- libraries/SPI/src/utility/spi_com.c | 57 +++++++++++++++++++++-------- libraries/SPI/src/utility/spi_com.h | 6 ++- 4 files changed, 89 insertions(+), 27 deletions(-) diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 3e6253c70d..3a89671bfb 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -42,19 +42,30 @@ SPIClass::SPIClass(uint32_t mosi, uint32_t miso, uint32_t sclk, uint32_t ssel) _spi.pin_mosi = digitalPinToPinName(mosi); _spi.pin_sclk = digitalPinToPinName(sclk); _spi.pin_ssel = digitalPinToPinName(ssel); + + // Default configuration + _spi.duplex = true; + _spi.direction = SPI_DIRECTION_2LINES; + _spi.mode = SPI_MODE_MASTER; } /** * @brief Initialize the SPI instance. * @param device: device mode (optional), SPI_MASTER or SPI_SLAVE. Default is master. */ -void SPIClass::begin(SPIDeviceMode device) +bool SPIClass::begin(SPIDeviceMode device) { _spi.handle.State = HAL_SPI_STATE_RESET; _spiSettings = SPISettings(); _spiSettings.deviceMode = device; - spi_init(&_spi, _spiSettings.clockFreq, _spiSettings.dataMode, + auto error = spi_init(&_spi, _spiSettings.clockFreq, _spiSettings.dataMode, _spiSettings.bitOrder, _spiSettings.deviceMode); + if (error != SPI_OK) { + Serial.printf("SPI init error: %d\n", error); + } + + init = error == SPI_OK; + return init; } /** @@ -62,13 +73,20 @@ void SPIClass::begin(SPIDeviceMode device) * don't use the default parameters set by the begin() function. * @param settings: SPI settings(clock speed, bit order, data mode, device mode). */ -void SPIClass::beginTransaction(SPISettings settings) +bool SPIClass::beginTransaction(SPISettings settings) { if (_spiSettings != settings) { _spiSettings = settings; - spi_init(&_spi, _spiSettings.clockFreq, _spiSettings.dataMode, + auto error = spi_init(&_spi, _spiSettings.clockFreq, + _spiSettings.dataMode, _spiSettings.bitOrder, _spiSettings.deviceMode); + if (error != SPI_OK) { + Serial.printf("SPI init error: %d\n", error); + } + init = error == SPI_OK; } + + return init; } /** @@ -76,7 +94,7 @@ void SPIClass::beginTransaction(SPISettings settings) */ void SPIClass::endTransaction(void) { - + // Nothing to do here } /** @@ -211,9 +229,9 @@ void SPIClass::transfer(void *buf, size_t count, bool skipReceive) * 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_status_e 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); + return spi_transfer(&_spi, ((const uint8_t *)tx_buf), ((uint8_t *)rx_buf), count); } diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 07a42134bb..2deb662065 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -126,13 +126,27 @@ class SPIClass { _spi.pin_ssel = (ssel); }; - void begin(SPIDeviceMode device = SPI_MASTER); + void setDirection(uint32_t direction) + { + _spi.direction = direction; + if (direction == SPI_DIRECTION_1LINE) + _spi.duplex = false; + else + _spi.duplex = true; + }; + + void setDeviceMode(SPIDeviceMode deviceMode) + { + _spiSettings.deviceMode = deviceMode; + }; + + bool begin(SPIDeviceMode device = SPI_MASTER); 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); + bool beginTransaction(SPISettings settings); void endTransaction(void); /* Transfer functions: must be called after initialization of the SPI @@ -145,7 +159,7 @@ class SPIClass { /* Expand SPI API * https://github.com/arduino/ArduinoCore-API/discussions/189 */ - void transfer(const void *tx_buf, void *rx_buf, size_t count); + spi_status_e 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. @@ -181,6 +195,7 @@ class SPIClass { protected: // spi instance spi_t _spi; + bool init; private: /* Current SPISettings */ diff --git a/libraries/SPI/src/utility/spi_com.c b/libraries/SPI/src/utility/spi_com.c index 3fc4d6ef76..e327ab7d56 100644 --- a/libraries/SPI/src/utility/spi_com.c +++ b/libraries/SPI/src/utility/spi_com.c @@ -206,12 +206,15 @@ static uint32_t compute_disable_delay(spi_t *obj) * @param device : spi device mode: master or slave * @retval None */ -void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device) +spi_status_e spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device) { if (obj == NULL) { - return; + return SPI_ERROR; } + // Set the device mode before any other initialization + obj->mode = device; + SPI_HandleTypeDef *handle = &(obj->handle); uint32_t spi_freq = 0; uint32_t pull = 0; @@ -226,9 +229,11 @@ void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMo SPI_TypeDef *spi_ssel = pinmap_peripheral(obj->pin_ssel, PinMap_SPI_SSEL); /* Pins MOSI/MISO/SCLK must not be NP. ssel can be NP. */ - if (spi_mosi == NP || spi_miso == NP || spi_sclk == NP) { - core_debug("ERROR: at least one SPI pin has no peripheral\n"); - return; + if (spi_mosi == NP || spi_miso == NP || spi_sclk == NP || spi_ssel == NP) { + if (spi_miso == NP && obj->duplex) { + core_debug("ERROR: at least one SPI pin has no peripheral\n"); + return SPI_ERROR; + } } 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 // Are all pins connected to the same SPI instance? if (spi_data == NP || spi_cntl == NP || obj->spi == NP) { core_debug("ERROR: SPI pins mismatch\n"); - return; + return SPI_ERROR; } #if defined(SUBGHZSPI_BASE) } else { if (obj->pin_mosi != NC || obj->pin_miso != NC || obj->pin_sclk != NC || obj->pin_ssel != NC) { core_debug("ERROR: SUBGHZ_SPI cannot define custom pins\n"); - return; + return SPI_ERROR; } } #endif // Configure the SPI pins if (obj->pin_ssel != NC) { - handle->Init.NSS = SPI_NSS_HARD_OUTPUT; + if (obj->mode == SPI_MODE_SLAVE) { + handle->Init.NSS = SPI_NSS_HARD_INPUT; + } else { + handle->Init.NSS = SPI_NSS_HARD_OUTPUT; + } } else { handle->Init.NSS = SPI_NSS_SOFT; } /* Fill default value */ - handle->Instance = obj->spi; - handle->Init.Mode = (device == SPI_MASTER) ? SPI_MODE_MASTER : SPI_MODE_SLAVE; + handle->Instance = obj->spi; + handle->Init.Mode = obj->mode; spi_freq = spi_getClkFreqInst(obj->spi); /* 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 obj->disable_delay = compute_disable_delay(obj); #endif - handle->Init.Direction = SPI_DIRECTION_2LINES; + handle->Init.Direction = obj->direction; if ((mode == SPI_MODE0) || (mode == SPI_MODE2)) { 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 #if defined(SUBGHZSPI_BASE) if (handle->Instance != SUBGHZSPI) { #endif - /* Configure SPI GPIO pins */ - pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); - pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); + /* Configure SPI GPIO pins based on device mode and duplex setting */ + if (obj->mode == SPI_MODE_MASTER) { + /* Master mode: configure MOSI for output */ + pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); + /* Configure MISO for input if duplex is enabled */ + if (obj->duplex) { + pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); + } + } else { + /* Slave mode: configure MISO for output */ + pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); + /* Configure MOSI for input if duplex is enabled */ + if (obj->duplex) { + pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); + } + } pinmap_pinout(obj->pin_sclk, PinMap_SPI_SCLK); /* * 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 } #endif - HAL_SPI_Init(handle); + HAL_StatusTypeDef status = HAL_SPI_Init(handle); + if (status != HAL_OK) { + core_debug("ERROR: HAL_SPI_Init failed\n"); + return SPI_ERROR; + } /* In order to set correctly the SPI polarity we need to enable the peripheral */ __HAL_SPI_ENABLE(handle); + return SPI_OK; } /** diff --git a/libraries/SPI/src/utility/spi_com.h b/libraries/SPI/src/utility/spi_com.h index cdde9ed0d2..af598b0af5 100644 --- a/libraries/SPI/src/utility/spi_com.h +++ b/libraries/SPI/src/utility/spi_com.h @@ -30,6 +30,9 @@ struct spi_s { PinName pin_mosi; PinName pin_sclk; PinName pin_ssel; + bool duplex; + uint32_t direction; + uint32_t mode; #if defined(SPI_IFCR_EOTC) // Delay before disabling SPI. // See https://github.com/stm32duino/Arduino_Core_STM32/issues/1294 @@ -39,7 +42,6 @@ struct spi_s { typedef struct spi_s spi_t; - ///@brief specifies the SPI speed bus in HZ. #define SPI_SPEED_CLOCK_DEFAULT 4000000 @@ -88,7 +90,7 @@ typedef enum { } spi_status_e; /* Exported functions ------------------------------------------------------- */ -void spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device); +spi_status_e spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPIDeviceMode device_mode); void spi_deinit(spi_t *obj); 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);