diff --git a/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.c b/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.c index 0a0348a..59daa64 100644 --- a/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.c +++ b/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.c @@ -1,6 +1,6 @@ /******************************************************************************* * @file w25q32.c - * @brief W25Q32RV External SPI Flash Driver (Pure Bit-bang - bypass nrfx) + * @brief W25Q32RV External SPI Flash Driver (HW SPI via spi2_bus) ******************************************************************************/ #include "w25q32.h" @@ -8,6 +8,7 @@ #include "../../spi2_bus.h" #include "nrf_gpio.h" #include "nrf_delay.h" +#include /*============================================================================== * COMMANDS @@ -24,12 +25,23 @@ #define SR_BUSY 0x01 /*============================================================================== - * BIT-BANG SPI (bypass nrfx_spim completely) + * CS CONTROL (manual GPIO - P0.24) *============================================================================*/ static inline void cs_low(void) { nrf_gpio_pin_clear(W25Q_CS_PIN); } static inline void cs_high(void) { nrf_gpio_pin_set(W25Q_CS_PIN); } +/*============================================================================== + * SPI BUS HELPERS + *============================================================================*/ + +static void ensure_spi_ready(void) +{ + if (!spi2_bus_is_initialized()) { + spi2_bus_init(); + } +} + /*============================================================================== * POWER CONTROL - P0.01 + P0.11 both HIGH = W25Q32 power ON *============================================================================*/ @@ -51,104 +63,24 @@ void w25q32_power_off(void) DBG_PRINTF("[W25Q] Power OFF\r\n"); } -static void bb_spi_init(void) -{ - /* Step 1: Power ON W25Q32 */ - w25q32_power_on(); - - /* Step 2: Release nrfx_spim ownership of pins */ - if (spi2_bus_is_initialized()) { - DBG_PRINTF("[W25Q] Releasing SPI2 bus...\r\n"); - spi2_bus_uninit(); - nrf_delay_ms(1); - } - - /* Step 3: ADA2200 CS HIGH (deselect) */ - nrf_gpio_cfg_output(13); /* ADA2200 CS = P0.13 */ - nrf_gpio_pin_set(13); /* HIGH = deselected */ - DBG_PRINTF("[W25Q] ADA2200 CS(P0.13) = HIGH\r\n"); - - /* Step 4: Force pins back to GPIO mode */ - nrf_gpio_cfg_default(W25Q_SCK_PIN); - nrf_gpio_cfg_default(W25Q_MOSI_PIN); - nrf_gpio_cfg_default(W25Q_MISO_PIN); - nrf_gpio_cfg_default(W25Q_CS_PIN); - - /* Step 5: Configure bit-bang SPI with HIGH drive strength */ - nrf_gpio_cfg(W25Q_SCK_PIN, - NRF_GPIO_PIN_DIR_OUTPUT, - NRF_GPIO_PIN_INPUT_DISCONNECT, - NRF_GPIO_PIN_NOPULL, - NRF_GPIO_PIN_H0H1, - NRF_GPIO_PIN_NOSENSE); - - nrf_gpio_cfg(W25Q_MOSI_PIN, - NRF_GPIO_PIN_DIR_OUTPUT, - NRF_GPIO_PIN_INPUT_DISCONNECT, - NRF_GPIO_PIN_NOPULL, - NRF_GPIO_PIN_H0H1, - NRF_GPIO_PIN_NOSENSE); - - nrf_gpio_cfg_input(W25Q_MISO_PIN, NRF_GPIO_PIN_PULLUP); - - nrf_gpio_cfg(W25Q_CS_PIN, - NRF_GPIO_PIN_DIR_OUTPUT, - NRF_GPIO_PIN_INPUT_DISCONNECT, - NRF_GPIO_PIN_NOPULL, - NRF_GPIO_PIN_H0H1, - NRF_GPIO_PIN_NOSENSE); - - nrf_gpio_pin_clear(W25Q_SCK_PIN); /* Clock idle low (Mode 0) */ - nrf_gpio_pin_set(W25Q_CS_PIN); /* CS high (deselected) */ - - DBG_PRINTF("[W25Q] GPIO: SCK=%d MOSI=%d MISO=%d(dedicated) CS=%d\r\n", - W25Q_SCK_PIN, W25Q_MOSI_PIN, W25Q_MISO_PIN, W25Q_CS_PIN); - - /* Step 6: Check MISO state */ - int miso_state = nrf_gpio_pin_read(W25Q_MISO_PIN); - DBG_PRINTF("[W25Q] MISO(P0.%d) = %s\r\n", W25Q_MISO_PIN, miso_state ? "HIGH" : "LOW"); -} - -static uint8_t bb_spi_transfer_byte(uint8_t tx) -{ - uint8_t rx = 0; - for (int i = 7; i >= 0; i--) { - /* Set MOSI */ - if (tx & (1 << i)) { - nrf_gpio_pin_set(W25Q_MOSI_PIN); - } else { - nrf_gpio_pin_clear(W25Q_MOSI_PIN); - } - - /* Clock high - sample MISO */ - nrf_gpio_pin_set(W25Q_SCK_PIN); - __NOP(); __NOP(); __NOP(); __NOP(); - - if (nrf_gpio_pin_read(W25Q_MISO_PIN)) { - rx |= (1 << i); - } - - /* Clock low */ - nrf_gpio_pin_clear(W25Q_SCK_PIN); - __NOP(); __NOP(); __NOP(); __NOP(); - } - return rx; -} - /*============================================================================== * PRIVATE HELPERS *============================================================================*/ static uint8_t read_status(void) { - uint8_t status; + uint8_t tx[2] = { CMD_RDSR, 0xFF }; + uint8_t rx[2] = { 0 }; + ret_code_t err; cs_low(); - bb_spi_transfer_byte(CMD_RDSR); - status = bb_spi_transfer_byte(0xFF); + err = spi2_bus_transfer(tx, 2, rx, 2); cs_high(); - return status; + if (err != NRF_SUCCESS) { + DBG_PRINTF("[W25Q] read_status ERR=%d\r\n", err); + } + return rx[1]; } static void wait_ready(void) @@ -158,15 +90,29 @@ static void wait_ready(void) } } +static void write_enable(void) +{ + uint8_t cmd = CMD_WREN; + cs_low(); + spi2_bus_transfer(&cmd, 1, NULL, 0); + cs_high(); +} + /*============================================================================== * PUBLIC FUNCTIONS *============================================================================*/ bool w25q32_init(void) { - DBG_PRINTF("[W25Q] init (bit-bang)\r\n"); + DBG_PRINTF("[W25Q] init (HW SPI)\r\n"); - bb_spi_init(); + w25q32_power_on(); + + /* CS pin setup */ + nrf_gpio_cfg_output(W25Q_CS_PIN); + nrf_gpio_pin_set(W25Q_CS_PIN); + + ensure_spi_ready(); nrf_delay_ms(5); return w25q32_check_jedec(); @@ -174,81 +120,101 @@ bool w25q32_init(void) bool w25q32_check_jedec(void) { - uint8_t mfr, type, cap; + uint8_t tx[4] = { CMD_JEDEC_ID, 0xFF, 0xFF, 0xFF }; + uint8_t rx[4] = { 0 }; + + ensure_spi_ready(); cs_low(); - bb_spi_transfer_byte(CMD_JEDEC_ID); - mfr = bb_spi_transfer_byte(0xFF); - type = bb_spi_transfer_byte(0xFF); - cap = bb_spi_transfer_byte(0xFF); + spi2_bus_transfer(tx, 4, rx, 4); cs_high(); - DBG_PRINTF("[W25Q] JEDEC = %02X %02X %02X\r\n", mfr, type, cap); + /* rx[0] = dummy (during CMD), rx[1]=mfr, rx[2]=type, rx[3]=cap */ + DBG_PRINTF("[W25Q] JEDEC = %02X %02X %02X\r\n", rx[1], rx[2], rx[3]); /* Winbond = 0xEF, W25Q32 = 0x40 0x16 */ - return (mfr == 0xEF); + return (rx[1] == 0xEF); } void w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len) { - cs_low(); - bb_spi_transfer_byte(CMD_READ); - bb_spi_transfer_byte((uint8_t)(addr >> 16)); - bb_spi_transfer_byte((uint8_t)(addr >> 8)); - bb_spi_transfer_byte((uint8_t)(addr)); + uint8_t cmd[4]; - for (uint32_t i = 0; i < len; i++) { - buf[i] = bb_spi_transfer_byte(0xFF); + DBG_PRINTF("[W25Q] read addr=0x%06X len=%u\r\n", addr, len); + ensure_spi_ready(); + + cmd[0] = CMD_READ; + cmd[1] = (uint8_t)(addr >> 16); + cmd[2] = (uint8_t)(addr >> 8); + cmd[3] = (uint8_t)(addr); + + cs_low(); + /* Send command + address */ + spi2_bus_transfer(cmd, 4, NULL, 0); + /* Read data - send dummy bytes, receive into buf */ + while (len > 0) { + uint8_t chunk = (len > 255) ? 255 : (uint8_t)len; + uint8_t dummy[255]; + memset(dummy, 0xFF, chunk); + spi2_bus_transfer(dummy, chunk, buf, chunk); + buf += chunk; + len -= chunk; } cs_high(); } void w25q32_write(uint32_t addr, const uint8_t *buf, uint32_t len) { + uint8_t cmd[255]; + + DBG_PRINTF("[W25Q] write addr=0x%06X len=%u\r\n", addr, len); + ensure_spi_ready(); + while (len) { uint32_t page = 256 - (addr & 0xFF); if (page > len) page = len; + if (page > 251) page = 251; /* 4 (cmd+addr) + 251 = 255 max */ + + write_enable(); + + /* Build command: PP + 3-byte address + data */ + cmd[0] = CMD_PP; + cmd[1] = (uint8_t)(addr >> 16); + cmd[2] = (uint8_t)(addr >> 8); + cmd[3] = (uint8_t)(addr); + memcpy(&cmd[4], buf, page); - /* Write Enable */ cs_low(); - bb_spi_transfer_byte(CMD_WREN); - cs_high(); - - /* Page Program */ - cs_low(); - bb_spi_transfer_byte(CMD_PP); - bb_spi_transfer_byte((uint8_t)(addr >> 16)); - bb_spi_transfer_byte((uint8_t)(addr >> 8)); - bb_spi_transfer_byte((uint8_t)(addr)); - - for (uint32_t i = 0; i < page; i++) { - bb_spi_transfer_byte(buf[i]); - } + spi2_bus_transfer(cmd, (uint8_t)(4 + page), NULL, 0); cs_high(); wait_ready(); + DBG_PRINTF("[W25Q] page done %u bytes\r\n", page); addr += page; buf += page; len -= page; } + DBG_PRINTF("[W25Q] write OK\r\n"); } void w25q32_sector_erase(uint32_t addr) { + uint8_t cmd[4]; + + ensure_spi_ready(); + addr &= ~0xFFF; /* Align to 4KB sector */ - /* Write Enable */ - cs_low(); - bb_spi_transfer_byte(CMD_WREN); - cs_high(); + write_enable(); + + cmd[0] = CMD_SECTOR_ERASE; + cmd[1] = (uint8_t)(addr >> 16); + cmd[2] = (uint8_t)(addr >> 8); + cmd[3] = (uint8_t)(addr); - /* Sector Erase */ cs_low(); - bb_spi_transfer_byte(CMD_SECTOR_ERASE); - bb_spi_transfer_byte((uint8_t)(addr >> 16)); - bb_spi_transfer_byte((uint8_t)(addr >> 8)); - bb_spi_transfer_byte((uint8_t)(addr)); + spi2_bus_transfer(cmd, 4, NULL, 0); cs_high(); wait_ready(); @@ -256,16 +222,17 @@ void w25q32_sector_erase(uint32_t addr) void w25q32_chip_erase(void) { + uint8_t cmd; + + ensure_spi_ready(); + DBG_PRINTF("[W25Q] Chip erase...\r\n"); - /* Write Enable */ - cs_low(); - bb_spi_transfer_byte(CMD_WREN); - cs_high(); + write_enable(); - /* Chip Erase */ + cmd = CMD_CHIP_ERASE; cs_low(); - bb_spi_transfer_byte(CMD_CHIP_ERASE); + spi2_bus_transfer(&cmd, 1, NULL, 0); cs_high(); wait_ready(); diff --git a/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.h b/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.h index a0e58bc..ef1a8c3 100644 --- a/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.h +++ b/project/ble_peripheral/ble_app_vivaMayo/drivers/w25q32/w25q32.h @@ -13,10 +13,8 @@ * PIN CONFIGURATION - VivaMayo hardware *============================================================================*/ -#define W25Q_CS_PIN 24 /* P0.24 - Chip Select */ -#define W25Q_SCK_PIN 14 /* P0.14 - Clock (shared with ADA2200) */ -#define W25Q_MOSI_PIN 16 /* P0.16 - Master Out (shared with ADA2200) */ -#define W25Q_MISO_PIN 19 /* P0.19 - Master In (dedicated, avoid ADA2200 conflict) */ +#define W25Q_CS_PIN 24 /* P0.24 - Chip Select (GPIO manual control) */ +/* SCK/MOSI/MISO are shared with ADA2200 via spi2_bus (P0.14/P0.16/P0.15) */ /* Power control pins - both HIGH = W25Q32 power ON */ #define W25Q_PWR_PIN1 1 /* P0.01 - W25Q32 dedicated */