/******************************************************************************* * @file dr_w25q32.h * @brief W25Q32 SPI Flash Driver for nRF52840 * 32Mbit (4MB) Serial NOR Flash Memory * @date 2026-03-12 * * @details Software SPI (bit-bang) driver for W25Q32RVXHJQ. * Shares SPI bus (SCK/MISO/MOSI) with ADC121S051. * Only CS pin is unique (P0.24). * * Pin connections: * nRF52840 W25Q32 (U16) * P0.24 -----> /CS (pin 1) * P0.15 <----- DO (pin 2, MISO) * P0.16 -----> DI (pin 5, MOSI) * P0.14 -----> CLK (pin 6) * * @note WP (pin 3) and HOLD (pin 7) are pulled HIGH via 10k resistors. ******************************************************************************/ #ifndef DR_W25Q32_H #define DR_W25Q32_H #include #include #include "nrf_gpio.h" /*============================================================================== * PIN CONFIGURATION *============================================================================*/ #define DR_FLASH_PIN_CS NRF_GPIO_PIN_MAP(0, 24) /* Chip Select - SPI_CS_FLASH(P0.24) */ #define DR_FLASH_PIN_SCLK NRF_GPIO_PIN_MAP(0, 14) /* Serial Clock(shared) - SPI_SCK(P0.14) */ #define DR_FLASH_PIN_MISO NRF_GPIO_PIN_MAP(0, 15) /* DI_(IO0)(shared) - SPI_MOSI(P0.15) */ #define DR_FLASH_PIN_MOSI NRF_GPIO_PIN_MAP(0, 16) /* DO_(IO1)(shared) - SPI_MISO(P0.16) */ /*============================================================================== * W25Q32 SPECIFICATIONS *============================================================================*/ #define DR_FLASH_PAGE_SIZE 256 /**< Page size (bytes) */ #define DR_FLASH_SECTOR_SIZE 4096 /**< Sector size (bytes, 4KB) */ #define DR_FLASH_BLOCK_32K (32*1024) /**< 32KB block */ #define DR_FLASH_BLOCK_64K (64*1024) /**< 64KB block */ #define DR_FLASH_TOTAL_SIZE (4*1024*1024) /**< 4MB total */ #define DR_FLASH_UID_LENGTH 8 /**< Unique ID length (bytes) */ /*============================================================================== * W25Q32 COMMAND OPCODES *============================================================================*/ #define DR_FLASH_CMD_WRITE_ENABLE 0x06 #define DR_FLASH_CMD_WRITE_DISABLE 0x04 #define DR_FLASH_CMD_READ_STATUS1 0x05 #define DR_FLASH_CMD_READ_STATUS2 0x35 #define DR_FLASH_CMD_WRITE_STATUS 0x01 #define DR_FLASH_CMD_READ_DATA 0x03 #define DR_FLASH_CMD_PAGE_PROGRAM 0x02 #define DR_FLASH_CMD_SECTOR_ERASE 0x20 /**< 4KB erase */ #define DR_FLASH_CMD_BLOCK_ERASE_32K 0x52 /**< 32KB erase */ #define DR_FLASH_CMD_BLOCK_ERASE_64K 0xD8 /**< 64KB erase */ #define DR_FLASH_CMD_CHIP_ERASE 0xC7 /**< Full chip erase */ #define DR_FLASH_CMD_POWER_DOWN 0xB9 /**< Deep power-down */ #define DR_FLASH_CMD_RELEASE_PD 0xAB /**< Release from deep power-down */ #define DR_FLASH_CMD_READ_UID 0x4B /**< Read unique ID */ #define DR_FLASH_CMD_READ_JEDEC_ID 0x9F /**< Read JEDEC ID */ #define DR_FLASH_CMD_READ_MFR_ID 0x90 /**< Read manufacturer/device ID */ /* Status Register 1 bits */ #define DR_FLASH_SR1_BUSY 0x01 /**< Erase/Write in progress */ #define DR_FLASH_SR1_WEL 0x02 /**< Write enable latch */ /*============================================================================== * ERROR CODES *============================================================================*/ typedef enum { DR_FLASH_OK = 0, DR_FLASH_ERR_NOT_INIT, DR_FLASH_ERR_INVALID_PARAM, DR_FLASH_ERR_TIMEOUT, DR_FLASH_ERR_WRITE_FAILED, DR_FLASH_ERR_JEDEC_MISMATCH } dr_flash_err_t; /*============================================================================== * DATA STRUCTURES *============================================================================*/ /** @brief JEDEC ID structure */ typedef struct { uint8_t manufacturer_id; /**< 0xEF = Winbond */ uint8_t memory_type; /**< 0x40 = SPI */ uint8_t capacity; /**< 0x16 = 32Mbit */ } dr_flash_jedec_t; /*============================================================================== * INITIALIZATION *============================================================================*/ /** * @brief Initialize W25Q32 Flash driver * @return dr_flash_err_t Error code * @note Sets up GPIO pins. Does NOT wake chip from deep power-down. */ dr_flash_err_t dr_w25q32_init(void); /** * @brief Uninitialize driver, release GPIO pins */ void dr_w25q32_uninit(void); /** * @brief Check if driver is initialized */ bool dr_w25q32_is_initialized(void); /*============================================================================== * IDENTIFICATION *============================================================================*/ /** * @brief Read JEDEC ID (manufacturer, type, capacity) * @param jedec Pointer to JEDEC ID structure * @return dr_flash_err_t Error code * @note Expected: manufacturer=0xEF, type=0x40, capacity=0x16 */ dr_flash_err_t dr_w25q32_read_jedec_id(dr_flash_jedec_t *jedec); /** * @brief Read 8-byte unique ID * @param uid Buffer to store 8-byte UID (must be >= 8 bytes) * @return dr_flash_err_t Error code */ dr_flash_err_t dr_w25q32_read_uid(uint8_t *uid); /*============================================================================== * READ *============================================================================*/ /** * @brief Read data from flash * @param addr 24-bit start address (0x000000 ~ 0x3FFFFF) * @param buf Buffer to store read data * @param len Number of bytes to read * @return dr_flash_err_t Error code */ dr_flash_err_t dr_w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len); /*============================================================================== * WRITE *============================================================================*/ /** * @brief Write data to flash (page program, max 256 bytes) * @param addr 24-bit start address (must be page-aligned for best results) * @param data Data to write * @param len Number of bytes (1~256, must not cross page boundary) * @return dr_flash_err_t Error code * @note Automatically sends Write Enable before programming. * Waits for completion (BUSY flag). */ dr_flash_err_t dr_w25q32_write(uint32_t addr, const uint8_t *data, uint32_t len); /*============================================================================== * ERASE *============================================================================*/ /** * @brief Erase 4KB sector * @param addr Any address within the sector to erase * @return dr_flash_err_t Error code * @note Typical 45ms, max 400ms. Waits for completion. */ dr_flash_err_t dr_w25q32_erase_sector(uint32_t addr); /** * @brief Erase 32KB block * @param addr Any address within the block * @return dr_flash_err_t Error code */ dr_flash_err_t dr_w25q32_erase_block_32k(uint32_t addr); /** * @brief Erase 64KB block * @param addr Any address within the block * @return dr_flash_err_t Error code */ dr_flash_err_t dr_w25q32_erase_block_64k(uint32_t addr); /** * @brief Erase entire chip * @return dr_flash_err_t Error code * @note Takes 6~50 seconds. Use with caution. */ dr_flash_err_t dr_w25q32_chip_erase(void); /*============================================================================== * POWER MANAGEMENT *============================================================================*/ /** * @brief Enter deep power-down mode (~1uA) * @return dr_flash_err_t Error code */ dr_flash_err_t dr_w25q32_deep_powerdown(void); /** * @brief Release from deep power-down * @return dr_flash_err_t Error code * @note Requires ~3us recovery time (handled internally) */ dr_flash_err_t dr_w25q32_wakeup(void); /*============================================================================== * STATUS *============================================================================*/ /** * @brief Read Status Register 1 * @param status Pointer to store status byte * @return dr_flash_err_t Error code */ dr_flash_err_t dr_w25q32_read_status(uint8_t *status); /** * @brief Check if flash is busy (erase/write in progress) * @return true if busy */ bool dr_w25q32_is_busy(void); /** * @brief Wait until flash is not busy * @param timeout_ms Maximum wait time in milliseconds * @return dr_flash_err_t DR_FLASH_OK or DR_FLASH_ERR_TIMEOUT */ dr_flash_err_t dr_w25q32_wait_busy(uint32_t timeout_ms); /*============================================================================== * DEBUG / TEST *============================================================================*/ /** * @brief Test flash communication by reading JEDEC ID * @return true if JEDEC ID matches W25Q32 (0xEF, 0x40, 0x16) */ bool dr_w25q32_test(void); #endif /* DR_W25Q32_H */