存储设备 #

一、存储设备概述 #

1.1 存储设备分类 #

text
┌─────────────────────────────────────────────────────────┐
│                    存储设备分类                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  按存储介质分类:                                        │
│  ├── SD卡:大容量,可移动                               │
│  ├── SPI Flash:中等容量,速度快                        │
│  ├── EEPROM:小容量,可靠性高                           │
│  └── FRAM:非易失,高速写入                             │
│                                                         │
│  按接口分类:                                            │
│  ├── SDIO:SD卡原生接口                                 │
│  ├── SPI:通用串行接口                                  │
│  └── I2C:EEPROM常用接口                                │
│                                                         │
│  按容量分类:                                            │
│  ├── EEPROM:几KB - 几百KB                              │
│  ├── SPI Flash:几MB - 几十MB                           │
│  └── SD卡:几GB - 几百GB                                │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.2 存储设备对比 #

类型 容量 速度 接口 典型应用
EEPROM KB级 I2C 配置存储
SPI Flash MB级 SPI 固件存储
SD卡 GB级 中等 SDIO/SPI 数据记录
USB Flash GB级 USB 数据传输

二、SD卡存储 #

2.1 SD卡基础操作 #

Raspberry Pi上的SD卡通常作为系统盘使用,我们可以通过文件系统直接访问。

java
package com.example.storage;

import java.io.*;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ArrayList;

public class SDCardStorage {

    private final Path basePath;
    
    public SDCardStorage(String basePath) {
        this.basePath = Paths.get(basePath);
        
        if (!Files.exists(this.basePath)) {
            try {
                Files.createDirectories(this.basePath);
            } catch (IOException e) {
                throw new RuntimeException("无法创建存储目录", e);
            }
        }
    }
    
    public void writeFile(String filename, byte[] data) throws IOException {
        Path filePath = basePath.resolve(filename);
        Files.write(filePath, data);
    }
    
    public void writeFile(String filename, String content) throws IOException {
        Path filePath = basePath.resolve(filename);
        Files.write(filePath, content.getBytes());
    }
    
    public byte[] readFile(String filename) throws IOException {
        Path filePath = basePath.resolve(filename);
        return Files.readAllBytes(filePath);
    }
    
    public String readTextFile(String filename) throws IOException {
        Path filePath = basePath.resolve(filename);
        return new String(Files.readAllBytes(filePath));
    }
    
    public void appendFile(String filename, String content) throws IOException {
        Path filePath = basePath.resolve(filename);
        Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE, 
            StandardOpenOption.APPEND);
    }
    
    public void deleteFile(String filename) throws IOException {
        Path filePath = basePath.resolve(filename);
        Files.deleteIfExists(filePath);
    }
    
    public boolean exists(String filename) {
        return Files.exists(basePath.resolve(filename));
    }
    
    public List<String> listFiles() throws IOException {
        List<String> files = new ArrayList<>();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(basePath)) {
            for (Path path : stream) {
                files.add(path.getFileName().toString());
            }
        }
        return files;
    }
    
    public long getFileSize(String filename) throws IOException {
        Path filePath = basePath.resolve(filename);
        return Files.size(filePath);
    }
    
    public StorageInfo getStorageInfo() throws IOException {
        FileStore store = Files.getFileStore(basePath);
        
        long total = store.getTotalSpace();
        long free = store.getUsableSpace();
        long used = total - free;
        
        return new StorageInfo(total, used, free);
    }
    
    public static class StorageInfo {
        public final long totalBytes;
        public final long usedBytes;
        public final long freeBytes;
        
        public StorageInfo(long total, long used, long free) {
            this.totalBytes = total;
            this.usedBytes = used;
            this.freeBytes = free;
        }
        
        public double getUsedPercentage() {
            return (usedBytes * 100.0) / totalBytes;
        }
        
        @Override
        public String toString() {
            return String.format("Total: %.2fGB, Used: %.2fGB (%.1f%%), Free: %.2fGB",
                totalBytes / 1024.0 / 1024 / 1024,
                usedBytes / 1024.0 / 1024 / 1024,
                getUsedPercentage(),
                freeBytes / 1024.0 / 1024 / 1024);
        }
    }
    
    public static void main(String[] args) throws IOException {
        SDCardStorage storage = new SDCardStorage("/home/pi/data");
        
        System.out.println("SD卡存储测试");
        System.out.println("存储信息: " + storage.getStorageInfo());
        
        storage.writeFile("test.txt", "Hello SD Card!");
        System.out.println("写入文件: test.txt");
        
        String content = storage.readTextFile("test.txt");
        System.out.println("读取内容: " + content);
        
        System.out.println("文件列表: " + storage.listFiles());
        
        System.out.println("文件大小: " + storage.getFileSize("test.txt") + " bytes");
    }
}

2.2 数据记录器 #

java
package com.example.storage;

import java.io.*;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
import java.util.function.Supplier;

public class DataLogger {

    private final Path logPath;
    private final DateTimeFormatter formatter;
    private final ScheduledExecutorService scheduler;
    private final Supplier<String> dataSupplier;
    private ScheduledFuture<?> loggingTask;
    
    public DataLogger(String logDirectory, String filenamePrefix, 
                     Supplier<String> dataSupplier) {
        this.logPath = Paths.get(logDirectory);
        this.formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        this.dataSupplier = dataSupplier;
        this.scheduler = Executors.newSingleThreadScheduledExecutor();
        
        try {
            Files.createDirectories(logPath);
        } catch (IOException e) {
            throw new RuntimeException("无法创建日志目录", e);
        }
    }
    
    public void startLogging(int intervalMs) {
        if (loggingTask != null) {
            loggingTask.cancel(false);
        }
        
        loggingTask = scheduler.scheduleAtFixedRate(() -> {
            try {
                String timestamp = LocalDateTime.now().format(formatter);
                String data = dataSupplier.get();
                String logLine = timestamp + "," + data + "\n";
                
                String filename = "log_" + 
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + 
                    ".csv";
                
                Path filePath = logPath.resolve(filename);
                Files.write(filePath, logLine.getBytes(), 
                    StandardOpenOption.CREATE, StandardOpenOption.APPEND);
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 0, intervalMs, TimeUnit.MILLISECONDS);
    }
    
    public void stopLogging() {
        if (loggingTask != null) {
            loggingTask.cancel(false);
            loggingTask = null;
        }
    }
    
    public void logEvent(String event) throws IOException {
        String timestamp = LocalDateTime.now().format(formatter);
        String logLine = timestamp + ",EVENT," + event + "\n";
        
        String filename = "events_" + 
            LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + 
            ".log";
        
        Path filePath = logPath.resolve(filename);
        Files.write(filePath, logLine.getBytes(), 
            StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
    
    public void shutdown() {
        stopLogging();
        scheduler.shutdown();
    }
    
    public static void main(String[] args) throws InterruptedException {
        DataLogger logger = new DataLogger("/home/pi/logs", "sensor", 
            () -> String.format("%.2f,%.2f", Math.random() * 100, Math.random() * 50));
        
        System.out.println("数据记录器测试");
        
        logger.startLogging(1000);
        
        Thread.sleep(10000);
        
        logger.stopLogging();
        logger.shutdown();
        
        System.out.println("记录完成");
    }
}

2.3 循环缓冲文件 #

java
package com.example.storage;

import java.io.*;
import java.nio.file.*;
import java.util.concurrent.atomic.AtomicLong;

public class CircularFileBuffer {

    private final Path basePath;
    private final long maxFileSize;
    private final int maxFiles;
    private final AtomicLong currentSize = new AtomicLong(0);
    private Path currentFile;
    private int currentIndex = 0;
    
    public CircularFileBuffer(String basePath, long maxFileSize, int maxFiles) {
        this.basePath = Paths.get(basePath);
        this.maxFileSize = maxFileSize;
        this.maxFiles = maxFiles;
        
        try {
            Files.createDirectories(this.basePath);
            findCurrentFile();
        } catch (IOException e) {
            throw new RuntimeException("无法创建缓冲目录", e);
        }
    }
    
    private void findCurrentFile() throws IOException {
        for (int i = 0; i < maxFiles; i++) {
            Path file = basePath.resolve("buffer_" + i + ".dat");
            if (Files.exists(file)) {
                long size = Files.size(file);
                if (size < maxFileSize) {
                    currentFile = file;
                    currentIndex = i;
                    currentSize.set(size);
                    return;
                }
            }
        }
        
        rotateFile();
    }
    
    private void rotateFile() throws IOException {
        currentIndex = (currentIndex + 1) % maxFiles;
        currentFile = basePath.resolve("buffer_" + currentIndex + ".dat");
        
        Files.deleteIfExists(currentFile);
        Files.createFile(currentFile);
        currentSize.set(0);
    }
    
    public synchronized void write(byte[] data) throws IOException {
        if (currentSize.get() + data.length > maxFileSize) {
            rotateFile();
        }
        
        Files.write(currentFile, data, StandardOpenOption.APPEND);
        currentSize.addAndGet(data.length);
    }
    
    public synchronized void writeLine(String line) throws IOException {
        write((line + "\n").getBytes());
    }
    
    public byte[] readOldest() throws IOException {
        int oldestIndex = (currentIndex + 1) % maxFiles;
        Path oldestFile = basePath.resolve("buffer_" + oldestIndex + ".dat");
        
        if (Files.exists(oldestFile)) {
            return Files.readAllBytes(oldestFile);
        }
        return null;
    }
    
    public byte[] readCurrent() throws IOException {
        if (currentFile != null && Files.exists(currentFile)) {
            return Files.readAllBytes(currentFile);
        }
        return null;
    }
    
    public void clear() throws IOException {
        for (int i = 0; i < maxFiles; i++) {
            Path file = basePath.resolve("buffer_" + i + ".dat");
            Files.deleteIfExists(file);
        }
        rotateFile();
    }
    
    public static void main(String[] args) throws IOException, InterruptedException {
        CircularFileBuffer buffer = new CircularFileBuffer("/home/pi/circular", 
            1024 * 1024, 5);
        
        System.out.println("循环缓冲文件测试");
        
        for (int i = 0; i < 100; i++) {
            buffer.writeLine("Line " + i + ": " + System.currentTimeMillis());
            Thread.sleep(100);
        }
        
        System.out.println("当前文件内容:");
        byte[] current = buffer.readCurrent();
        if (current != null) {
            System.out.println(new String(current));
        }
    }
}

三、EEPROM存储 #

3.1 I2C EEPROM驱动 #

java
package com.example.storage;

import com.pi4j.Pi4J;
import com.pi4j.io.i2c.I2C;
import com.pi4j.io.i2c.I2CConfig;

public class I2CEEPROM {

    private static final int PAGE_SIZE = 64;
    private static final int WRITE_DELAY_MS = 5;
    
    private final I2C device;
    private final int capacity;
    
    public I2CEEPROM(var pi4j, int bus, int address, int capacity) {
        this.capacity = capacity;
        
        this.device = pi4j.create(I2C.newConfigBuilder(pi4j)
                .id("eeprom")
                .name("I2C EEPROM")
                .bus(bus)
                .device(address)
                .build());
    }
    
    public void writeByte(int address, byte data) throws InterruptedException {
        byte[] buffer = new byte[2];
        buffer[0] = (byte) ((address >> 8) & 0xFF);
        buffer[1] = (byte) (address & 0xFF);
        
        device.write(buffer);
        device.write(data);
        
        Thread.sleep(WRITE_DELAY_MS);
    }
    
    public byte readByte(int address) {
        byte[] addrBytes = {
            (byte) ((address >> 8) & 0xFF),
            (byte) (address & 0xFF)
        };
        
        device.write(addrBytes);
        return (byte) device.read();
    }
    
    public void write(int address, byte[] data) throws InterruptedException {
        int offset = 0;
        
        while (offset < data.length) {
            int pageStart = (address + offset) & ~(PAGE_SIZE - 1);
            int pageOffset = (address + offset) % PAGE_SIZE;
            int writeSize = Math.min(PAGE_SIZE - pageOffset, data.length - offset);
            
            byte[] buffer = new byte[writeSize + 2];
            buffer[0] = (byte) (((address + offset) >> 8) & 0xFF);
            buffer[1] = (byte) ((address + offset) & 0xFF);
            System.arraycopy(data, offset, buffer, 2, writeSize);
            
            device.write(buffer);
            Thread.sleep(WRITE_DELAY_MS);
            
            offset += writeSize;
        }
    }
    
    public byte[] read(int address, int length) {
        byte[] addrBytes = {
            (byte) ((address >> 8) & 0xFF),
            (byte) (address & 0xFF)
        };
        
        device.write(addrBytes);
        
        byte[] data = new byte[length];
        device.read(data, 0, length);
        return data;
    }
    
    public void writeString(int address, String str) throws InterruptedException {
        byte[] data = str.getBytes();
        byte[] withLength = new byte[data.length + 2];
        withLength[0] = (byte) ((data.length >> 8) & 0xFF);
        withLength[1] = (byte) (data.length & 0xFF);
        System.arraycopy(data, 0, withLength, 2, data.length);
        write(address, withLength);
    }
    
    public String readString(int address) {
        byte[] lengthBytes = read(address, 2);
        int length = ((lengthBytes[0] & 0xFF) << 8) | (lengthBytes[1] & 0xFF);
        
        if (length <= 0 || length > capacity - address - 2) {
            return null;
        }
        
        byte[] data = read(address + 2, length);
        return new String(data);
    }
    
    public void clear() throws InterruptedException {
        byte[] zeros = new byte[PAGE_SIZE];
        
        for (int address = 0; address < capacity; address += PAGE_SIZE) {
            write(address, zeros);
        }
    }
    
    public int getCapacity() {
        return capacity;
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        I2CEEPROM eeprom = new I2CEEPROM(pi4j, 1, 0x50, 32768);
        
        System.out.println("I2C EEPROM测试");
        System.out.println("容量: " + eeprom.getCapacity() + " bytes");
        
        eeprom.writeString(0, "Hello EEPROM!");
        System.out.println("写入字符串");
        
        String str = eeprom.readString(0);
        System.out.println("读取字符串: " + str);
        
        eeprom.writeByte(100, (byte) 0x55);
        byte b = eeprom.readByte(100);
        System.out.printf("写入/读取字节: 0x%02X%n", b);
        
        pi4j.shutdown();
    }
}

3.2 配置存储管理 #

java
package com.example.storage;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ConfigStorage {

    private final I2CEEPROM eeprom;
    private final Map<String, String> config = new ConcurrentHashMap<>();
    private static final int CONFIG_START = 0;
    private static final int CONFIG_MAX_SIZE = 4096;
    
    public ConfigStorage(I2CEEPROM eeprom) {
        this.eeprom = eeprom;
        load();
    }
    
    public void put(String key, String value) {
        config.put(key, value);
        save();
    }
    
    public String get(String key) {
        return config.get(key);
    }
    
    public String get(String key, String defaultValue) {
        return config.getOrDefault(key, defaultValue);
    }
    
    public int getInt(String key, int defaultValue) {
        String value = config.get(key);
        if (value != null) {
            try {
                return Integer.parseInt(value);
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    public double getDouble(String key, double defaultValue) {
        String value = config.get(key);
        if (value != null) {
            try {
                return Double.parseDouble(value);
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    public boolean getBoolean(String key, boolean defaultValue) {
        String value = config.get(key);
        if (value != null) {
            return Boolean.parseBoolean(value);
        }
        return defaultValue;
    }
    
    public void remove(String key) {
        config.remove(key);
        save();
    }
    
    public Set<String> keys() {
        return config.keySet();
    }
    
    public void clear() {
        config.clear();
        save();
    }
    
    private void save() {
        try {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : config.entrySet()) {
                sb.append(entry.getKey())
                  .append("=")
                  .append(entry.getValue().replace("\n", "\\n"))
                  .append("\n");
            }
            
            String content = sb.toString();
            if (content.length() > CONFIG_MAX_SIZE) {
                throw new RuntimeException("配置数据超出最大限制");
            }
            
            eeprom.writeString(CONFIG_START, content);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    private void load() {
        try {
            String content = eeprom.readString(CONFIG_START);
            if (content != null && !content.isEmpty()) {
                String[] lines = content.split("\n");
                for (String line : lines) {
                    int eqIndex = line.indexOf('=');
                    if (eqIndex > 0) {
                        String key = line.substring(0, eqIndex);
                        String value = line.substring(eqIndex + 1).replace("\\n", "\n");
                        config.put(key, value);
                    }
                }
            }
        } catch (Exception e) {
            // 首次使用,配置为空
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        I2CEEPROM eeprom = new I2CEEPROM(pi4j, 1, 0x50, 32768);
        ConfigStorage config = new ConfigStorage(eeprom);
        
        System.out.println("配置存储测试");
        
        config.put("device.name", "EmbeddedDevice01");
        config.put("device.interval", "1000");
        config.put("device.threshold", "25.5");
        config.put("device.enabled", "true");
        
        System.out.println("设备名称: " + config.get("device.name"));
        System.out.println("间隔: " + config.getInt("device.interval", 0));
        System.out.println("阈值: " + config.getDouble("device.threshold", 0));
        System.out.println("启用: " + config.getBoolean("device.enabled", false));
        
        pi4j.shutdown();
    }
}

四、SPI Flash存储 #

4.1 W25Q Flash驱动 #

java
package com.example.storage;

import com.pi4j.Pi4J;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiMode;

public class W25QFlash {

    private static final byte CMD_WRITE_ENABLE = 0x06;
    private static final byte CMD_WRITE_DISABLE = 0x04;
    private static final byte CMD_READ_STATUS1 = 0x05;
    private static final byte CMD_READ_STATUS2 = 0x35;
    private static final byte CMD_WRITE_STATUS = 0x01;
    private static final byte CMD_PAGE_PROGRAM = 0x02;
    private static final byte CMD_QUAD_PAGE_PROGRAM = 0x32;
    private static final byte CMD_BLOCK_ERASE_4K = 0x20;
    private static final byte CMD_BLOCK_ERASE_32K = 0x52;
    private static final byte CMD_BLOCK_ERASE_64K = (byte) 0xD8;
    private static final byte CMD_CHIP_ERASE = (byte) 0xC7;
    private static final byte CMD_READ_DATA = 0x03;
    private static final byte CMD_FAST_READ = 0x0B;
    private static final byte CMD_READ_JEDEC_ID = (byte) 0x9F;
    private static final byte CMD_POWER_DOWN = (byte) 0xB9;
    private static final byte CMD_RELEASE_POWER_DOWN = (byte) 0xAB;
    
    private static final int PAGE_SIZE = 256;
    private static final int SECTOR_SIZE = 4096;
    private static final int BLOCK_SIZE = 65536;
    
    private final Spi spi;
    
    public W25QFlash(var pi4j, int bus, int chipSelect) {
        this.spi = pi4j.create(Spi.newConfigBuilder(pi4j)
                .id("w25q")
                .name("W25Q SPI Flash")
                .bus(bus)
                .chipSelect(chipSelect)
                .mode(SpiMode.MODE_0)
                .baud(16000000)
                .build());
    }
    
    public int[] readJEDECId() {
        byte[] txData = {CMD_READ_JEDEC_ID, 0, 0, 0};
        byte[] rxData = spi.transfer(txData);
        
        return new int[]{rxData[1] & 0xFF, rxData[2] & 0xFF, rxData[3] & 0xFF};
    }
    
    public byte readStatus() {
        byte[] txData = {CMD_READ_STATUS1, 0};
        byte[] rxData = spi.transfer(txData);
        return rxData[1];
    }
    
    public boolean isBusy() {
        return (readStatus() & 0x01) != 0;
    }
    
    public void waitReady() {
        while (isBusy()) {
            try { Thread.sleep(1); } catch (InterruptedException e) {}
        }
    }
    
    public void writeEnable() {
        spi.transfer(CMD_WRITE_ENABLE);
    }
    
    public void writeDisable() {
        spi.transfer(CMD_WRITE_DISABLE);
    }
    
    public byte[] read(int address, int length) {
        byte[] txData = new byte[length + 4];
        txData[0] = CMD_READ_DATA;
        txData[1] = (byte) ((address >> 16) & 0xFF);
        txData[2] = (byte) ((address >> 8) & 0xFF);
        txData[3] = (byte) (address & 0xFF);
        
        byte[] rxData = spi.transfer(txData);
        
        byte[] result = new byte[length];
        System.arraycopy(rxData, 4, result, 0, length);
        return result;
    }
    
    public void pageProgram(int address, byte[] data) {
        if (data.length > PAGE_SIZE) {
            throw new IllegalArgumentException("数据长度超过页大小");
        }
        
        writeEnable();
        waitReady();
        
        byte[] txData = new byte[data.length + 4];
        txData[0] = CMD_PAGE_PROGRAM;
        txData[1] = (byte) ((address >> 16) & 0xFF);
        txData[2] = (byte) ((address >> 8) & 0xFF);
        txData[3] = (byte) (address & 0xFF);
        System.arraycopy(data, 0, txData, 4, data.length);
        
        spi.transfer(txData);
        waitReady();
    }
    
    public void write(int address, byte[] data) {
        int offset = 0;
        
        while (offset < data.length) {
            int pageRemaining = PAGE_SIZE - ((address + offset) % PAGE_SIZE);
            int writeSize = Math.min(pageRemaining, data.length - offset);
            
            byte[] pageData = new byte[writeSize];
            System.arraycopy(data, offset, pageData, 0, writeSize);
            
            pageProgram(address + offset, pageData);
            
            offset += writeSize;
        }
    }
    
    public void eraseSector(int address) {
        writeEnable();
        waitReady();
        
        byte[] txData = {
            CMD_BLOCK_ERASE_4K,
            (byte) ((address >> 16) & 0xFF),
            (byte) ((address >> 8) & 0xFF),
            (byte) (address & 0xFF)
        };
        
        spi.transfer(txData);
        waitReady();
    }
    
    public void eraseBlock(int address) {
        writeEnable();
        waitReady();
        
        byte[] txData = {
            CMD_BLOCK_ERASE_64K,
            (byte) ((address >> 16) & 0xFF),
            (byte) ((address >> 8) & 0xFF),
            (byte) (address & 0xFF)
        };
        
        spi.transfer(txData);
        waitReady();
    }
    
    public void chipErase() {
        writeEnable();
        waitReady();
        
        spi.transfer(CMD_CHIP_ERASE);
        waitReady();
    }
    
    public void powerDown() {
        spi.transfer(CMD_POWER_DOWN);
    }
    
    public void powerUp() {
        spi.transfer(CMD_RELEASE_POWER_DOWN);
        try { Thread.sleep(3); } catch (InterruptedException e) {}
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        W25QFlash flash = new W25QFlash(pi4j, 0, 0);
        
        System.out.println("W25Q SPI Flash测试");
        
        int[] jedec = flash.readJEDECId();
        System.out.printf("JEDEC ID: %02X %02X %02X%n", jedec[0], jedec[1], jedec[2]);
        
        byte[] writeData = "Hello SPI Flash!".getBytes();
        
        flash.eraseSector(0);
        flash.write(0, writeData);
        System.out.println("写入数据: " + new String(writeData));
        
        byte[] readData = flash.read(0, writeData.length);
        System.out.println("读取数据: " + new String(readData));
        
        pi4j.shutdown();
    }
}

五、存储管理器 #

5.1 统一存储接口 #

java
package com.example.storage;

import java.io.IOException;

public interface Storage {
    
    void write(int address, byte[] data) throws IOException, InterruptedException;
    
    byte[] read(int address, int length) throws IOException;
    
    void erase(int address, int length) throws IOException, InterruptedException;
    
    int getCapacity();
    
    String getName();
}

5.2 存储管理器 #

java
package com.example.storage;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class StorageManager {

    private final Map<String, Storage> storages = new ConcurrentHashMap<>();
    
    public void register(String name, Storage storage) {
        storages.put(name, storage);
    }
    
    public Storage get(String name) {
        return storages.get(name);
    }
    
    public void write(String storageName, int address, byte[] data) 
            throws IOException, InterruptedException {
        Storage storage = storages.get(storageName);
        if (storage != null) {
            storage.write(address, data);
        }
    }
    
    public byte[] read(String storageName, int address, int length) throws IOException {
        Storage storage = storages.get(storageName);
        if (storage != null) {
            return storage.read(address, length);
        }
        return null;
    }
    
    public Map<String, Integer> getCapacities() {
        Map<String, Integer> capacities = new ConcurrentHashMap<>();
        storages.forEach((name, storage) -> capacities.put(name, storage.getCapacity()));
        return capacities;
    }
}

六、总结 #

存储设备驱动要点:

  1. 接口选择:根据容量和速度需求选择合适的存储介质
  2. 写入保护:注意Flash写入前需要擦除
  3. 页对齐:Flash写入需要按页对齐
  4. 数据校验:添加CRC校验确保数据完整性
  5. 磨损均衡:对于频繁写入的场景实现磨损均衡

下一章我们将学习网络通信,实现设备间的数据传输。

最后更新:2026-03-27