usb - 使用STM32 USB设备库将闪存作为大容量存储设备

标签 usb storage device stm32

板上有此闪存IC,它已连接到STM32F04 ARM处理器。处理器的USB端口可供用户使用。我希望通过USB连接到PC时将我的闪存检测为存储设备。

第一步,我在程序中将USB类定义为MSC,这可以正常工作。由于将板连接到PC时,它会检测到已连接的大容量存储设备,因此出现错误“您应该在使用光盘之前对其进行格式化”。

现在的问题是,如何将闪存定义为处理器的“存储”。以下可能是您的答案的一部分:
-usbd_msc_storage_template.c
-FAT文件系统

我正在使用STM32F446处理器。
FREERTOS和FATFS。
我的PC上的Windows 10。

提前致谢 :)

最佳答案

首先-如果您只需要使闪存作为大容量存储设备在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文件为您提供了一个示例空模板供您实施,最重要的功能是完成真正的“工作”的ReadWrite。要将设备初始化为在连接到PC主机时显示为USB大容量存储设备,剩下的就是:初始化USB本身(USBD_Init),注册MSC设备类(USBD_RegisterClass),在驱动程序中注册所述结构(USBD_MSC_RegisterStorage),并在检测到主机连接时启动驱动程序(USBD_Start)的USB设备进程。有很多例子可以做到这一点-请参阅Discovery或Eval板的参考实现。您似乎已正确完成了此操作,因为主机正确地将您的设备检测为USB MSC设备,并报告其未格式化。

您的系统表示未格式化驱动器的原因是,usbd_msc_storage_template.c文件中的空实现返回了STORAGE_Read功能的成功执行(返回代码0),但实际上并未执行任何读取操作-没有数据回传。尽管不同的主机可能会因操作系统而异,但最可能的情况是您将看到一条有关存储未格式化或数据损坏的消息。

将USB Mass Storage设备回调与物理内存连接

如上所述,调用USBD_MSC_RegisterStorage将在USB MSC设备类驱动程序中注册您的结构。这时,驱动程序本身将在适当的时候调用该函数,只要主机请求即可。如果目标内存是SD卡,那么自然的步骤就是首先实现访问SD卡的功能。一旦测试了这些功能并证明它们可以正常工作,剩下的就是将它们放入USB MSC设备的ReadWrite函数中,并且-假设中断优先级正确-通常应该“开箱即用”。系统应该能够格式化卡,并随后通过您的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返回true来使其变为只读,如果您的申请可以这样做的话。


现在,关于当前的SD卡如何实现所有这些功能-如今我所知道的所有SD卡都包含一个简单的微控制器(某种8081,ARM7或类似产品),该微控制器可实现以上所有功能以及SD协议。与卡通讯时,您实际上并没有与原始内存通讯,而是与位于您和您的数据之间的MCU通信。它的作用是为您提供完美的连续数据的幻觉。

关于usb - 使用STM32 USB设备库将闪存作为大容量存储设备,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44499101/

相关文章:

android - adb devices(在 android 主机上)显示没有设备

docker - 大型文件的最佳kubernetes存储选项

java - 将对象写入内部存储

iphone - 蓝牙管理器 : Don't get any "BluetoothDeviceDiscoveredNotification"

linux - 如何编写 Linux 驱动模块调用/使用另一个驱动模块?

python - 如何在 Windows 中监控 USB 端口上的输入?

linux - USB 串行通信在 Linux 上不工作(Bill Validator GBA ST2)

访问 usb HID(usb 游戏 Controller )的 Javascript

android - 数据存储 - 歌曲歌词应用程序的哪种方法?

iphone - 在设备上调试时,XCode/GDB 在自动继续断点处停止