基于STM32F411RET6 + 双路MB85RS2MT的铁电U盘
本方案使用STM32F411RET6作为主控,搭载2片MB85RS2MT铁电存储器,总容量512KB,支持USB 2.0免驱通信,同时提供传统HAL库 和TinyGo语言两种软件实现方案。
一、核心芯片选型确认
1. 主控:STM32F411RET6(LQFP64封装)
- 核心参数:Cortex-M4内核,100MHz主频,512KB Flash,128KB RAM;
- 关键资源:内置USB 2.0 Full-Speed Device控制器,3路SPI外设,足够的GPIO;
- 核心优势:性能比G0系列更强,RAM更大,适合跑更复杂的代码(包括TinyGo)。
2. 存储:MB85RS2MTAPNF-G-BDERE1(SOIC8封装)
- 核心参数:2Mbit(256KB)容量,SPI接口,最高40MHz时钟,无写入延迟,10¹⁴次擦写寿命;
- 数量:2片,总容量512KB。
二、完整硬件设计
1. 整体架构
USB3.0座子(仅用USB2.0引脚)→ 电源电路(5V转3.3V)→ STM32F411RET6 → SPI总线 → 2片MB85RS2MT
2. USB3.0座子接线(沿用你提供的9脚座子)
STM32F411RET6不支持USB3.0,仅用USB2.0引脚,接线和之前G0方案完全一致:
| 座子引脚 | 名称 | 接线对象 | 要求 |
|---|---|---|---|
| 1 | VBUS | 电源电路输入端 | 串0.5A保险丝,加TVS保护 |
| 2 | D- | STM32F411的PA11(USB_DM) | 串22Ω电阻,加ESD保护 |
| 3 | D+ | STM32F411的PA12(USB_DP) | 串22Ω电阻,加ESD保护 |
| 4 | GND | 系统主GND | 全板铺地连通 |
| 5/6/8/9 | USB3.0差分对 | 悬空 | 禁止接MCU |
| 7 | GND_DRAIN | 座子金属外壳 → 单点接GND | 可选,提升抗静电能力 |
3. STM32F411RET6详细引脚分配
| 引脚序号 | MCU引脚 | 功能 | 连接对象 | 配置说明 |
|---|---|---|---|---|
| 电源与地 | ||||
| 4/17/32/48 | VDD_1~4 | 3.3V输入 | LDO输出 | 每脚并联100nF去耦电容 |
| 5/18/31/47 | VSS_1~4 | 数字地 | 系统GND | 全板铺地 |
| 13 | VDDA | 3.3V模拟输入 | LDO输出 | 并联100nF+1μF电容 |
| 12 | VSSA | 模拟地 | 系统GND | 单点连接数字地 |
| USB接口 | ||||
| 33 | PA11 | USB_DM | Type-C D- | 复用AF10 |
| 34 | PA12 | USB_DP | Type-C D+ | 复用AF10 |
| SPI总线(共用) | ||||
| 11 | PA5 | SPI1_SCK | 2片MB85RS2MT的6脚 | 复用AF5,推挽输出 |
| 14 | PA6 | SPI1_MISO | 2片MB85RS2MT的2脚 | 复用AF5,上拉输入 |
| 15 | PA7 | SPI1_MOSI | 2片MB85RS2MT的5脚 | 复用AF5,推挽输出 |
| 第一片MB85RS2MT(U1,地址0x00000~0x3FFFF) | ||||
| 37 | PB0 | FRAM_CS1 | U1的1脚 | 推挽输出,初始高电平 |
| 38 | PB1 | FRAM_WP1 | U1的3脚 | 推挽输出,初始高电平 |
| 39 | PB2 | FRAM_HOLD1 | U1的7脚 | 推挽输出,初始高电平 |
| 第二片MB85RS2MT(U2,地址0x40000~0x7FFFF) | ||||
| 40 | PB3 | FRAM_CS2 | U2的1脚 | 推挽输出,初始高电平 |
| 41 | PB4 | FRAM_WP2 | U2的3脚 | 推挽输出,初始高电平 |
| 42 | PB5 | FRAM_HOLD2 | U2的7脚 | 推挽输出,初始高电平 |
| 辅助功能 | ||||
| 7 | NRST | 复位 | RC复位电路 | 10K上拉+100nF到GND |
| 8/9 | PF0/PF1 | HSE | 8MHz晶振 | 并联22pF电容 |
| 6 | PC13 | LED | 状态LED | 推挽输出,串1K电阻 |
4. 核心单元电路
- 电源电路:USB 5V → 0.5A保险丝 → TVS管 → AMS1117-3.3V → 3.3V输出(并联10μF+100nF电容);
- 铁电电路:2片MB85RS2MT的VDD(8脚)接3.3V,VSS(4脚)接GND,每片就近加100nF去耦电容;
- ESD保护:D+、D-并联SRV05-4 ESD器件。
5. PCB设计要点
- SPI总线(SCK、MISO、MOSI)尽量短且等长,远离晶振;
- USB D+、D-差分对等长布线,全程包地,阻抗90Ω;
- 去耦电容紧贴芯片电源引脚,过孔直接打到底层地平面。
三、软件实现方案一:STM32CubeMX + HAL库(传统稳定版)
1. STM32CubeMX配置步骤
- 时钟配置 :
- 外部晶振HSE 8MHz,PLL倍频到100MHz系统时钟;
- USB时钟配置为48MHz(必须精确)。
- 外设配置 :
- SPI1:全双工主模式,CPOL=0、CPHA=0(模式0),8位数据,16MHz时钟;
- USB:激活USB_Device(FS),中间件选择Mass Storage Class(MSC);
- GPIO:CS、WP、HOLD、LED设为推挽输出,PC13初始低电平。
- 生成MDK-ARM或STM32CubeIDE工程。
2. 铁电驱动代码(仅需修改片选宏)
c
#include "main.h"
#include "spi.h"
// 铁电指令
#define FRAM_CMD_WREN 0x06
#define FRAM_CMD_WRITE 0x02
#define FRAM_CMD_READ 0x03
#define FRAM_CMD_RDID 0x9F
// 容量定义
#define FRAM_SINGLE_SIZE 0x40000
#define FRAM_TOTAL_SIZE 0x80000
#define FRAM_SECTOR_SIZE 512
// 片选控制(对应新引脚)
#define FRAM_CS1_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET)
#define FRAM_CS1_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET)
#define FRAM_CS2_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET)
#define FRAM_CS2_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET)
// SPI读写一个字节
static uint8_t SPI_RW(uint8_t data)
{
uint8_t rx;
HAL_SPI_TransmitReceive(&hspi1, &data, &rx, 1, 100);
return rx;
}
// 选择芯片并返回偏移地址
static uint32_t SelectChip(uint32_t addr)
{
if(addr < FRAM_SINGLE_SIZE)
{
FRAM_CS1_LOW();
FRAM_CS2_HIGH();
return addr;
}
else
{
FRAM_CS1_HIGH();
FRAM_CS2_LOW();
return addr - FRAM_SINGLE_SIZE;
}
}
// 读数据
void FRAM_Read(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint32_t offset = SelectChip(addr);
SPI_RW(FRAM_CMD_READ);
SPI_RW((offset >> 16) & 0xFF);
SPI_RW((offset >> 8) & 0xFF);
SPI_RW(offset & 0xFF);
for(uint32_t i=0; i<len; i++) buf[i] = SPI_RW(0xFF);
FRAM_CS1_HIGH();
FRAM_CS2_HIGH();
}
// 写数据
void FRAM_Write(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint32_t offset = SelectChip(addr);
// 写使能
SPI_RW(FRAM_CMD_WREN);
FRAM_CS1_HIGH();
FRAM_CS2_HIGH();
// 执行写
SelectChip(addr);
SPI_RW(FRAM_CMD_WRITE);
SPI_RW((offset >> 16) & 0xFF);
SPI_RW((offset >> 8) & 0xFF);
SPI_RW(offset & 0xFF);
for(uint32_t i=0; i<len; i++) SPI_RW(buf[i]);
FRAM_CS1_HIGH();
FRAM_CS2_HIGH();
}
// 初始化
uint8_t FRAM_Init(void)
{
uint8_t id[3];
FRAM_CS1_LOW();
SPI_RW(FRAM_CMD_RDID);
id[0] = SPI_RW(0xFF);
id[1] = SPI_RW(0xFF);
id[2] = SPI_RW(0xFF);
FRAM_CS1_HIGH();
if(id[0] != 0x04 || id[1] != 0x7F) return 1; // 检查MB85RS2MT ID
return 0;
}
3. USB MSC移植
修改usbd_storage_if.c,对接上述FRAM_Read和FRAM_Write函数,和之前G0方案完全一致。
四、软件实现方案二:TinyGo(Go语言版)
1. 环境搭建
- 安装Go 1.18+:https://go.dev/dl/
- 安装TinyGo:https://github.com/tinygo-org/tinygo/releases
- 安装ST-Link驱动(用于烧录)。
2. TinyGo铁电读写基础代码
创建main.go,实现SPI初始化和铁电读写:
go
package main
import (
"machine"
"time"
)
// 铁电指令
const (
FRAM_CMD_WREN = 0x06
FRAM_CMD_WRITE = 0x02
FRAM_CMD_READ = 0x03
FRAM_CMD_RDID = 0x9F
)
// 引脚定义
var (
spi = machine.SPI1
cs1 = machine.PB0 // 第一片CS
cs2 = machine.PB3 // 第二片CS
led = machine.PC13
)
func init() {
// 初始化LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
led.Low()
// 初始化CS引脚
cs1.Configure(machine.PinConfig{Mode: machine.PinOutput})
cs1.High()
cs2.Configure(machine.PinConfig{Mode: machine.PinOutput})
cs2.High()
// 初始化SPI
spi.Configure(machine.SPIConfig{
Frequency: 16000000, // 16MHz
SCK: machine.PA5,
SDO: machine.PA7,
SDI: machine.PA6,
Mode: 0, // SPI模式0
})
}
// SPI读写一个字节
func spiRW(data byte) byte {
rx := make([]byte, 1)
tx := []byte{data}
spi.Tx(tx, rx)
return rx[0]
}
// 读数据
func framRead(addr uint32, buf []byte) {
var cs machine.Pin
var offset uint32
if addr < 0x40000 {
cs = cs1
offset = addr
} else {
cs = cs2
offset = addr - 0x40000
}
cs.Low()
spiRW(FRAM_CMD_READ)
spiRW(byte((offset >> 16) & 0xFF))
spiRW(byte((offset >> 8) & 0xFF))
spiRW(byte(offset & 0xFF))
for i := range buf {
buf[i] = spiRW(0xFF)
}
cs.High()
}
// 写数据
func framWrite(addr uint32, buf []byte) {
var cs machine.Pin
var offset uint32
if addr < 0x40000 {
cs = cs1
offset = addr
} else {
cs = cs2
offset = addr - 0x40000
}
// 写使能
cs.Low()
spiRW(FRAM_CMD_WREN)
cs.High()
// 执行写
cs.Low()
spiRW(FRAM_CMD_WRITE)
spiRW(byte((offset >> 16) & 0xFF))
spiRW(byte((offset >> 8) & 0xFF))
spiRW(byte(offset & 0xFF))
for i := range buf {
spiRW(buf[i])
}
cs.High()
}
func main() {
// 简单测试:写入并读取数据
testBuf := []byte("Hello TinyGo FRAM!")
framWrite(0, testBuf)
readBuf := make([]byte, len(testBuf))
framRead(0, readBuf)
// 闪烁LED表示测试完成
for {
led.High()
time.Sleep(500 * time.Millisecond)
led.Low()
time.Sleep(500 * time.Millisecond)
}
}
3. 烧录命令
在终端执行(TinyGo已支持STM32F411):
bash
tinygo flash -target nucleo-f411re main.go
五、调试要点
- 先测电源:确认3.3V输出正常,无短路;
- 再测SPI:用Go或HAL库读取铁电ID(0x047F),确认通信正常;
- 最后测USB:连接电脑,确认识别为大容量存储设备,格式化后测试读写。
需要我把HAL库版的完整工程文件或者TinyGo的USB MSC实现代码整理给你吗?