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,226 @@
/*******************************************************************************
* @file dr_mem.c
* @brief Name-based Memory API Implementation
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2026-02-19
* @copyright (c) 2026 Medithings Inc. All rights reserved.
*
* @details Routes name-based read/write to FDS or W25Q32 backend.
*
* dr_memWrite("serial_no", data, 12)
* |
* v
* [Lookup Table] --> FDS (config_data_t memcpy + config_save)
* --> W25Q32 (w25q32_write at physical address)
******************************************************************************/
#include "dr_mem.h"
#include "fstorage.h"
#include "drivers/w25q32/w25q32.h"
#include "debug_print.h"
#include <string.h>
#include <stddef.h>
#include <stdio.h>
/*==============================================================================
* DEFAULT VALUES
*============================================================================*/
static const char dflt_hw_no[12] = "123456789012";
static const char dflt_serial[12] = "2025AAAAT001";
static const uint8_t dflt_passkey[6] = {'1','2','3','4','5','6'};
static const uint8_t dflt_bond_del = 1;
static const int8_t dflt_reset_st = 99;
static const uint8_t dflt_adc_cnt = 8;
static const uint16_t dflt_pd_delay = 8000;
static const uint16_t dflt_agc[48] = {
2048,2048,2048,2048,2048,2048,2048,2048,
2048,2048,2048,2048,2048,2048,2048,2048,
2048,2048,2048,2048,2048,2048,2048,2048,
2048,2048,2048,2048,2048,2048,2048,2048,
2048,2048,2048,2048,2048,2048,2048,2048,
2048,2048,2048,2048,2048,2048,2048,2048
};
static const uint32_t dflt_life_cycle = 0;
/*==============================================================================
* LOOKUP TABLE
*
* FDS entries: plaintext in config_data_t (internal flash, RBP protected)
* W25Q32 entries: direct physical address access
*============================================================================*/
static const dr_mem_entry_t mem_table[] = {
/* FDS entries (device config - internal flash, plaintext) */
{ "hw_no", MEM_BACKEND_FDS, offsetof(config_data_t, hw_no), 12, false, dflt_hw_no },
{ "serial_no", MEM_BACKEND_FDS, offsetof(config_data_t, serial_no), 12, false, dflt_serial },
{ "passkey", MEM_BACKEND_FDS, offsetof(config_data_t, static_passkey), 6, false, dflt_passkey },
{ "bond_delete", MEM_BACKEND_FDS, offsetof(config_data_t, bond_data_delete), 1, false, &dflt_bond_del },
{ "reset_status", MEM_BACKEND_FDS, offsetof(config_data_t, reset_status), 1, false, &dflt_reset_st },
{ "pd_adc_cnt", MEM_BACKEND_FDS, offsetof(config_data_t, pd_adc_cnt), 1, false, &dflt_adc_cnt },
{ "pd_delay", MEM_BACKEND_FDS, offsetof(config_data_t, pd_delay_us), 2, false, &dflt_pd_delay },
/* W25Q32 entries (calibration + data - external flash) */
{ "agc_gain", MEM_BACKEND_W25Q32, 0x000000, 96, false, dflt_agc },
{ "life_cycle", MEM_BACKEND_W25Q32, 0x000060, 4, false, &dflt_life_cycle },
};
#define MEM_TABLE_COUNT (sizeof(mem_table) / sizeof(mem_table[0]))
/*==============================================================================
* PRIVATE FUNCTIONS
*============================================================================*/
/**
* @brief Find entry by key name
*/
static const dr_mem_entry_t *find_entry(const char *key)
{
for (uint32_t i = 0; i < MEM_TABLE_COUNT; i++) {
if (strcmp(mem_table[i].key, key) == 0) {
return &mem_table[i];
}
}
return NULL;
}
/**
* @brief Write to FDS backend (config_data_t field + config_save)
* FDS stores plaintext - internal flash is RBP protected
*/
static ret_code_t fds_backend_write(const dr_mem_entry_t *entry,
const void *data, uint16_t len)
{
uint16_t copy_len = (len < entry->size) ? len : entry->size;
uint8_t *dst = (uint8_t *)&m_config + entry->offset;
DBG_PRINTF("[FDS_W] '%s' %uB offset=%u\r\n", entry->key, copy_len, entry->offset);
memcpy(dst, data, copy_len);
DBG_PRINTF("[FDS_W] memcpy OK, calling config_save\r\n");
config_save();
DBG_PRINTF("[FDS_W] config_save returned\r\n");
return NRF_SUCCESS;
}
/**
* @brief Read from FDS backend (config_data_t field)
*/
static ret_code_t fds_backend_read(const dr_mem_entry_t *entry,
void *data, uint16_t len)
{
uint16_t copy_len = (len < entry->size) ? len : entry->size;
uint8_t *src = (uint8_t *)&m_config + entry->offset;
memcpy(data, src, copy_len);
return NRF_SUCCESS;
}
/**
* @brief Write to W25Q32 backend
*/
static ret_code_t w25q_backend_write(const dr_mem_entry_t *entry,
const void *data, uint16_t len)
{
uint16_t copy_len = (len < entry->size) ? len : entry->size;
w25q32_write(entry->offset, (const uint8_t *)data, copy_len);
return NRF_SUCCESS;
}
/**
* @brief Read from W25Q32 backend
*/
static ret_code_t w25q_backend_read(const dr_mem_entry_t *entry,
void *data, uint16_t len)
{
uint16_t copy_len = (len < entry->size) ? len : entry->size;
w25q32_read(entry->offset, (uint8_t *)data, copy_len);
return NRF_SUCCESS;
}
/*==============================================================================
* PUBLIC API
*============================================================================*/
ret_code_t dr_memInit(void)
{
/* FDS is initialized in fs_storage_init() + config_load() (main.c) */
/* W25Q32 is initialized in w25q32_power_on() + w25q32_init() (main.c) */
printf("[DR_MEM] Init OK (FDS + W25Q32)\r\n");
return NRF_SUCCESS;
}
ret_code_t dr_memWrite(const char *key, const void *data, uint16_t len)
{
const dr_mem_entry_t *entry = find_entry(key);
if (entry == NULL) {
printf("[DR_MEM] Write ERR: key '%s' not found\r\n", key);
return NRF_ERROR_NOT_FOUND;
}
if (len > entry->size) {
printf("[DR_MEM] Write WARN: len %u > size %u for '%s'\r\n",
len, entry->size, key);
}
printf("[DR_MEM] W '%s' %uB -> %s\r\n", key, len,
entry->backend == MEM_BACKEND_FDS ? "FDS" : "W25Q32");
switch (entry->backend) {
case MEM_BACKEND_FDS:
return fds_backend_write(entry, data, len);
case MEM_BACKEND_W25Q32:
return w25q_backend_write(entry, data, len);
default:
return NRF_ERROR_NOT_SUPPORTED;
}
}
ret_code_t dr_memRead(const char *key, void *data, uint16_t len)
{
const dr_mem_entry_t *entry = find_entry(key);
if (entry == NULL) {
printf("[DR_MEM] Read ERR: key '%s' not found\r\n", key);
return NRF_ERROR_NOT_FOUND;
}
if (len > entry->size) {
printf("[DR_MEM] Read WARN: len %u > size %u for '%s'\r\n",
len, entry->size, key);
}
switch (entry->backend) {
case MEM_BACKEND_FDS:
return fds_backend_read(entry, data, len);
case MEM_BACKEND_W25Q32:
return w25q_backend_read(entry, data, len);
default:
return NRF_ERROR_NOT_SUPPORTED;
}
}
const dr_mem_entry_t *dr_memFind(const char *key)
{
return find_entry(key);
}
ret_code_t dr_memSetDefaults(void)
{
printf("[DR_MEM] Setting defaults (RAM only)\r\n");
/* FDS entries: write to m_config RAM only (no config_save)
* FDS flash defaults are handled by config_load() -> fds_default_value_set()
* W25Q32 is not initialized during boot */
for (uint32_t i = 0; i < MEM_TABLE_COUNT; i++) {
const dr_mem_entry_t *e = &mem_table[i];
if (e->default_val == NULL) continue;
if (e->backend == MEM_BACKEND_FDS) {
uint8_t *dst = (uint8_t *)&m_config + e->offset;
memcpy(dst, e->default_val, e->size);
}
printf("[DR_MEM] D '%s' %uB\r\n", e->key, e->size);
}
printf("[DR_MEM] Defaults applied (%u entries, RAM)\r\n", MEM_TABLE_COUNT);
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,88 @@
/*******************************************************************************
* @file dr_mem.h
* @brief Name-based Memory API (dr_memWrite / dr_memRead)
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2026-02-19
* @copyright (c) 2026 Medithings Inc. All rights reserved.
*
* @details Provides name-based memory access that automatically routes to
* the correct backend storage:
* - FDS (internal flash) for device config
* - W25Q32 (external flash) for calibration & data
*
* Usage:
* dr_memWrite("serial_no", data, 12);
* dr_memRead("serial_no", buf, 12);
******************************************************************************/
#ifndef DR_MEM_H
#define DR_MEM_H
#include <stdint.h>
#include <stdbool.h>
#include "sdk_errors.h"
/*==============================================================================
* BACKEND TYPES
*============================================================================*/
typedef enum {
MEM_BACKEND_FDS, /**< Internal Flash (FDS) - device config */
MEM_BACKEND_W25Q32 /**< External Flash (W25Q32RV) - calibration/data */
} mem_backend_t;
/*==============================================================================
* LOOKUP TABLE ENTRY
*============================================================================*/
typedef struct {
const char *key; /**< Name key (e.g., "hw_no", "agc_gain") */
mem_backend_t backend; /**< Storage backend */
uint32_t offset; /**< FDS: offsetof in config_data_t, W25Q32: address */
uint16_t size; /**< Data size in bytes */
bool encrypted; /**< AES-128 CBC encryption */
const void *default_val; /**< Default value (NULL = no default) */
} dr_mem_entry_t;
/*==============================================================================
* PUBLIC API
*============================================================================*/
/**
* @brief Initialize memory subsystem (FDS + W25Q32)
* @return NRF_SUCCESS on success
*/
ret_code_t dr_memInit(void);
/**
* @brief Write data by name
* @param key Name key (e.g., "serial_no", "agc_gain")
* @param data Data to write
* @param len Data length in bytes
* @return NRF_SUCCESS on success, NRF_ERROR_NOT_FOUND if key unknown
*/
ret_code_t dr_memWrite(const char *key, const void *data, uint16_t len);
/**
* @brief Read data by name
* @param key Name key (e.g., "serial_no", "agc_gain")
* @param data Output buffer
* @param len Buffer length in bytes
* @return NRF_SUCCESS on success, NRF_ERROR_NOT_FOUND if key unknown
*/
ret_code_t dr_memRead(const char *key, void *data, uint16_t len);
/**
* @brief Get entry info by name (for debugging)
* @param key Name key
* @return Pointer to entry, or NULL if not found
*/
const dr_mem_entry_t *dr_memFind(const char *key);
/**
* @brief Write default values to all entries (when hw_no is uninitialized)
* @return NRF_SUCCESS on success
*/
ret_code_t dr_memSetDefaults(void);
#endif /* DR_MEM_H */

View File

@@ -0,0 +1,69 @@
/*******************************************************************************
* @file mem_config.h
* @brief Memory Configuration - Select memory type
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-02-03
******************************************************************************/
#ifndef MEM_CONFIG_H
#define MEM_CONFIG_H
/*==============================================================================
* MEMORY TYPE SELECTION
* Enable only ONE option
*============================================================================*/
#define MEM_USE_EEPROM 0 /* I2C EEPROM (24LC64) */
#define MEM_USE_W25Q_FLASH 1 /* W25Q32RV SPI Flash */
#define MEM_USE_INTERNAL_FDS 0 /* Internal Flash (FDS) - for testing */
/*==============================================================================
* W25Q32RV SPI PIN CONFIGURATION
*============================================================================*/
#if MEM_USE_W25Q_FLASH
/* SPI Instance - use SPI2 (shared with ADC) */
#define MEM_SPI_INSTANCE 2
/* SPI Pins (shared bus) */
#define MEM_SPI_SCK_PIN 14 /* P0.14 */
#define MEM_SPI_MISO_PIN 15 /* P0.15 */
#define MEM_SPI_MOSI_PIN 16 /* P0.16 */
#define MEM_SPI_CS_PIN 24 /* P0.24 - Flash dedicated */
#endif /* MEM_USE_W25Q_FLASH */
/*==============================================================================
* MEMORY PARAMETERS
*============================================================================*/
#if MEM_USE_EEPROM
#define MEM_PAGE_SIZE 64
#define MEM_SECTOR_SIZE 64 /* EEPROM has no sector concept */
#define MEM_TOTAL_SIZE (8*1024) /* 8KB - 24LC64 */
#elif MEM_USE_W25Q_FLASH
#define MEM_PAGE_SIZE 256
#define MEM_SECTOR_SIZE 4096 /* 4KB */
#define MEM_TOTAL_SIZE (4*1024*1024) /* 4MB */
#elif MEM_USE_INTERNAL_FDS
#define MEM_PAGE_SIZE 256
#define MEM_SECTOR_SIZE 4096
#define MEM_TOTAL_SIZE (32*1024) /* ~32KB available */
#endif
/*==============================================================================
* ADDRESS MAP (same for all memory types)
*============================================================================*/
#define MEM_ADDR_HW_NO 0x0010 /* 12 bytes - HW Number */
#define MEM_ADDR_PASSKEY 0x0020 /* 6 bytes - BLE Passkey */
#define MEM_ADDR_SERIAL_NO 0x0030 /* 12 bytes - Serial Number */
#define MEM_ADDR_BOND_DELETE 0x0060 /* 1 byte - Bond delete flag */
#define MEM_ADDR_RESET_STATUS 0x0065 /* 1 byte - Reset status */
#define MEM_ADDR_PD_ADC_CNT 0x0070 /* 1 byte - ADC count */
#define MEM_ADDR_PD_DELAY 0x0080 /* 2 bytes - PD delay */
#define MEM_ADDR_LIFE_CYCLE 0x0090 /* 4 bytes - Life cycle counter */
#define MEM_ADDR_AGC_GAIN 0x0480 /* 96 bytes - AGC gain array */
#endif /* MEM_CONFIG_H */

View File

@@ -0,0 +1,429 @@
/*******************************************************************************
* @file mem_flash.c
* @brief W25Q32RV SPI Flash Driver Implementation
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-02-03
*
* @details W25Q32RV: 4MB Serial NOR Flash
* - SPI Mode 0/3
* - Page size: 256 bytes
* - Sector size: 4KB
* - Erase before write required
******************************************************************************/
#include "mem_hal.h"
#include "mem_config.h"
#if MEM_USE_W25Q_FLASH
#include <string.h>
#include <stdio.h>
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "app_error.h"
#include "../spi2_bus.h" /* Shared SPI2 bus */
/*==============================================================================
* W25Q32RV COMMANDS
*============================================================================*/
#define W25Q_CMD_WRITE_ENABLE 0x06
#define W25Q_CMD_WRITE_DISABLE 0x04
#define W25Q_CMD_READ_STATUS1 0x05
#define W25Q_CMD_READ_STATUS2 0x35
#define W25Q_CMD_WRITE_STATUS 0x01
#define W25Q_CMD_READ_DATA 0x03
#define W25Q_CMD_FAST_READ 0x0B
#define W25Q_CMD_PAGE_PROGRAM 0x02
#define W25Q_CMD_SECTOR_ERASE 0x20 /* 4KB */
#define W25Q_CMD_BLOCK_ERASE_32K 0x52 /* 32KB */
#define W25Q_CMD_BLOCK_ERASE_64K 0xD8 /* 64KB */
#define W25Q_CMD_CHIP_ERASE 0xC7
#define W25Q_CMD_POWER_DOWN 0xB9
#define W25Q_CMD_RELEASE_PD 0xAB
#define W25Q_CMD_DEVICE_ID 0x90
#define W25Q_CMD_JEDEC_ID 0x9F
#define W25Q_CMD_ENABLE_RESET 0x66
#define W25Q_CMD_RESET_DEVICE 0x99
/* Status Register-1 Bits */
#define W25Q_STATUS_BUSY 0x01
#define W25Q_STATUS_WEL 0x02
/* Device ID */
#define W25Q_MANUFACTURER_WINBOND 0xEF
#define W25Q_DEVICE_ID_32RV 0x4016
/*==============================================================================
* PRIVATE VARIABLES
*============================================================================*/
static bool m_initialized = false;
/* 4KB sector buffer for Read-Modify-Write */
static uint8_t m_sector_buffer[MEM_SECTOR_SIZE];
/*==============================================================================
* PRIVATE FUNCTIONS
*============================================================================*/
/**
* @brief Assert CS (active low) - Flash CS from shared bus
*/
static inline void cs_low(void)
{
nrf_gpio_pin_clear(SPI2_CS_FLASH);
}
/**
* @brief Deassert CS
*/
static inline void cs_high(void)
{
nrf_gpio_pin_set(SPI2_CS_FLASH);
}
/**
* @brief SPI transfer with CS control (blocking mode via shared bus)
*/
static ret_code_t spi_transfer(const uint8_t *tx_buf, uint8_t tx_len,
uint8_t *rx_buf, uint8_t rx_len)
{
printf("[FLASH] xfer tx=%d rx=%d...", tx_len, rx_len);
cs_low();
ret_code_t err = spi2_bus_transfer(tx_buf, tx_len, rx_buf, rx_len);
cs_high();
printf("done(%d)\r\n", err);
return err;
}
/**
* @brief Read Status Register-1
*/
static uint8_t w25q_read_status(void)
{
uint8_t cmd = W25Q_CMD_READ_STATUS1;
uint8_t rx[2];
cs_low();
spi2_bus_transfer(&cmd, 1, rx, 2);
cs_high();
return rx[1];
}
/**
* @brief Wait for BUSY flag to clear
*/
static void w25q_wait_busy(void)
{
uint32_t timeout = 100000; /* ~seconds */
while ((w25q_read_status() & W25Q_STATUS_BUSY) && timeout--)
{
nrf_delay_us(10);
}
}
/**
* @brief Send Write Enable command
*/
static void w25q_write_enable(void)
{
uint8_t cmd = W25Q_CMD_WRITE_ENABLE;
spi_transfer(&cmd, 1, NULL, 0);
}
/**
* @brief Read data from flash (blocking mode via shared bus)
*/
static ret_code_t w25q_read(uint32_t addr, uint8_t *buf, uint16_t len)
{
uint8_t cmd[4];
cmd[0] = W25Q_CMD_READ_DATA;
cmd[1] = (addr >> 16) & 0xFF;
cmd[2] = (addr >> 8) & 0xFF;
cmd[3] = addr & 0xFF;
cs_low();
/* Send command */
ret_code_t err = spi2_bus_transfer(cmd, 4, NULL, 0);
if (err != NRF_SUCCESS) { cs_high(); return err; }
/* Read data */
err = spi2_bus_transfer(NULL, 0, buf, len);
cs_high();
return err;
}
/**
* @brief Program a page (max 256 bytes, must not cross page boundary)
*/
static ret_code_t w25q_page_program(uint32_t addr, const uint8_t *buf, uint16_t len)
{
if (len > MEM_PAGE_SIZE) len = MEM_PAGE_SIZE;
w25q_write_enable();
uint8_t cmd[4];
cmd[0] = W25Q_CMD_PAGE_PROGRAM;
cmd[1] = (addr >> 16) & 0xFF;
cmd[2] = (addr >> 8) & 0xFF;
cmd[3] = addr & 0xFF;
cs_low();
/* Send command */
ret_code_t err = spi2_bus_transfer(cmd, 4, NULL, 0);
if (err != NRF_SUCCESS) { cs_high(); return err; }
/* Send data */
err = spi2_bus_transfer(buf, len, NULL, 0);
cs_high();
if (err == NRF_SUCCESS)
{
w25q_wait_busy(); /* Wait for program complete (~0.25ms typ) */
}
return err;
}
/**
* @brief Erase a 4KB sector
*/
static ret_code_t w25q_sector_erase(uint32_t addr)
{
uint32_t sector_addr = addr & ~(MEM_SECTOR_SIZE - 1);
w25q_write_enable();
uint8_t cmd[4];
cmd[0] = W25Q_CMD_SECTOR_ERASE;
cmd[1] = (sector_addr >> 16) & 0xFF;
cmd[2] = (sector_addr >> 8) & 0xFF;
cmd[3] = sector_addr & 0xFF;
spi_transfer(cmd, 4, NULL, 0);
w25q_wait_busy(); /* Wait for erase complete (~30ms typ, 240ms max) */
return NRF_SUCCESS;
}
/*==============================================================================
* PUBLIC FUNCTIONS - mem_hal.h IMPLEMENTATION
*============================================================================*/
ret_code_t mem_init(void)
{
ret_code_t err;
printf("[FLASH] Init start\r\n");
/* Ensure shared SPI2 bus is initialized */
if (!spi2_bus_is_initialized())
{
err = spi2_bus_init();
if (err != NRF_SUCCESS)
{
printf("[FLASH] SPI2 init FAIL: %d\r\n", err);
return err;
}
}
/* Check MISO pin state (should be high when idle) */
uint32_t miso_state = nrf_gpio_pin_read(SPI2_MISO_PIN);
printf("[FLASH] MISO(P0.%d)=%d\r\n", SPI2_MISO_PIN, miso_state);
cs_high(); /* Ensure Flash is deselected */
/* Release from power-down - try multiple times with longer delay */
printf("[FLASH] Wake up...\r\n");
uint8_t cmd = W25Q_CMD_RELEASE_PD;
for (int i = 0; i < 3; i++)
{
spi_transfer(&cmd, 1, NULL, 0);
nrf_delay_ms(1); /* Longer delay - 1ms */
}
/* Check MISO again after wake */
miso_state = nrf_gpio_pin_read(SPI2_MISO_PIN);
printf("[FLASH] MISO after wake=%d\r\n", miso_state);
m_initialized = true;
printf("[FLASH] Init OK\r\n");
return NRF_SUCCESS;
}
void mem_uninit(void)
{
if (m_initialized)
{
/* Enter power-down mode */
uint8_t cmd = W25Q_CMD_POWER_DOWN;
spi_transfer(&cmd, 1, NULL, 0);
/* Note: Don't uninit shared bus - other devices may still use it */
m_initialized = false;
}
}
bool mem_is_ready(void)
{
return m_initialized && !(w25q_read_status() & W25Q_STATUS_BUSY);
}
ret_code_t mem_read_byte(uint32_t addr, uint8_t *out)
{
return w25q_read(addr, out, 1);
}
ret_code_t mem_write_byte(uint32_t addr, uint8_t data)
{
return mem_write_bytes(addr, &data, 1);
}
ret_code_t mem_read_bytes(uint32_t addr, uint8_t *buf, uint16_t len)
{
return w25q_read(addr, buf, len);
}
ret_code_t mem_write_bytes(uint32_t addr, const uint8_t *buf, uint16_t len)
{
/*
* Flash write requires Read-Modify-Write:
* 1. Read entire sector
* 2. Modify data in buffer
* 3. Erase sector
* 4. Write back entire sector
*/
uint32_t sector_addr = addr & ~(MEM_SECTOR_SIZE - 1);
uint32_t offset = addr - sector_addr;
/* Check if data fits in one sector */
if (offset + len > MEM_SECTOR_SIZE)
{
/* TODO: Handle cross-sector writes */
return NRF_ERROR_INVALID_LENGTH;
}
/* 1. Read entire sector */
ret_code_t err = w25q_read(sector_addr, m_sector_buffer, MEM_SECTOR_SIZE);
if (err != NRF_SUCCESS) return err;
/* 2. Modify data */
memcpy(&m_sector_buffer[offset], buf, len);
/* 3. Erase sector */
err = w25q_sector_erase(sector_addr);
if (err != NRF_SUCCESS) return err;
/* 4. Write back page by page */
for (uint16_t i = 0; i < MEM_SECTOR_SIZE; i += MEM_PAGE_SIZE)
{
err = w25q_page_program(sector_addr + i, &m_sector_buffer[i], MEM_PAGE_SIZE);
if (err != NRF_SUCCESS) return err;
}
return NRF_SUCCESS;
}
ret_code_t mem_read_word(uint32_t addr, uint16_t *out)
{
uint8_t buf[2];
ret_code_t err = w25q_read(addr, buf, 2);
if (err == NRF_SUCCESS)
{
*out = buf[0] | (buf[1] << 8);
}
return err;
}
ret_code_t mem_write_word(uint32_t addr, uint16_t data)
{
uint8_t buf[2] = { data & 0xFF, (data >> 8) & 0xFF };
return mem_write_bytes(addr, buf, 2);
}
ret_code_t mem_read_uint32(uint32_t addr, uint32_t *out)
{
uint8_t buf[4];
ret_code_t err = w25q_read(addr, buf, 4);
if (err == NRF_SUCCESS)
{
*out = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
return err;
}
ret_code_t mem_write_uint32(uint32_t addr, uint32_t data)
{
uint8_t buf[4] = {
data & 0xFF,
(data >> 8) & 0xFF,
(data >> 16) & 0xFF,
(data >> 24) & 0xFF
};
return mem_write_bytes(addr, buf, 4);
}
ret_code_t mem_read_uint16_array(uint32_t addr, uint16_t *arr, uint16_t count)
{
return w25q_read(addr, (uint8_t *)arr, count * 2);
}
ret_code_t mem_write_uint16_array(uint32_t addr, const uint16_t *arr, uint16_t count)
{
return mem_write_bytes(addr, (const uint8_t *)arr, count * 2);
}
ret_code_t mem_read_decrypted(uint32_t addr, uint8_t *buf, uint16_t len)
{
/* TODO: Implement AES-128 CBC decryption */
/* For now, just read raw data */
return w25q_read(addr, buf, len);
}
ret_code_t mem_write_encrypted(uint32_t addr, const uint8_t *buf, uint16_t len)
{
/* TODO: Implement AES-128 CBC encryption */
/* For now, just write raw data */
return mem_write_bytes(addr, buf, len);
}
ret_code_t mem_erase_sector(uint32_t addr)
{
return w25q_sector_erase(addr);
}
ret_code_t mem_get_device_id(uint8_t *manufacturer_id, uint16_t *device_id)
{
uint8_t cmd = W25Q_CMD_JEDEC_ID;
uint8_t rx[4] = {0};
printf("[FLASH] Reading JEDEC ID...\r\n");
cs_low();
ret_code_t err = spi2_bus_transfer(&cmd, 1, rx, 4);
cs_high();
if (err != NRF_SUCCESS)
{
printf("[FLASH] JEDEC xfer err: %d\r\n", err);
return err;
}
/* rx[1] = Manufacturer ID (0xEF for Winbond) */
/* rx[2:3] = Device ID (0x4016 for W25Q32RV) */
*manufacturer_id = rx[1];
*device_id = (rx[2] << 8) | rx[3];
printf("[FLASH] JEDEC: MFR=0x%02X DEV=0x%04X\r\n", rx[1], (rx[2]<<8)|rx[3]);
return NRF_SUCCESS;
}
#endif /* MEM_USE_W25Q_FLASH */

View File

@@ -0,0 +1,137 @@
/*******************************************************************************
* @file mem_hal.c
* @brief Memory HAL - Common functions and driver selection
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-02-03
*
* @details This file provides common functionality that applies to all
* memory backends. Specific implementations are in:
* - mem_flash.c (W25Q32RV SPI Flash)
* - mem_eeprom.c (I2C EEPROM) - not yet implemented
******************************************************************************/
#include "mem_hal.h"
#include "mem_config.h"
#include <stdio.h>
/*==============================================================================
* DEBUG MACROS
*============================================================================*/
#define MEM_DEBUG 1
#if MEM_DEBUG
#define MEM_LOG(...) printf(__VA_ARGS__)
#else
#define MEM_LOG(...)
#endif
/*==============================================================================
* MEMORY TEST FUNCTION
*============================================================================*/
/**
* @brief Test memory read/write functionality
* @return NRF_SUCCESS if all tests pass
*/
ret_code_t mem_test(void)
{
ret_code_t err;
uint8_t manufacturer_id;
uint16_t device_id;
MEM_LOG("[MEM] Testing memory...\r\n");
#if MEM_USE_W25Q_FLASH
/* Read device ID */
err = mem_get_device_id(&manufacturer_id, &device_id);
if (err != NRF_SUCCESS)
{
MEM_LOG("[MEM] Failed to read device ID\r\n");
return err;
}
MEM_LOG("[MEM] Manufacturer: 0x%02X, Device: 0x%04X\r\n",
manufacturer_id, device_id);
/* Verify it's W25Q32RV */
if (manufacturer_id != 0xEF || device_id != 0x4016)
{
MEM_LOG("[MEM] WARNING: Unexpected device ID!\r\n");
}
#endif
/* Test read/write at address 0x0000 */
uint8_t test_data[4] = { 0x12, 0x34, 0x56, 0x78 };
uint8_t read_data[4] = { 0 };
MEM_LOG("[MEM] Writing test data...\r\n");
err = mem_write_bytes(0x0000, test_data, 4);
if (err != NRF_SUCCESS)
{
MEM_LOG("[MEM] Write failed: %d\r\n", err);
return err;
}
MEM_LOG("[MEM] Reading back...\r\n");
err = mem_read_bytes(0x0000, read_data, 4);
if (err != NRF_SUCCESS)
{
MEM_LOG("[MEM] Read failed: %d\r\n", err);
return err;
}
/* Verify */
bool match = true;
for (int i = 0; i < 4; i++)
{
if (test_data[i] != read_data[i])
{
match = false;
break;
}
}
if (match)
{
MEM_LOG("[MEM] Test PASSED\r\n");
return NRF_SUCCESS;
}
else
{
MEM_LOG("[MEM] Test FAILED: data mismatch\r\n");
MEM_LOG("[MEM] Wrote: %02X %02X %02X %02X\r\n",
test_data[0], test_data[1], test_data[2], test_data[3]);
MEM_LOG("[MEM] Read: %02X %02X %02X %02X\r\n",
read_data[0], read_data[1], read_data[2], read_data[3]);
return NRF_ERROR_INTERNAL;
}
}
/*==============================================================================
* STUB IMPLEMENTATIONS (when memory type not selected)
*============================================================================*/
#if !MEM_USE_W25Q_FLASH && !MEM_USE_EEPROM && !MEM_USE_INTERNAL_FDS
/* No memory backend selected - provide stub implementations */
ret_code_t mem_init(void) { return NRF_ERROR_NOT_SUPPORTED; }
void mem_uninit(void) { }
bool mem_is_ready(void) { return false; }
ret_code_t mem_read_byte(uint32_t addr, uint8_t *out) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_write_byte(uint32_t addr, uint8_t data) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_read_bytes(uint32_t addr, uint8_t *buf, uint16_t len) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_write_bytes(uint32_t addr, const uint8_t *buf, uint16_t len) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_read_word(uint32_t addr, uint16_t *out) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_write_word(uint32_t addr, uint16_t data) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_read_uint32(uint32_t addr, uint32_t *out) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_write_uint32(uint32_t addr, uint32_t data) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_read_uint16_array(uint32_t addr, uint16_t *arr, uint16_t count) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_write_uint16_array(uint32_t addr, const uint16_t *arr, uint16_t count) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_read_decrypted(uint32_t addr, uint8_t *buf, uint16_t len) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_write_encrypted(uint32_t addr, const uint8_t *buf, uint16_t len) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_erase_sector(uint32_t addr) { return NRF_ERROR_NOT_SUPPORTED; }
ret_code_t mem_get_device_id(uint8_t *manufacturer_id, uint16_t *device_id) { return NRF_ERROR_NOT_SUPPORTED; }
#endif

View File

@@ -0,0 +1,151 @@
/*******************************************************************************
* @file mem_hal.h
* @brief Memory Hardware Abstraction Layer Interface
* @author Charles KWON <charleskwon@medithings.co.kr>
* @date 2025-02-03
*
* @details Provides unified API for different memory types:
* - EEPROM (I2C)
* - W25Q32RV Flash (SPI)
* - Internal Flash (FDS)
******************************************************************************/
#ifndef MEM_HAL_H
#define MEM_HAL_H
#include <stdint.h>
#include <stdbool.h>
#include "sdk_errors.h"
#include "mem_config.h"
/*==============================================================================
* INITIALIZATION
*============================================================================*/
/**
* @brief Initialize memory subsystem
* @return NRF_SUCCESS on success
*/
ret_code_t mem_init(void);
/**
* @brief Uninitialize memory subsystem
*/
void mem_uninit(void);
/**
* @brief Check if memory is ready
* @return true if ready
*/
bool mem_is_ready(void);
/*==============================================================================
* BASIC READ/WRITE OPERATIONS
*============================================================================*/
/**
* @brief Read single byte
* @param addr Memory address
* @param out Output buffer
* @return NRF_SUCCESS on success
*/
ret_code_t mem_read_byte(uint32_t addr, uint8_t *out);
/**
* @brief Write single byte
* @param addr Memory address
* @param data Byte to write
* @return NRF_SUCCESS on success
*/
ret_code_t mem_write_byte(uint32_t addr, uint8_t data);
/**
* @brief Read multiple bytes
* @param addr Start address
* @param buf Output buffer
* @param len Number of bytes
* @return NRF_SUCCESS on success
*/
ret_code_t mem_read_bytes(uint32_t addr, uint8_t *buf, uint16_t len);
/**
* @brief Write multiple bytes
* @param addr Start address
* @param buf Data buffer
* @param len Number of bytes
* @return NRF_SUCCESS on success
*/
ret_code_t mem_write_bytes(uint32_t addr, const uint8_t *buf, uint16_t len);
/*==============================================================================
* WORD/DWORD OPERATIONS
*============================================================================*/
/**
* @brief Read 16-bit word
*/
ret_code_t mem_read_word(uint32_t addr, uint16_t *out);
/**
* @brief Write 16-bit word
*/
ret_code_t mem_write_word(uint32_t addr, uint16_t data);
/**
* @brief Read 32-bit value
*/
ret_code_t mem_read_uint32(uint32_t addr, uint32_t *out);
/**
* @brief Write 32-bit value
*/
ret_code_t mem_write_uint32(uint32_t addr, uint32_t data);
/*==============================================================================
* ARRAY OPERATIONS
*============================================================================*/
/**
* @brief Read array of 16-bit values
*/
ret_code_t mem_read_uint16_array(uint32_t addr, uint16_t *arr, uint16_t count);
/**
* @brief Write array of 16-bit values
*/
ret_code_t mem_write_uint16_array(uint32_t addr, const uint16_t *arr, uint16_t count);
/*==============================================================================
* ENCRYPTED OPERATIONS (AES-128 CBC)
*============================================================================*/
/**
* @brief Read and decrypt data
*/
ret_code_t mem_read_decrypted(uint32_t addr, uint8_t *buf, uint16_t len);
/**
* @brief Encrypt and write data
*/
ret_code_t mem_write_encrypted(uint32_t addr, const uint8_t *buf, uint16_t len);
/*==============================================================================
* FLASH-SPECIFIC OPERATIONS
*============================================================================*/
/**
* @brief Erase sector (Flash only, NOP for EEPROM)
* @param addr Any address within the sector
* @return NRF_SUCCESS on success
*/
ret_code_t mem_erase_sector(uint32_t addr);
/**
* @brief Get device ID (Flash only)
* @param manufacturer_id Output: Manufacturer ID
* @param device_id Output: Device ID
* @return NRF_SUCCESS on success
*/
ret_code_t mem_get_device_id(uint8_t *manufacturer_id, uint16_t *device_id);
#endif /* MEM_HAL_H */