Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions libraries/SPI/src/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,41 +42,59 @@ 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;
}

/**
* @brief This function should be used to configure the SPI instance in case you
* 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;
}

/**
* @brief End the transaction after beginTransaction usage
*/
void SPIClass::endTransaction(void)
{

// Nothing to do here
}

/**
Expand Down Expand Up @@ -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);
}


Expand Down
26 changes: 23 additions & 3 deletions libraries/SPI/src/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,32 @@ 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;
};

void setIsMaster(bool isMaster)
{
_spi.mode = isMaster ? SPI_MODE_MASTER : SPI_MODE_SLAVE;
};

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
Expand All @@ -145,7 +164,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.
Expand Down Expand Up @@ -181,6 +200,7 @@ class SPIClass {
protected:
// spi instance
spi_t _spi;
bool init;

private:
/* Current SPISettings */
Expand Down
117 changes: 102 additions & 15 deletions libraries/SPI/src/utility/spi_com.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,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;
Expand All @@ -227,9 +230,56 @@ 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;
}
}

/* 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);
Expand All @@ -240,27 +290,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_*' */
Expand Down Expand Up @@ -291,7 +345,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;
Expand Down Expand Up @@ -326,9 +380,37 @@ 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 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 {
/* 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);
}
}
pinmap_pinout(obj->pin_sclk, PinMap_SPI_SCLK);
/*
* According the STM32 Datasheet for SPI peripheral we need to PULLDOWN
Expand Down Expand Up @@ -397,10 +479,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;
}

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

Expand Down Expand Up @@ -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);
Expand Down
45 changes: 43 additions & 2 deletions variants/STM32F4xx/F413V(G-H)T_F423VHT/generic_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_* */
Loading