首先 - 如果您只需要闪存作为大容量存储设备在 PC 上可见,那么您不需要 FatFS,因为它用于从 MCU 以逐个文件的方式访问存储。当 PC 访问存储设备时,它会自行管理其上的文件系统,您可以在格式化驱动器时选择要使用哪种文件系统。在低层,当与存储本身通信时,它所做的只是告诉存储“从 Y 地址读/写 X 字节”。设备所需要做的就是写入或读取给定数据并返回操作结果。
USB 大容量存储设备类
该 USB 类将您的设备作为存储设备向主机公开,允许其从指定地址读取或向指定地址写入给定数量的字节。对于您提到的STM32F4,您需要实现的功能如下(基于STM32Cube库):
typedef struct _USBD_STORAGE
{
int8_t (* Init) (uint8_t lun);
int8_t (* GetCapacity) (uint8_t lun, uint32_t *block_num, uint16_t *block_size);
int8_t (* IsReady) (uint8_t lun);
int8_t (* IsWriteProtected) (uint8_t lun);
int8_t (* Read) (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
int8_t (* Write)(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
int8_t (* GetMaxLun)(void);
int8_t *pInquiry;
}USBD_StorageTypeDef;
正如你所提到的,有一个USBD_MSC_Template_fops.c / .h
提供示例空模板供您实现的文件,最重要的功能是Read
and Write
真正的“工作”完成的地方。要初始化您的设备,使其在连接到 PC 主机时显示为 USB 大容量存储设备,剩下的就是:初始化 USB 本身(USBD_Init
),注册MSC设备类(USBD_RegisterClass
),在驱动程序中注册所述结构(USBD_MSC_RegisterStorage
)并启动驱动程序的 USB 设备进程(USBD_Start
)当检测到与主机的连接时。有许多示例可以做到这一点 - 请参阅 Discovery 或 Eval 板的参考实现。您似乎已正确完成此操作,因为主机属性将您的设备检测为 USB MSC 设备并将其报告为未格式化。
您的系统说驱动器未格式化的原因是因为驱动器中的空实现usbd_msc_storage_template.c
文件返回执行成功(返回码0)STORAGE_Read
函数,但实际上并不执行任何读取 - 没有数据被发送回。虽然这可能因操作系统而异,但最可能的情况是您会看到有关存储未格式化或数据已损坏的消息。
USB 大容量存储设备回调与物理内存的接口
如上所述,调用USBD_MSC_RegisterStorage
将在 USB MSC 设备类驱动程序中注册您的结构。此时,驱动程序本身将在适当的时候调用您提供的函数 - 无论主机何时请求。如果目标内存是 SD 卡,自然的步骤是首先实现访问 SD 卡的功能。一旦这些功能经过测试并证明可以工作,剩下的就是将它们放入 USB MSC 设备中Read
and Write
功能并且 - 假设正确的中断优先级 - 它通常应该“开箱即用”。系统应该能够格式化卡,然后通过 MCU 读取和写入文件。
对于您选择的任何类型的内存,其工作方式都是相同的。唯一的要求是实施USBD_StorageTypeDef
回调函数与原样完全相同。这意味着主机可以选择在报告的地址空间内的任何地址写入任意数量的随机字节,您要么完全服从(按原样写入所有数据)并返回“成功执行”,要么返回错误,这很可能会意味着您的驱动器将被卸载,并且将提示用户一条错误消息。在读取的情况下,这意味着如果主机从 Y 地址请求 X 个字节,则设备需要准确返回该数据量。这意味着,如果您的内存类型不完全适合这种访问,则在访问物理内存的层中将需要做更多的工作才能遵守 USB MSC 接口。所有这些自然地将我们引向下面的最后一点。
闪存作为文件系统存储
对于直接访问原始数据的闪存来说,存在某些缺点,使其不完全适合文件系统应用程序。这些来自于这些记忆的构建方式。虽然可以实现,但为了隐藏这些缺陷,还必须执行额外的步骤:
单独写入“1” - 直接访问闪存时,仅允许您在给定地址下写入“0”位。一旦某个位被翻转为“0”,它就不能再单独翻转回“1”。为此,需要首先擦除整个数据块。根据闪存部分的不同,该区域通常为 512、4096 等字节。这意味着,如果您想将给定字节从 1(二进制 0000 0001)更改为 4(二进制 0000 0100),则必须对整个扇区执行读取-擦除-写入操作。对于您来说,这意味着即使主机请求写入的位之一需要从“0”翻转为“1”,您也需要首先擦除该区域。
随机访问 - 根据内存类型 (NOR/NAND),您可能或可能无法随机访问数据。特别是,对于 NOR 闪存,您可以单独读取或写入数据,而对于 NAND 存储器,由于单元的互连方式,仅允许页访问。这意味着您可能必须读取或写入超出必要的数据。
写入耐久性 - 闪存的每个单元都有一定数量的写入周期。这意味着如果您不断地将数据写入同一地址,您可能很快就会超出此限制。这对于 FAT 等文件系统尤其重要,因为 FAT 区域将不断被写入。这是通过实施某种形式的磨损均衡来解决的,其中物理扇区写入均匀分布。您当然可以选择通过从IsWriteProtected
,如果您的应用程序可以的话。
现在至于当前的 SD 卡如何实现这一切 - 我所知道的所有 SD 卡现在都包含一个简单的微控制器(某种 8081、ARM7 或类似的),它实现了上述所有功能以及 SD 协议。与卡通信时,您并不是真正与原始内存通信,而是与位于您和数据之间的 MCU 通信。它的作用是向您呈现完美连续数据的错觉。