initial commit

This commit is contained in:
jhChun
2026-04-08 16:58:54 +09:00
commit 82e33d8bf9
2578 changed files with 1590432 additions and 0 deletions

View File

@@ -0,0 +1,274 @@
/*******************************************************************************
* @file w25q32.c
* @brief W25Q32RV External SPI Flash Driver (Pure Bit-bang - bypass nrfx)
******************************************************************************/
#include "w25q32.h"
#include "../../debug_print.h"
#include "../../spi2_bus.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
/*==============================================================================
* COMMANDS
*============================================================================*/
#define CMD_JEDEC_ID 0x9F
#define CMD_READ 0x03
#define CMD_WREN 0x06
#define CMD_PP 0x02
#define CMD_SECTOR_ERASE 0x20
#define CMD_CHIP_ERASE 0xC7
#define CMD_RDSR 0x05
#define SR_BUSY 0x01
/*==============================================================================
* BIT-BANG SPI (bypass nrfx_spim completely)
*============================================================================*/
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); }
/*==============================================================================
* POWER CONTROL - P0.01 + P0.11 both HIGH = W25Q32 power ON
*============================================================================*/
void w25q32_power_on(void)
{
nrf_gpio_cfg_output(W25Q_PWR_PIN1);
nrf_gpio_cfg_output(W25Q_PWR_PIN2);
nrf_gpio_pin_set(W25Q_PWR_PIN1); /* P0.01 HIGH */
nrf_gpio_pin_set(W25Q_PWR_PIN2); /* P0.11 HIGH */
nrf_delay_ms(10); /* Wait for power stabilization */
DBG_PRINTF("[W25Q] Power ON (P0.%d=H, P0.%d=H)\r\n", W25Q_PWR_PIN1, W25Q_PWR_PIN2);
}
void w25q32_power_off(void)
{
nrf_gpio_pin_clear(W25Q_PWR_PIN1);
nrf_gpio_pin_clear(W25Q_PWR_PIN2);
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;
cs_low();
bb_spi_transfer_byte(CMD_RDSR);
status = bb_spi_transfer_byte(0xFF);
cs_high();
return status;
}
static void wait_ready(void)
{
while (read_status() & SR_BUSY) {
nrf_delay_ms(1);
}
}
/*==============================================================================
* PUBLIC FUNCTIONS
*============================================================================*/
bool w25q32_init(void)
{
DBG_PRINTF("[W25Q] init (bit-bang)\r\n");
bb_spi_init();
nrf_delay_ms(5);
return w25q32_check_jedec();
}
bool w25q32_check_jedec(void)
{
uint8_t mfr, type, cap;
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);
cs_high();
DBG_PRINTF("[W25Q] JEDEC = %02X %02X %02X\r\n", mfr, type, cap);
/* Winbond = 0xEF, W25Q32 = 0x40 0x16 */
return (mfr == 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));
for (uint32_t i = 0; i < len; i++) {
buf[i] = bb_spi_transfer_byte(0xFF);
}
cs_high();
}
void w25q32_write(uint32_t addr, const uint8_t *buf, uint32_t len)
{
while (len) {
uint32_t page = 256 - (addr & 0xFF);
if (page > len) page = len;
/* 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]);
}
cs_high();
wait_ready();
addr += page;
buf += page;
len -= page;
}
}
void w25q32_sector_erase(uint32_t addr)
{
addr &= ~0xFFF; /* Align to 4KB sector */
/* Write Enable */
cs_low();
bb_spi_transfer_byte(CMD_WREN);
cs_high();
/* 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));
cs_high();
wait_ready();
}
void w25q32_chip_erase(void)
{
DBG_PRINTF("[W25Q] Chip erase...\r\n");
/* Write Enable */
cs_low();
bb_spi_transfer_byte(CMD_WREN);
cs_high();
/* Chip Erase */
cs_low();
bb_spi_transfer_byte(CMD_CHIP_ERASE);
cs_high();
wait_ready();
DBG_PRINTF("[W25Q] Erase done\r\n");
}

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* @file w25q32.h
* @brief W25Q32RV External SPI Flash Driver (Simplified)
******************************************************************************/
#ifndef W25Q32_H
#define W25Q32_H
#include <stdbool.h>
#include <stdint.h>
/*==============================================================================
* 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) */
/* Power control pins - both HIGH = W25Q32 power ON */
#define W25Q_PWR_PIN1 1 /* P0.01 - W25Q32 dedicated */
#define W25Q_PWR_PIN2 11 /* P0.11 - W25Q32 dedicated */
/*==============================================================================
* API FUNCTIONS
*============================================================================*/
void w25q32_power_on(void);
void w25q32_power_off(void);
bool w25q32_init(void);
bool w25q32_check_jedec(void);
#define w25q32_check_id w25q32_check_jedec /* Legacy alias */
void w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len);
void w25q32_write(uint32_t addr, const uint8_t *buf, uint32_t len);
void w25q32_sector_erase(uint32_t addr);
void w25q32_chip_erase(void);
#endif /* W25Q32_H */