1. 项目概述:当铁电存储器遇上U盘
去年在做一个工业数据采集项目时,遇到了一个头疼的问题:传统U盘在频繁小文件写入时,不仅速度下降明显,而且Flash芯片的擦写寿命成了瓶颈。当时就想,如果用铁电存储器(FRAM)来做存储介质会怎样?于是有了这个"铁电U盘"的实验项目。
这个方案采用STM32F411RET6作为主控,搭配两颗富士通的MB85RS2MT铁电存储器(共4MB容量),实现了支持USB大容量存储设备(MSC)协议的U盘。相比传统方案,最大特点是:
- 写入速度恒定(不受碎片化影响)
- 理论擦写次数达10万亿次
- 掉电数据保持10年以上
- 单字节可编程(无需擦除)
注意:虽然FRAM性能优异,但成本是普通Flash的5-8倍,更适合特定场景而非普通消费级应用
2. 硬件设计关键点
2.1 主控选型考量
STM32F411RET6的选取基于三点:
- 自带USB 2.0全速控制器(12Mbps)
- 144MHz主频足够处理USB协议栈
- 丰富的SPI接口(驱动双FRAM需占用两个SPI)
实际测试发现,使用DMA+SPI时钟分频到18MHz时,传输稳定性最佳。硬件连接示意图:
| 主控引脚 | FRAM1连接 | FRAM2连接 |
|---|---|---|
| SPI1_SCK | SCK | - |
| SPI1_MISO | MISO | - |
| SPI1_MOSI | MOSI | - |
| PA4 | SS | - |
| SPI2_SCK | - | SCK |
| SPI2_MISO | - | MISO |
| SPI2_MOSI | - | MOSI |
| PB12 | - | SS |
2.2 铁电存储器的特殊处理
MB85RS2MT虽然接口标准与SPI Flash兼容,但有三个关键差异:
- 写保护机制:WP引脚必须接高电平,否则无法写入
- 时序要求:连续写入时,字节间隔需>15ns
- 功耗管理:深度休眠模式电流仅1μA,但唤醒需要5ms延时
我们在PCB布局时特别注意:
- 每颗FRAM的VCC引脚添加0.1μF+10μF去耦电容
- SPI时钟线做等长处理(误差<50ps)
- 采用菊花链方式连接两颗FRAM的HOLD引脚
3. 固件实现详解
3.1 USB MSC协议适配
使用STM32CubeMX生成基础框架后,需要修改这些关键部分:
c复制// 在usbd_storage_if.c中实现以下回调
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) {
FRAM_ReadMulti(blk_addr * BLOCK_SIZE, buf, blk_len * BLOCK_SIZE);
return 0;
}
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) {
FRAM_WriteMulti(blk_addr * BLOCK_SIZE, buf, blk_len * BLOCK_SIZE);
return 0;
}
其中BLOCK_SIZE设为512字节以兼容Windows系统。实测发现两个优化点:
- 启用USB DMA可提升约15%的传输效率
- 在Get_Capacity回调中返回实际容量减1个块,可避免系统自动格式化
3.2 双FRAM的负载均衡
采用简单的"交替写入"策略:
c复制void FRAM_WriteMulti(uint32_t addr, uint8_t *buf, uint32_t len) {
uint8_t chip_select = (addr < FRAM_SIZE) ? 0 : 1;
uint32_t chip_addr = addr % FRAM_SIZE;
HAL_GPIO_WritePin(chip_select ? FRAM2_SS_GPIO_Port : FRAM1_SS_GPIO_Port,
chip_select ? FRAM2_SS_Pin : FRAM1_SS_Pin, GPIO_PIN_RESET);
uint8_t cmd[4] = {FRAM_WRITE, (chip_addr >> 16) & 0xFF,
(chip_addr >> 8) & 0xFF, chip_addr & 0xFF};
[HAL](https://taotoken.net/?utm_source=hardware)_SPI_Transmit(&hspi[chip_select], cmd, 4, HAL_MAX_DELAY);
HAL_SPI_Transmit(&hspi[chip_select], buf, len, HAL_MAX_DELAY);
HAL_GPIO_WritePin(chip_select ? FRAM2_SS_GPIO_Port : FRAM1_SS_GPIO_Port,
chip_select ? FRAM2_SS_Pin : FRAM1_SS_Pin, GPIO_PIN_SET);
}
实测写入速度稳定在420KB/s(全速USB的理论极限约700KB/s),比普通U盘在小文件写入时快3-5倍。
4. 性能优化技巧
4.1 SPI时序调优
通过调整SPI时钟相位得到最佳眼图:
| SPI模式 | CPOL | CPHA | 稳定性 | 最高时钟 |
|---|---|---|---|---|
| 0 | 0 | 0 | ★★☆ | 10MHz |
| 1 | 0 | 1 | ★★★ | 18MHz |
| 2 | 1 | 0 | ★★☆ | 12MHz |
| 3 | 1 | 1 | ★★★☆ | 15MHz |
最终选择模式1(CPOL=0, CPHA=1),配合如下GPIO设置:
c复制hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
4.2 文件系统优化
由于FRAM无需擦除,我们修改了FATFS的diskio.c:
c复制DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
switch(cmd) {
case CTRL_SYNC: // 省去Flash的sync操作
return RES_OK;
case GET_BLOCK_SIZE:
*(DWORD*)buff = 1; // 虚拟块大小设为1扇区
return RES_OK;
}
return RES_PARERR;
}
5. 实测问题与解决方案
5.1 枚举失败问题
现象:插入电脑时约30%概率识别失败
排查:
- 用USB分析仪抓包发现SET_CONFIGURATION阶段超时
- 检查发现USB DP引脚未接1.5k上拉电阻
解决:
- 在PA12(USB_DP)添加1.5k电阻到3.3V
- 在CubeMX中启用USB早连接(Early Connect)
5.2 写入数据异常
现象:连续写入超过2KB时数据错位
分析:
- 逻辑分析仪显示SPI时钟在持续传输时出现毛刺
- 发现FRAM的HOLD引脚未正确处理
优化:
c复制// 在每次传输前确保HOLD无效
void FRAM_HoldOff(void) {
HAL_GPIO_WritePin(FRAM_HOLD_GPIO_Port, FRAM_HOLD_Pin, GPIO_PIN_SET);
delay_us(1); // 保持至少500ns
}
6. 应用场景建议
经过三个月的实测,这个铁电U盘特别适合:
- 工业数据记录:以1秒间隔写入100字节数据,可连续工作15年
- 嵌入式系统配置存储:频繁修改参数不会损耗存储器
- 应急日志系统:突然断电时不会丢失最后写入的数据
成本对比(单件):
- 传统方案:STM32F103($2) + 4MB SPI Flash($0.8) = $2.8
- 本方案:STM32F411($3.5) + 2xMB85RS2MT($12) = $15.5
建议在需要数据高可靠性的场景使用,普通U盘应用还是传统方案更经济。未来考虑用更大容量的MB85RS4MT(4Mb/片)来做8MB版本,不过要注意SPI时钟线长度需控制在10cm以内。
