Skip to content
Merged
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
55 changes: 52 additions & 3 deletions examples/stm32wb/stm32wb55xx_nucleo.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ whal_Gpio g_whalGpio = {

.cfg = &(whal_Stm32wbGpio_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_GPIOB_CLOCK},
.clk = (const void *[2]) {
&(whal_Stm32wbRcc_Clk){WHAL_STM32WB55_GPIOA_CLOCK},
&(whal_Stm32wbRcc_Clk){WHAL_STM32WB55_GPIOB_CLOCK},
},
.clkCount = 2,

.pinCfg = (whal_Stm32wbGpio_PinCfg[3]) {
.pinCfg = (whal_Stm32wbGpio_PinCfg[7]) {
[LED_PIN] = { /* LED */
.port = WHAL_STM32WB_GPIO_PORT_B,
.pin = 5,
Expand Down Expand Up @@ -60,8 +64,44 @@ whal_Gpio g_whalGpio = {
.pull = WHAL_STM32WB_GPIO_PULL_UP,
.altFn = 7,
},
[SPI1_SCK_PIN] = { /* SPI1 SCK */
.port = WHAL_STM32WB_GPIO_PORT_A,
.pin = 5,
.mode = WHAL_STM32WB_GPIO_MODE_ALTFN,
.outType = WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL,
.speed = WHAL_STM32WB_GPIO_SPEED_FAST,
.pull = WHAL_STM32WB_GPIO_PULL_UP,
.altFn = 5,
},
[SPI1_MISO_PIN] = { /* SPI1 MISO */
.port = WHAL_STM32WB_GPIO_PORT_A,
.pin = 6,
.mode = WHAL_STM32WB_GPIO_MODE_ALTFN,
.outType = WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL,
.speed = WHAL_STM32WB_GPIO_SPEED_FAST,
.pull = WHAL_STM32WB_GPIO_PULL_UP,
.altFn = 5,
},
[SPI1_MOSI_PIN] = { /* SPI1 MOSI */
.port = WHAL_STM32WB_GPIO_PORT_A,
.pin = 7,
.mode = WHAL_STM32WB_GPIO_MODE_ALTFN,
.outType = WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL,
.speed = WHAL_STM32WB_GPIO_SPEED_FAST,
.pull = WHAL_STM32WB_GPIO_PULL_UP,
.altFn = 5,
},
[CS_PIN] = { /* SPI1 CS */
.port = WHAL_STM32WB_GPIO_PORT_A,
.pin = 4,
.mode = WHAL_STM32WB_GPIO_MODE_OUT,
.outType = WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL,
.speed = WHAL_STM32WB_GPIO_SPEED_LOW,
.pull = WHAL_STM32WB_GPIO_PULL_UP,
.altFn = 0,
},
},
.pinCount = 3,
.pinCount = 7,
},
};

Expand Down Expand Up @@ -97,3 +137,12 @@ whal_Flash g_whalFlash = {
.size = 0x100000,
},
};

whal_Spi g_whalSpi = {
WHAL_STM32WB55_SPI1_DEVICE,

.cfg = &(whal_Stm32wbSpi_Cfg) {
.clkCtrl = &g_whalClock,
.clk = &(whal_Stm32wbRcc_Clk) {WHAL_STM32WB55_SPI1_CLOCK},
},
};
6 changes: 6 additions & 0 deletions examples/stm32wb/stm32wb55xx_nucleo.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ enum {
LED_PIN,
LPUART1_TX_PIN,
LPUART1_RX_PIN,
SPI1_SCK_PIN,
SPI1_MISO_PIN,
SPI1_MOSI_PIN,
CS_PIN,
};

/* RCC clock controller instance. */
Expand All @@ -30,5 +34,7 @@ extern whal_Uart g_whalUart;
/* Flash controller instance. */
extern whal_Flash g_whalFlash;

/* SPI controller instance. */
extern whal_Spi g_whalSpi;

#endif /* STM32WB55XX_NUCLEO_H */
21 changes: 13 additions & 8 deletions src/gpio/stm32wb_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ whal_Error whal_Stm32wbGpio_Init(whal_Gpio *gpioDev)
cfg = (whal_Stm32wbGpio_Cfg *)gpioDev->cfg;
pinCfg = cfg->pinCfg;

/* Enable GPIO port clock before accessing registers */
err = whal_Clock_Enable(cfg->clkCtrl, cfg->clk);
if (err) {
return err;
for (size_t i = 0; i < cfg->clkCount; ++i) {
/* Enable GPIO port clock before accessing registers */
err = whal_Clock_Enable(cfg->clkCtrl, cfg->clk[i]);
if (err) {
return err;
}
}

/* Initialize each pin in the configuration array */
Expand All @@ -130,12 +132,15 @@ whal_Error whal_Stm32wbGpio_Deinit(whal_Gpio *gpioDev)
{
whal_Error err;
whal_Stm32wbGpio_Cfg *cfg;

cfg = (whal_Stm32wbGpio_Cfg *)gpioDev->cfg;

/* Disable GPIO port clock */
err = whal_Clock_Disable(cfg->clkCtrl, cfg->clk);
if (err) {
return err;
for (size_t i = 0; i < cfg->clkCount; ++i) {
/* Disable GPIO port clock */
err = whal_Clock_Disable(cfg->clkCtrl, cfg->clk[i]);
if (err) {
return err;
}
}

return WHAL_SUCCESS;
Expand Down
214 changes: 194 additions & 20 deletions src/spi/stm32wb_spi.c
Original file line number Diff line number Diff line change
@@ -1,56 +1,230 @@
#include <stdint.h>
#include <wolfHAL/spi/stm32wb_spi.h>
#include <wolfHAL/spi/spi.h>
#include <wolfHAL/clock/clock.h>
#include <wolfHAL/error.h>
#include <wolfHAL/regmap.h>
#include <wolfHAL/bitops.h>

/*
* STM32WB SPI Driver - Stub Implementation
* STM32WB SPI Register Definitions
*
* TODO: Implement SPI register configuration and data transfer.
* The SPI peripheral provides full-duplex synchronous serial communication.
* This driver configures it in master mode with software slave management
* and 8-bit data frames.
*/

/* Control Register 1 - master config, clock, enable */
#define SSPI_CR1_REG 0x00
#define SSPI_CR1_CPHA WHAL_MASK(0) /* Clock phase */
#define SSPI_CR1_CPOL WHAL_MASK(1) /* Clock polarity */
#define SSPI_CR1_MSTR WHAL_MASK(2) /* Master selection */
#define SSPI_CR1_BR WHAL_MASK_RANGE(5, 3) /* Baud rate prescaler */
#define SSPI_CR1_SPE WHAL_MASK(6) /* SPI enable */
#define SSPI_CR1_SSI WHAL_MASK(8) /* Internal slave select */
#define SSPI_CR1_SSM WHAL_MASK(9) /* Software slave management */

/* Control Register 2 - data size, FIFO threshold */
#define SSPI_CR2_REG 0x04
#define SSPI_CR2_DS WHAL_MASK_RANGE(11, 8) /* Data size (0111 = 8-bit) */
#define SSPI_CR2_FRXTH WHAL_MASK(12) /* FIFO reception threshold */

/* Status Register */
#define SSPI_SR_REG 0x08
#define SSPI_SR_RXNE WHAL_MASK(0) /* Receive buffer not empty */
#define SSPI_SR_TXE WHAL_MASK(1) /* Transmit buffer empty */
#define SSPI_SR_BSY WHAL_MASK(7) /* Busy flag */

/* Data Register - 8/16-bit access */
#define SSPI_DR_REG 0x0C
#define SSPI_DR_MASK WHAL_MASK_RANGE(7, 0)

/* 8-bit data size value for DS field */
#define SSPI_DS_8BIT 0x7

/*
* Calculate the baud rate prescaler index for a target baud rate.
*
* SPI baud rate = fPCLK / (2 ^ (BR + 1))
* BR=0 -> /2, BR=1 -> /4, BR=2 -> /8, ... BR=7 -> /256
*
* Returns the smallest prescaler that does not exceed the target baud.
*/
static uint32_t whal_Stm32wbSpi_CalcBr(size_t pclk, uint32_t targetBaud)
{
uint32_t br;

for (br = 0; br < 7; br++) {
if ((pclk / (2u << br)) <= targetBaud) {
return br;
}
}

return 7;
}

/*
* Configure SPI mode and baud rate, then enable the peripheral.
* Must be called with SPE disabled.
*/
static void whal_Stm32wbSpi_ApplyComCfg(const whal_Regmap *reg,
whal_Stm32wbSpi_Cfg *cfg,
whal_Stm32wbSpi_ComCfg *comCfg)
{
whal_Error err;
size_t pclk;
uint32_t cpol, cpha, br;

err = whal_Clock_GetRate(cfg->clkCtrl, &pclk);
if (err) {
return;
}

br = whal_Stm32wbSpi_CalcBr(pclk, comCfg->baud);

cpol = (comCfg->mode >> 1) & 1;
cpha = comCfg->mode & 1;

/* Disable SPE before reconfiguring */
whal_Reg_Update(reg->base, SSPI_CR1_REG, SSPI_CR1_SPE,
whal_SetBits(SSPI_CR1_SPE, 0));

/* Set mode and baud rate */
whal_Reg_Update(reg->base, SSPI_CR1_REG,
SSPI_CR1_CPOL | SSPI_CR1_CPHA | SSPI_CR1_BR,
whal_SetBits(SSPI_CR1_CPOL, cpol) |
whal_SetBits(SSPI_CR1_CPHA, cpha) |
whal_SetBits(SSPI_CR1_BR, br));

/* Re-enable SPE */
whal_Reg_Update(reg->base, SSPI_CR1_REG, SSPI_CR1_SPE,
whal_SetBits(SSPI_CR1_SPE, 1));
}

whal_Error whal_Stm32wbSpi_Init(whal_Spi *spiDev)
{
(void)spiDev;
whal_Error err;
whal_Stm32wbSpi_Cfg *cfg;
const whal_Regmap *reg;

if (!spiDev || !spiDev->cfg) {
return WHAL_EINVAL;
}

reg = &spiDev->regmap;
cfg = (whal_Stm32wbSpi_Cfg *)spiDev->cfg;

err = whal_Clock_Enable(cfg->clkCtrl, cfg->clk);
if (err != WHAL_SUCCESS) {
return err;
}

/* Master mode with software slave management */
whal_Reg_Update(reg->base, SSPI_CR1_REG,
SSPI_CR1_MSTR | SSPI_CR1_SSM | SSPI_CR1_SSI,
whal_SetBits(SSPI_CR1_MSTR, 1) |
whal_SetBits(SSPI_CR1_SSM, 1) |
whal_SetBits(SSPI_CR1_SSI, 1));

/* 8-bit data size and FIFO receive threshold for 8-bit */
whal_Reg_Update(reg->base, SSPI_CR2_REG,
SSPI_CR2_DS | SSPI_CR2_FRXTH,
whal_SetBits(SSPI_CR2_DS, SSPI_DS_8BIT) |
whal_SetBits(SSPI_CR2_FRXTH, 1));

Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SPI peripheral is not enabled during initialization. The SPE (SPI Enable) bit must be set for the SPI peripheral to function. Add a call to enable SPE after configuring CR1 and CR2 registers: whal_Reg_Update(reg->base, SSPI_CR1_REG, SSPI_CR1_SPE, whal_SetBits(SSPI_CR1_SPE, 1));

Suggested change
/* Enable SPI peripheral */
whal_Reg_Update(reg->base, SSPI_CR1_REG, SSPI_CR1_SPE,
whal_SetBits(SSPI_CR1_SPE, 1));

Copilot uses AI. Check for mistakes.
return WHAL_SUCCESS;
}

whal_Error whal_Stm32wbSpi_Deinit(whal_Spi *spiDev)
{
(void)spiDev;
whal_Error err;
const whal_Regmap *reg;
whal_Stm32wbSpi_Cfg *cfg;

if (!spiDev || !spiDev->cfg) {
return WHAL_EINVAL;
}

reg = &spiDev->regmap;
cfg = (whal_Stm32wbSpi_Cfg *)spiDev->cfg;

/* Disable SPI */
whal_Reg_Update(reg->base, SSPI_CR1_REG, SSPI_CR1_SPE,
whal_SetBits(SSPI_CR1_SPE, 0));

err = whal_Clock_Disable(cfg->clkCtrl, cfg->clk);
if (err) {
return err;
}

return WHAL_SUCCESS;
}

whal_Error whal_Stm32wbSpi_SendRecv(whal_Spi *spiDev, void *spiComCfg, const uint8_t *tx,
size_t txLen, uint8_t *rx, size_t rxLen)
{
(void)spiDev;
(void)spiComCfg;
(void)tx;
(void)txLen;
(void)rx;
(void)rxLen;
const whal_Regmap *reg;
whal_Stm32wbSpi_Cfg *cfg;
whal_Stm32wbSpi_ComCfg *comCfg;

if (!spiDev || !spiDev->cfg || !spiComCfg) {
return WHAL_EINVAL;
}

reg = &spiDev->regmap;
cfg = (whal_Stm32wbSpi_Cfg *)spiDev->cfg;
comCfg = (whal_Stm32wbSpi_ComCfg *)spiComCfg;
size_t totalLen = txLen > rxLen ? txLen : rxLen;
size_t status;
size_t d;

whal_Stm32wbSpi_ApplyComCfg(reg, cfg, comCfg);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SPI peripheral is reconfigured (mode and baud rate) on every call to SendRecv, which includes disabling and re-enabling SPE. This is inefficient for consecutive transactions with the same device. Consider caching the current configuration and only reconfiguring when it changes, or providing separate configuration and transfer functions to allow the user to configure once and transfer multiple times.

Copilot uses AI. Check for mistakes.

for (size_t i = 0; i < totalLen; i++) {
if (txLen && tx) {
/* Wait for TX buffer empty */
do {
whal_Reg_Get(reg->base, SSPI_SR_REG, SSPI_SR_TXE, &status);
} while (!status);

/* Write data or dummy byte */
uint8_t txByte = (i >= txLen) ? tx[txLen-1] : tx[i];
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for determining txByte is incorrect. When i >= txLen, the condition evaluates to true and accesses tx[txLen-1], but when i < txLen, it should use tx[i]. The condition should be reversed to: (i < txLen) ? tx[i] : tx[txLen-1]. Currently, this will always use the last byte of the transmit buffer instead of sending data sequentially.

Suggested change
uint8_t txByte = (i >= txLen) ? tx[txLen-1] : tx[i];
uint8_t txByte = (i < txLen) ? tx[i] : tx[txLen - 1];

Copilot uses AI. Check for mistakes.
*(volatile uint8_t *)(reg->base + SSPI_DR_REG) = txByte;
}

if (rxLen && rx) {
/* Wait for RX buffer not empty */
do {
whal_Reg_Get(reg->base, SSPI_SR_REG, SSPI_SR_RXNE, &status);
} while (!status);

/* Read received byte */
d = *(volatile uint8_t *)(reg->base + SSPI_DR_REG);
if (i < rxLen) {
rx[i] = (uint8_t)d;
}
}
}

/* Wait for not busy */
do {
whal_Reg_Get(reg->base, SSPI_SR_REG, SSPI_SR_BSY, &status);
} while (status);

return WHAL_SUCCESS;
}

whal_Error whal_Stm32wbSpi_Send(whal_Spi *spiDev, void *spiComCfg, const uint8_t *data,
size_t dataSz)
{
(void)spiDev;
(void)spiComCfg;
(void)data;
(void)dataSz;
return WHAL_SUCCESS;
return whal_Stm32wbSpi_SendRecv(spiDev, spiComCfg, data, dataSz, NULL, 0);
}

whal_Error whal_Stm32wbSpi_Recv(whal_Spi *spiDev, void *spiComCfg, uint8_t *data,
size_t dataSz)
{
(void)spiDev;
(void)spiComCfg;
(void)data;
(void)dataSz;
return WHAL_SUCCESS;
uint8_t dummyByte = 0xFF;
return whal_Stm32wbSpi_SendRecv(spiDev, spiComCfg, &dummyByte, 1, data, dataSz);
}

const whal_SpiDriver whal_Stm32wbSpi_Driver = {
Expand Down
3 changes: 2 additions & 1 deletion wolfHAL/gpio/stm32wb_gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ typedef struct {
*/
typedef struct {
whal_Clock *clkCtrl; /* Clock controller for enabling GPIO clock */
void *clk; /* Clock descriptor (whal_Stm32wbRcc_Clk) */
const void **clk; /* Array of clock descriptors */
size_t clkCount; /* Number of clock descriptors */

whal_Stm32wbGpio_PinCfg *pinCfg; /* Array of pin configurations */
size_t pinCount; /* Number of pins to configure */
Expand Down
Loading