From 3ec6fbbc3a02dae6e94b737470debfc7fd727370 Mon Sep 17 00:00:00 2001 From: Nishanth Samala Date: Sat, 11 Oct 2025 06:22:15 -0500 Subject: [PATCH 1/5] 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); From 00d2a84280f2e56474006c7e9950c0c3ca6e1b2a Mon Sep 17 00:00:00 2001 From: Nishanth Samala Date: Sun, 19 Oct 2025 12:27:00 -0500 Subject: [PATCH 2/5] Added ldscript for STM32F413VH --- .../STM32F4xx/F413V(G-H)T_F423VHT/ldscript.ld | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 variants/STM32F4xx/F413V(G-H)T_F423VHT/ldscript.ld diff --git a/variants/STM32F4xx/F413V(G-H)T_F423VHT/ldscript.ld b/variants/STM32F4xx/F413V(G-H)T_F423VHT/ldscript.ld new file mode 100644 index 0000000000..48c0b13eac --- /dev/null +++ b/variants/STM32F4xx/F413V(G-H)T_F423VHT/ldscript.ld @@ -0,0 +1,177 @@ +/** + ****************************************************************************** + * @file LinkerScript.ld + * @author Auto-generated by STM32CubeIDE + * @brief Linker script for STM32F413VHTx Device from STM32F4 series + * 1536Kbytes FLASH + * 320Kbytes RAM + * + * Set heap size, stack size and stack location according + * to application requirements. + * + * Set memory bank area and size if external memory is used + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2020 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ + +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Memories definition */ +MEMORY +{ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = LD_MAX_DATA_SIZE + FLASH (rx) : ORIGIN = 0x8000000 + LD_FLASH_OFFSET, LENGTH = LD_MAX_SIZE - LD_FLASH_OFFSET +} + +/* Sections */ +SECTIONS +{ + /* The startup code into "FLASH" Rom type memory */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data into "FLASH" Rom type memory */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data into "FLASH" Rom type memory */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab (READONLY) : { + . = ALIGN(4); + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(4); + } >FLASH + + .ARM (READONLY) : { + . = ALIGN(4); + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + . = ALIGN(4); + } >FLASH + + .preinit_array (READONLY) : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + . = ALIGN(4); + } >FLASH + + .init_array (READONLY) : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + . = ALIGN(4); + } >FLASH + + .fini_array (READONLY) : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(4); + } >FLASH + + /* Used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections into "RAM" Ram type memory */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.RamFunc) /* .RamFunc sections */ + *(.RamFunc*) /* .RamFunc* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + + } >RAM AT> FLASH + + /* Uninitialized data section into "RAM" Ram type memory */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM + + /* Remove information from the compiler libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} From acfeade46a130e8d3ddb7814b25ae8381dfc64b9 Mon Sep 17 00:00:00 2001 From: Nishanth Samala Date: Sun, 19 Oct 2025 12:43:01 -0500 Subject: [PATCH 3/5] Added system clock config --- .../F413V(G-H)T_F423VHT/generic_clock.c | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/variants/STM32F4xx/F413V(G-H)T_F423VHT/generic_clock.c b/variants/STM32F4xx/F413V(G-H)T_F423VHT/generic_clock.c index 8192545299..4b596e2175 100644 --- a/variants/STM32F4xx/F413V(G-H)T_F423VHT/generic_clock.c +++ b/variants/STM32F4xx/F413V(G-H)T_F423VHT/generic_clock.c @@ -21,8 +21,49 @@ */ WEAK void SystemClock_Config(void) { - /* SystemClock_Config can be generated by STM32CubeMX */ -#warning "SystemClock_Config() is empty. Default clock at reset is used." + RCC_OscInitTypeDef RCC_OscInitStruct = {}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {}; + + // Configure the main internal regulator output voltage + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + + // Initialize the RCC Oscillators according to the specified parameters + // Using HSI (16 MHz internal oscillator) + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; + + // PLL Configuration for 96 MHz system clock and 48 MHz USB clock + // HSI = 16 MHz + // VCO = (16 MHz / PLLM) * PLLN = (16 / 8) * 192 = 384 MHz + // SYSCLK = VCO / PLLP = 384 / 4 = 96 MHz + // USB Clock = VCO / PLLQ = 384 / 8 = 48 MHz (required for USB) + RCC_OscInitStruct.PLL.PLLM = 8; + RCC_OscInitStruct.PLL.PLLN = 192; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; + RCC_OscInitStruct.PLL.PLLQ = 8; + RCC_OscInitStruct.PLL.PLLR = 2; + + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) + { + Error_Handler(); + } + + // Initialize the CPU, AHB and APB buses clocks + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK + | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // 96 MHz + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // 48 MHz (max 50 MHz) + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // 96 MHz (max 100 MHz) + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) + { + Error_Handler(); + } } #endif /* ARDUINO_GENERIC_* */ From 54f49cc068107fd83d2f3b404133333d3c8c81f3 Mon Sep 17 00:00:00 2001 From: Nishanth Samala Date: Sun, 19 Oct 2025 13:13:50 -0500 Subject: [PATCH 4/5] Added back setIsMaster --- libraries/SPI/src/SPI.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 2deb662065..5b48b1d604 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -140,6 +140,11 @@ class SPIClass { _spiSettings.deviceMode = deviceMode; }; + void setIsMaster(bool isMaster) + { + _spi.mode = isMaster ? SPI_MODE_MASTER : SPI_MODE_SLAVE; + }; + bool begin(SPIDeviceMode device = SPI_MASTER); void end(void); From 101c8dbf9bbc4f60f4ec97f99d68072832e2a256 Mon Sep 17 00:00:00 2001 From: Nishanth Samala Date: Sun, 19 Oct 2025 13:16:58 -0500 Subject: [PATCH 5/5] Fix spi_com config for SPI_DIRECTION_2LINES_RXONLY --- libraries/SPI/src/utility/spi_com.c | 80 +++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/libraries/SPI/src/utility/spi_com.c b/libraries/SPI/src/utility/spi_com.c index 13ef87af2b..ca4464c49d 100644 --- a/libraries/SPI/src/utility/spi_com.c +++ b/libraries/SPI/src/utility/spi_com.c @@ -237,6 +237,51 @@ spi_status_e spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPI } } + /* Validate pins based on direction mode */ + if (obj->direction == SPI_DIRECTION_1LINE) { + /* Half-duplex: only MOSI (master) or MISO (slave) is used */ + if (obj->mode == SPI_MODE_MASTER) { + if (spi_mosi == NP) { + core_debug("ERROR: MOSI required for 1-line master mode\n"); + return SPI_ERROR; + } + spi_miso = spi_mosi; /* Merge with same peripheral for validation */ + } else { + if (spi_miso == NP) { + core_debug("ERROR: MISO required for 1-line slave mode\n"); + return SPI_ERROR; + } + spi_mosi = spi_miso; /* Merge with same peripheral for validation */ + } + } else if (obj->direction == SPI_DIRECTION_2LINES_RXONLY) { + /* RX-only: only MISO (master) or MOSI (slave) is used */ + if (obj->mode == SPI_MODE_MASTER) { + if (spi_miso == NP) { + core_debug("ERROR: MISO required for master RX-only mode\n"); + return SPI_ERROR; + } + spi_mosi = spi_miso; /* Merge with same peripheral for validation */ + } else { + if (spi_mosi == NP) { + core_debug("ERROR: MOSI required for slave RX-only mode\n"); + return SPI_ERROR; + } + spi_miso = spi_mosi; /* Merge with same peripheral for validation */ + } + } else { + /* Full duplex (SPI_DIRECTION_2LINES): both MOSI and MISO required */ + if (spi_mosi == NP || spi_miso == NP) { + core_debug("ERROR: Both MOSI and MISO required for 2-line mode\n"); + return SPI_ERROR; + } + } + + /* Validate SCLK is always required */ + if (spi_sclk == NP) { + core_debug("ERROR: SCLK is required\n"); + return SPI_ERROR; + } + SPI_TypeDef *spi_data = pinmap_merge_peripheral(spi_mosi, spi_miso); SPI_TypeDef *spi_cntl = pinmap_merge_peripheral(spi_sclk, spi_ssel); @@ -335,19 +380,34 @@ spi_status_e spi_init(spi_t *obj, uint32_t speed, SPIMode mode, uint8_t msb, SPI #if defined(SUBGHZSPI_BASE) if (handle->Instance != SUBGHZSPI) { #endif - /* 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) { + /* Configure SPI GPIO pins based on device mode and direction */ + if (obj->direction == SPI_DIRECTION_1LINE) { + /* Half-duplex mode: single bidirectional data line */ + if (obj->mode == SPI_MODE_MASTER) { + /* Master uses MOSI as bidirectional line */ + pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); + } else { + /* Slave uses MISO as bidirectional line */ pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); } + } else if (obj->direction == SPI_DIRECTION_2LINES_RXONLY) { + /* RX-only mode: only receive pin is configured */ + if (obj->mode == SPI_MODE_MASTER) { + /* Master RX-only: only configure MISO (receives from slave) */ + pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); + } else { + /* Slave RX-only: only configure MOSI (receives from master) */ + pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); + } } 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) { + /* Full duplex mode (SPI_DIRECTION_2LINES): configure both pins */ + if (obj->mode == SPI_MODE_MASTER) { + /* Master mode: configure MOSI for output and MISO for input */ + pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); + pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); + } else { + /* Slave mode: configure MISO for output and MOSI for input */ + pinmap_pinout(obj->pin_miso, PinMap_SPI_MISO); pinmap_pinout(obj->pin_mosi, PinMap_SPI_MOSI); } }