在嵌入式开发中,存储扩展一直是个绕不开的话题。最近我在一个物联网项目中遇到了需要高效存取大量传感器数据的场景,SD卡自然成了首选方案。PlatformIO作为嵌入式开发的利器,其SD_MMC库提供了比传统SD库更高效的访问方式,实测写入速度能提升3-5倍。本文将分享如何用PlatformIO的SD_MMC库实现SD卡的高速读写,包含从硬件连接到文件操作的全流程。
注意:SD_MMC仅支持4线模式,需要确认你的开发板支持SDMMC硬件接口(如ESP32的GPIO6-11)
选择SD卡模块时要注意:
以ESP32为例,标准接线方式如下:
| SD卡引脚 | ESP32引脚 | 备注 |
|---|---|---|
| CLK | GPIO14 | 时钟线 |
| CMD | GPIO15 | 命令线 |
| D0 | GPIO2 | 数据线0(必须连接) |
| D1 | GPIO4 | 数据线1(可选) |
| D2 | GPIO12 | 数据线2(可选) |
| D3 | GPIO13 | 数据线3(可选) |
实测发现:仅连接D0时速度为20MHz,四线全接可达40MHz
在platformio.ini中添加依赖:
ini复制[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
esphome/ESP32 SD_MMC Library@^1.1.0
创建基础读写类:
cpp复制#include <SD_MMC.h>
class SDCardManager {
public:
bool begin() {
return SD_MMC.begin("/sdcard", true); // 1-bit模式设为false
}
void listDir(const char * path) {
File root = SD_MMC.open(path);
while(File file = root.openNextFile()) {
Serial.printf("%s (%d bytes)\n", file.name(), file.size());
}
}
};
采用缓冲写入策略提升速度:
cpp复制void writeLog(const char* path, const char* message) {
File file = SD_MMC.open(path, FILE_APPEND);
if(!file) return;
// 512字节缓冲优化
static char buffer[512];
static size_t buf_pos = 0;
size_t msg_len = strlen(message);
if(buf_pos + msg_len >= sizeof(buffer)) {
file.write((uint8_t*)buffer, buf_pos);
buf_pos = 0;
}
memcpy(buffer + buf_pos, message, msg_len);
buf_pos += msg_len;
file.close(); // 实际项目建议定时flush
}
实现结构体存储示例:
cpp复制struct SensorData {
float temperature;
float humidity;
uint32_t timestamp;
};
void saveData(const SensorData& data) {
File file = SD_MMC.open("/data/log.bin", FILE_APPEND);
file.write((const uint8_t*)&data, sizeof(data));
file.close();
}
测试条件:ESP32-WROOM-32D, 16GB SanDisk Ultra TF卡
| 模式 | 写入速度 | 读取速度 |
|---|---|---|
| SPI 1线 | 450KB/s | 680KB/s |
| SDMMC 1线 | 1.2MB/s | 1.8MB/s |
| SDMMC 4线 | 2.7MB/s | 3.5MB/s |
cpp复制if(!SD_MMC.begin()) {
Serial.println("挂载失败,可能原因:");
if(SD_MMC.cardType() == CARD_NONE) {
Serial.println("- 卡未插入或接触不良");
}
if(SD_MMC.cardType() == CARD_MMC) {
Serial.println("- 卡不支持SDHC协议");
}
// 检查接线是否正确
}
cpp复制void checkCard() {
static bool lastState = true;
bool currentState = (digitalRead(CD_PIN) == HIGH);
if(lastState && !currentState) {
Serial.println("SD卡被拔出!");
SD_MMC.end();
}
lastState = currentState;
}
实现文件变化监听:
cpp复制void watchFileChanges() {
time_t lastMod = 0;
while(true) {
File f = SD_MMC.open("/config.json");
time_t currMod = f.getLastWrite();
if(currMod != lastMod) {
Serial.println("配置文件已更新");
lastMod = currMod;
}
delay(1000);
}
}
采用以下策略防止数据损坏:
示例代码:
cpp复制void safeWrite(const char* path, const char* data) {
// 先写临时文件
File tmp = SD_MMC.open("/temp.tmp", FILE_WRITE);
tmp.write(data);
tmp.flush();
// 原子操作重命名
SD_MMC.rename("/temp.tmp", path);
}
在实际物联网项目中,建议采用以下架构:
文件命名示例:
cpp复制String getLogFileName() {
time_t now;
time(&now);
struct tm *tm = localtime(&now);
char buf[32];
strftime(buf, sizeof(buf), "/log/%Y-%m-%d_%H.csv", tm);
return String(buf);
}
我在实际项目中发现,SD卡在长时间写入后会出现速度下降现象。通过定期(每24小时)重新挂载文件系统可恢复初始性能。另外建议选择工业级SD卡,普通消费级卡在高温环境下容易出现数据错误。