diff --git a/examples/stm32wb/stm32wb55xx_nucleo.c b/examples/stm32wb/stm32wb55xx_nucleo.c index bac51ff..7688f4f 100644 --- a/examples/stm32wb/stm32wb55xx_nucleo.c +++ b/examples/stm32wb/stm32wb55xx_nucleo.c @@ -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, @@ -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, }, }; @@ -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}, + }, +}; diff --git a/examples/stm32wb/stm32wb55xx_nucleo.h b/examples/stm32wb/stm32wb55xx_nucleo.h index 061ed7b..4a3e1d9 100644 --- a/examples/stm32wb/stm32wb55xx_nucleo.h +++ b/examples/stm32wb/stm32wb55xx_nucleo.h @@ -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. */ @@ -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 */ diff --git a/src/gpio/stm32wb_gpio.c b/src/gpio/stm32wb_gpio.c index 193c1d6..61cdd1c 100644 --- a/src/gpio/stm32wb_gpio.c +++ b/src/gpio/stm32wb_gpio.c @@ -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 */ @@ -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; diff --git a/src/spi/stm32wb_spi.c b/src/spi/stm32wb_spi.c index 4c1501d..83c69bb 100644 --- a/src/spi/stm32wb_spi.c +++ b/src/spi/stm32wb_spi.c @@ -1,56 +1,230 @@ #include #include #include +#include #include +#include +#include /* - * 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)); + 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); + + 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]; + *(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 = { diff --git a/wolfHAL/gpio/stm32wb_gpio.h b/wolfHAL/gpio/stm32wb_gpio.h index bfaa415..2ceb2da 100644 --- a/wolfHAL/gpio/stm32wb_gpio.h +++ b/wolfHAL/gpio/stm32wb_gpio.h @@ -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 */ diff --git a/wolfHAL/platform/st/stm32wb55xx.h b/wolfHAL/platform/st/stm32wb55xx.h index 5cae63b..c6a3c5f 100644 --- a/wolfHAL/platform/st/stm32wb55xx.h +++ b/wolfHAL/platform/st/stm32wb55xx.h @@ -6,6 +6,7 @@ #include #include #include +#include #include /* @@ -87,4 +88,8 @@ .regOffset = 0x60, \ .enableMask = (1 << 14) +#define WHAL_STM32WB55_SPI1_CLOCK \ + .regOffset = 0x60, \ + .enableMask = (1 << 12) + #endif /* WHAL_STM32WB55XX_H */ diff --git a/wolfHAL/spi/stm32wb_spi.h b/wolfHAL/spi/stm32wb_spi.h index b5f2b9c..5c70936 100644 --- a/wolfHAL/spi/stm32wb_spi.h +++ b/wolfHAL/spi/stm32wb_spi.h @@ -10,23 +10,39 @@ * @file stm32wb_spi.h * @brief STM32WB SPI driver configuration. * - * NOTE: This driver is currently a stub. Implementation pending. + * The STM32WB SPI peripheral provides: + * - Full-duplex synchronous serial communication + * - Master and slave modes (this driver supports master only) + * - Configurable clock polarity and phase (SPI modes 0-3) + * - Programmable baud rate prescaler (fPCLK/2 to fPCLK/256) + * - 4 to 16-bit data frame (this driver uses 8-bit) + * - Software slave management (chip select via GPIO) */ +/* + * @brief SPI clock polarity/phase mode selection. + */ +typedef enum { + WHAL_STM32WB_SPI_MODE_0, /* CPOL=0, CPHA=0 */ + WHAL_STM32WB_SPI_MODE_1, /* CPOL=0, CPHA=1 */ + WHAL_STM32WB_SPI_MODE_2, /* CPOL=1, CPHA=0 */ + WHAL_STM32WB_SPI_MODE_3, /* CPOL=1, CPHA=1 */ +} whal_Stm32wbSpi_Mode; + /* * @brief SPI device configuration. */ typedef struct whal_Stm32wbSpi_Cfg { - whal_Clock *sysClk; /* System clock for baud rate calculation */ + whal_Clock *clkCtrl; /* Clock controller for SPI peripheral clock */ + const void *clk; /* Clock descriptor */ } whal_Stm32wbSpi_Cfg; /* * @brief Per-transaction SPI communication parameters. */ typedef struct whal_Stm32wbSpi_ComCfg { - uint32_t mode; /* SPI mode (CPOL/CPHA) */ + uint32_t mode; /* SPI mode (WHAL_STM32WB_SPI_MODE_x) */ uint32_t baud; /* Baud rate in Hz */ - uint32_t chipSelect; /* Chip select configuration */ } whal_Stm32wbSpi_ComCfg; /*