HW SPI 적용
- 기존 bit-bang 방식, SPI 핀(P0.14/15/16)이 하드웨어 SPIM 모드로 잡혀있어 GPIO 제어 불가 - CS만 GPIO로 분리 제어(ADA2200, W25Q32)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* @file w25q32.c
|
* @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"
|
#include "w25q32.h"
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "../../spi2_bus.h"
|
#include "../../spi2_bus.h"
|
||||||
#include "nrf_gpio.h"
|
#include "nrf_gpio.h"
|
||||||
#include "nrf_delay.h"
|
#include "nrf_delay.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* COMMANDS
|
* COMMANDS
|
||||||
@@ -24,12 +25,23 @@
|
|||||||
#define SR_BUSY 0x01
|
#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_low(void) { nrf_gpio_pin_clear(W25Q_CS_PIN); }
|
||||||
static inline void cs_high(void) { nrf_gpio_pin_set(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
|
* 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");
|
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
|
* PRIVATE HELPERS
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
static uint8_t read_status(void)
|
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();
|
cs_low();
|
||||||
bb_spi_transfer_byte(CMD_RDSR);
|
err = spi2_bus_transfer(tx, 2, rx, 2);
|
||||||
status = bb_spi_transfer_byte(0xFF);
|
|
||||||
cs_high();
|
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)
|
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
|
* PUBLIC FUNCTIONS
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
bool w25q32_init(void)
|
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);
|
nrf_delay_ms(5);
|
||||||
|
|
||||||
return w25q32_check_jedec();
|
return w25q32_check_jedec();
|
||||||
@@ -174,81 +120,101 @@ bool w25q32_init(void)
|
|||||||
|
|
||||||
bool w25q32_check_jedec(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();
|
cs_low();
|
||||||
bb_spi_transfer_byte(CMD_JEDEC_ID);
|
spi2_bus_transfer(tx, 4, rx, 4);
|
||||||
mfr = bb_spi_transfer_byte(0xFF);
|
|
||||||
type = bb_spi_transfer_byte(0xFF);
|
|
||||||
cap = bb_spi_transfer_byte(0xFF);
|
|
||||||
cs_high();
|
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 */
|
/* Winbond = 0xEF, W25Q32 = 0x40 0x16 */
|
||||||
return (mfr == 0xEF);
|
return (rx[1] == 0xEF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len)
|
void w25q32_read(uint32_t addr, uint8_t *buf, uint32_t len)
|
||||||
{
|
{
|
||||||
cs_low();
|
uint8_t cmd[4];
|
||||||
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++) {
|
DBG_PRINTF("[W25Q] read addr=0x%06X len=%u\r\n", addr, len);
|
||||||
buf[i] = bb_spi_transfer_byte(0xFF);
|
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();
|
cs_high();
|
||||||
}
|
}
|
||||||
|
|
||||||
void w25q32_write(uint32_t addr, const uint8_t *buf, uint32_t len)
|
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) {
|
while (len) {
|
||||||
uint32_t page = 256 - (addr & 0xFF);
|
uint32_t page = 256 - (addr & 0xFF);
|
||||||
if (page > len) page = len;
|
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();
|
cs_low();
|
||||||
bb_spi_transfer_byte(CMD_WREN);
|
spi2_bus_transfer(cmd, (uint8_t)(4 + page), NULL, 0);
|
||||||
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();
|
cs_high();
|
||||||
|
|
||||||
wait_ready();
|
wait_ready();
|
||||||
|
DBG_PRINTF("[W25Q] page done %u bytes\r\n", page);
|
||||||
|
|
||||||
addr += page;
|
addr += page;
|
||||||
buf += page;
|
buf += page;
|
||||||
len -= page;
|
len -= page;
|
||||||
}
|
}
|
||||||
|
DBG_PRINTF("[W25Q] write OK\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void w25q32_sector_erase(uint32_t addr)
|
void w25q32_sector_erase(uint32_t addr)
|
||||||
{
|
{
|
||||||
|
uint8_t cmd[4];
|
||||||
|
|
||||||
|
ensure_spi_ready();
|
||||||
|
|
||||||
addr &= ~0xFFF; /* Align to 4KB sector */
|
addr &= ~0xFFF; /* Align to 4KB sector */
|
||||||
|
|
||||||
/* Write Enable */
|
write_enable();
|
||||||
cs_low();
|
|
||||||
bb_spi_transfer_byte(CMD_WREN);
|
cmd[0] = CMD_SECTOR_ERASE;
|
||||||
cs_high();
|
cmd[1] = (uint8_t)(addr >> 16);
|
||||||
|
cmd[2] = (uint8_t)(addr >> 8);
|
||||||
|
cmd[3] = (uint8_t)(addr);
|
||||||
|
|
||||||
/* Sector Erase */
|
|
||||||
cs_low();
|
cs_low();
|
||||||
bb_spi_transfer_byte(CMD_SECTOR_ERASE);
|
spi2_bus_transfer(cmd, 4, NULL, 0);
|
||||||
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();
|
cs_high();
|
||||||
|
|
||||||
wait_ready();
|
wait_ready();
|
||||||
@@ -256,16 +222,17 @@ void w25q32_sector_erase(uint32_t addr)
|
|||||||
|
|
||||||
void w25q32_chip_erase(void)
|
void w25q32_chip_erase(void)
|
||||||
{
|
{
|
||||||
|
uint8_t cmd;
|
||||||
|
|
||||||
|
ensure_spi_ready();
|
||||||
|
|
||||||
DBG_PRINTF("[W25Q] Chip erase...\r\n");
|
DBG_PRINTF("[W25Q] Chip erase...\r\n");
|
||||||
|
|
||||||
/* Write Enable */
|
write_enable();
|
||||||
cs_low();
|
|
||||||
bb_spi_transfer_byte(CMD_WREN);
|
|
||||||
cs_high();
|
|
||||||
|
|
||||||
/* Chip Erase */
|
cmd = CMD_CHIP_ERASE;
|
||||||
cs_low();
|
cs_low();
|
||||||
bb_spi_transfer_byte(CMD_CHIP_ERASE);
|
spi2_bus_transfer(&cmd, 1, NULL, 0);
|
||||||
cs_high();
|
cs_high();
|
||||||
|
|
||||||
wait_ready();
|
wait_ready();
|
||||||
|
|||||||
@@ -13,10 +13,8 @@
|
|||||||
* PIN CONFIGURATION - VivaMayo hardware
|
* PIN CONFIGURATION - VivaMayo hardware
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
#define W25Q_CS_PIN 24 /* P0.24 - Chip Select */
|
#define W25Q_CS_PIN 24 /* P0.24 - Chip Select (GPIO manual control) */
|
||||||
#define W25Q_SCK_PIN 14 /* P0.14 - Clock (shared with ADA2200) */
|
/* SCK/MOSI/MISO are shared with ADA2200 via spi2_bus (P0.14/P0.16/P0.15) */
|
||||||
#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 */
|
/* Power control pins - both HIGH = W25Q32 power ON */
|
||||||
#define W25Q_PWR_PIN1 1 /* P0.01 - W25Q32 dedicated */
|
#define W25Q_PWR_PIN1 1 /* P0.01 - W25Q32 dedicated */
|
||||||
|
|||||||
Reference in New Issue
Block a user