使用STM32 USB设备库作为大容量存储设备的闪存

使用STM32 USB设备库作为大容量存储设备的闪存,usb,storage,device,stm32,Usb,Storage,Device,Stm32,我的主板上有一个闪存IC,它连接到我的STM32F04 ARM处理器。处理器的USB端口可供用户使用。我希望我的闪存在通过USB连接到PC时被检测为存储设备 作为第一步,我在我的程序中将我的USB类定义为MSC,它运行良好。因为当我将主板连接到PC时,它会检测到已连接的大容量存储设备,并给出一个错误,即“您应该在使用光盘之前格式化光盘” 现在的问题是,我如何将闪存定义为处理器的“存储器”。以下可能是您答案的一部分: -usbd_msc_storage_template.c -FAT文件系统 我使

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

作为第一步,我在我的程序中将我的USB类定义为MSC,它运行良好。因为当我将主板连接到PC时,它会检测到已连接的大容量存储设备,并给出一个错误,即“您应该在使用光盘之前格式化光盘”

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

我使用的是STM32F446处理器。 FREERTOS和FATF。 在我的电脑上安装Windows 10


提前感谢:)

首先-如果您只需要闪存作为大容量存储设备在PC上可见,那么您不需要FATF,因为它用于从MCU以逐个文件的方式访问存储。当PC访问存储设备时,它会自行管理其上的文件系统,您可以选择格式化驱动器时使用哪种文件系统。在底层,当与存储器本身通信时,它所做的只是告诉存储器“从Y地址读取/写入X字节”。设备需要做的就是写入或读取给定的数据并返回操作结果

USB大容量存储设备类别

此USB类将您的设备作为存储设备公开给主机,允许它从指定地址读取或写入给定数量的字节。对于您提到的STM32F4,您需要实现以下功能(基于STM32Cube库):

正如您所提到的,有一个
USBD\u MSC\u Template\u fops.c/.h
文件提供了一个示例空模板供您实现,最重要的功能是
Read
Write
,其中真正的“工作”是完成的。要初始化设备,使其在连接到PC主机时显示为USB大容量存储设备,剩下的就是:初始化USB本身(
USBD\u Init
),注册MSC设备类(
USBD\u RegisterClass
),在驱动程序中注册所述结构(
USBD\u MSC\u RegisterStorage
)以及在检测到与主机的连接时启动驱动程序的USB设备进程(
USBD_Start
)。有很多这样做的例子——请参阅Discovery或Eval Board的参考实现。您的操作似乎正确,因为主机属性将您的设备检测为USB MSC设备,并报告为未格式化

系统之所以说驱动器未格式化,是因为
usbd\u msc\u storage\u template.c
文件中的空实现返回了
storage\u Read
函数的成功执行(返回代码0),但实际上没有执行任何读取-没有返回数据。虽然这可能因主机而异,具体取决于操作系统,但最可能的情况是,您将看到一条关于存储未格式化或数据已损坏的消息

将USB大容量存储设备回调与物理内存接口

如上所述,调用
USBD\u MSC\u RegisterStorage
将在USB MSC设备类驱动程序中注册您的结构。在这一点上,驱动程序本身将在适当的时刻调用您提供的函数——只要主机请求。如果目标内存是SD卡,那么自然的步骤是首先实现访问SD卡的功能。一旦这些功能被测试并证明有效,剩下的就是将它们放入USB MSC设备
读取
写入
功能中,并且假设中断优先级正确,它通常应该“开箱即用”。系统应该能够格式化卡,然后通过MCU读写文件

对于您选择的任何类型的内存,其工作方式都是相同的。唯一的要求是完全按照原样实现
USBD_StorageTypeDef
回调函数。这意味着主机可以选择在报告的地址空间内的任何地址写入任意数量的随机字节,您可以完全遵守(按原样写入所有数据)并返回“成功执行”或返回错误,这很可能意味着您的驱动器将被卸载,并向用户提示错误消息。在读取的情况下,这意味着如果主机从Y地址请求X个字节数,则设备需要准确返回该数量的数据。这意味着,如果您的内存类型不完全适合这种访问,那么为了遵守USB MSC接口,必须在访问物理内存的层中进行更多的工作。所有这些自然地将我们引向下面的最后一点

闪存作为文件系统存储

对于直接访问原始数据的闪存,存在某些缺陷,使其不完全适合文件系统应用程序。这些来自于这些记忆的构造方式。虽然可以实现,但为了隐藏这些缺陷,还必须采取其他步骤:

  • 单独写入“1”-直接访问闪存时,仅允许在给定地址下写入“0”位。一旦某个位被翻转到“0”,它就不能再被自动翻转回“1”。为此,需要首先擦除整个数据块。根据闪存部件的不同,这通常是512、4096等字节的区域。这意味着,如果要将给定字节从1(二进制0000 0001)更改为4(二进制0000 0100),则必须对整个扇区执行读-擦除-写操作。对您来说,这意味着即使主机请求写入的某个位需要
    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;