Table of Contents
在STM32F103C8芯片上模拟一个USB CDROM
STMicroelectronics公司给他们流行的STM32芯片提供的源代码中包含了一个USB mass storage device class示例. 我对这个示例源码做了一些增强,用以模拟一个微型的USB-CDROM。我只接了一颗512KB spi flash芯片做为光盘盘片并且向其中写入了一个很小的ISO9660映像文件。这就是为什么我称其为微型CDROM。原始的源代码仅支持一小部分SCSI请求,我扩充了源代码来支持一些扩展的指令。由于flash芯片的容量很小,我不得不做了一个工具用于分析ISO9660映像文件并且剪裁文件尺寸。我还做了一个工具,使用自定义的指令把光盘映像文件写入flash芯片中。不过这两个工具都是运行于WINDOWS平台的,我对LINUX下的软件开发不甚了解。
第一步:硬件设计
我选择了Bluepill板做为主控,它有两个SPI接口可用于连接flash芯片和TF卡。我配置了SPI2做为主接口,使用8bit数据位宽。PB12引脚用做CS线,由固件通过GPIO来控制,固件中也配置了SPI1接口但并没有真正使用它。我连接了一个串行flash芯片做为光盘片,不过我手头只有一些已停产的小容量芯片(AT45DB041B),这意味着我必须做一个尺寸很小的ISO9660映像文件才能写入这个芯片中。
第二步:生成固件
我们用STM32CubeMX来生成基本的固件源码:
- 使用HSE振荡器并且外接8MHz晶体,LSE振荡器不使用。
- 使用SWD串口调试。
- 把SPI2配置为全双工主接口,8bit数据位宽。
- 启用USB Device.
- 配置USB中间件,选择Mass Storage Class。
- 选择PLLCLK (72MHz)做为SYSCLK,HCLK设为72MHz并且USB时钟设为48MHz.
第三步:编码及编译
- 为串行flash芯片做一个驱动程序。(spiflash.c)
- 可以获取芯片的ID。(我打算尝试支持一些常见的flash芯片,比如说W25X32等等。)
- 对芯片进行初始化。
- 计算盘片的最大扇区地址。
- 写入/读取芯片的一个存储页。
- 写入/读取一个光盘片扇区。
- 把flash芯片驱动和USB类进行关联。(usbd_storage_if.c)
驱动中部分代码(SELECT/DESELECT/SPI_Xxx)是从eziya's STM32_SPI_SDCARD project这个项目借鉴来的。
- 处理一些STM32CubeMX不支持的扩展SCSI请求。
- READ TOC/PMA/ATIP (0x43)
- GET CONFIGURATION (0x46)
- GET EVENT/STATUS NOTIFICATION (0x4A)
- READ DISC INFORMATION (0x51)
- STM32CubeMX生成的源码需要打补订。
- INQUIRY (0x12)
- 在这个命令的响应数据中声明一个CDROM。
- 需要支持PAGE CODE。(我还不大确定这是否必须。)
- 在函数“MSC_BOT_CBW_Decode”之中:
- 当函数SCSI_ProcessCmd返回-1时它没有向主机发送CSW。
- 实现一些自定义的SCSI请求。
- GET FLASH CHIPID (0xFF)
- BURN DISC IMAGE (0xFE)
我使用EmBitz集成开发环境编译这个项目,它带有一个GCC编译器(arm_none_eabi)。我使用我自制的全新的CMSIS-DAP JTAG probe来调试这个项目。
第四步:制作并写入ISO9660映像文件
我选择了Folder2Iso这个软件制作ISO9660映像文件。由于AT45DB041B芯片的容量超小,我不得不制作一个工具剪裁映像文件的尺寸。请参考iso9660中的WIN32源码。部分源码来自于gootqt's blog: ISO9660文件系统分析。
WINDOWS平台上的盘片烧写工具(MSC_Test)有一点小花活,我使用函数“GetMscDeviceContext”来取得这个小光驱的路径名,如果你有另一个USB光驱插在主机上,你得先把它拔了,要不就得检查INQUIRY指令所取得的manufacturer/product字符串。如果你想尝试使用VID/PID来探测这个小光驱,请参考stackoverflow.com上的这个页面。我在我的项目中留了一些来自这个页面的源码(函数“GetDrivesDevInstByDeviceNumber”和“matchDevInstToUsbDevice”)。