~~NOCACHE~~ ====== 在STM32F103C8芯片上模拟一个USB CDROM ====== * 这个项目已经上传至[[https://github.com/GenieKits/USB-CDROM-Emulation-on-STM32F103C8|Github]],并且 * 你也可以从本站的[[:zh:downloads|下载页]]上取得全部源码。 ---- [[https://www.st.com/content/st_com/en.html|STMicroelectronics公司]]给他们流行的STM32芯片提供的源代码中包含了一个USB mass storage device class示例. 我对这个示例源码做了一些增强,用以模拟一个**微型**的USB-CDROM。我只接了一颗512KB spi flash芯片做为光盘盘片并且向其中写入了一个很小的ISO9660映像文件。这就是为什么我称其为**微型**CDROM。原始的源代码仅支持一小部分SCSI请求,我扩充了源代码来支持一些扩展的指令。由于flash芯片的容易很小,我不得不做了一个工具用于分析ISO9660映像文件并且剪裁文件尺寸。我还做了一个工具,使用自定义的指令把光盘映像文件写入flash芯片中。不过这两个工具都是运行于WINDOWS平台的,我对LINUX下的软件开发不甚了解。 ===== 第一步:硬件设计 ===== 我选择了[[https://satoshinm.github.io/blog/171212_stm32_blue_pill_arm_development_board_first_look_bare_metal_programming.html|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)是从[[https://github.com/eziya/STM32_SPI_SDCARD|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)。我使用我自制的全新的[[:zh:usb_express:cmsis-dap|CMSIS-DAP JTAG probe]]来调试这个项目。 ;-) ===== 第四步:制作并写入ISO9660映像文件 ===== 我选择了[[https://www.trustfm.net/software/utilities/Folder2Iso.php|Folder2Iso]]这个软件制作ISO9660映像文件。由于AT45DB041B芯片的容量超小,我不得不制作一个工具剪裁映像文件的尺寸。请参考[[https://github.com/GenieKits/USB-CDROM-Emulation-on-STM32F103C8/tree/master/Win32|iso9660]]中的WIN32源码。部分源码来自于[[https://blog.csdn.net/GoodQt/article/details/17202109|gootqt's blog: ISO9660文件系统分析]]。 WINDOWS平台上的盘片烧写工具[[https://github.com/GenieKits/USB-CDROM-Emulation-on-STM32F103C8/tree/master/Win32|(MSC_Test)]]有一点小花活,我使用函数"GetMscDeviceContext"来取得这个小光驱的路径名,如果你有另一个USB光驱插在主机上,你得先把它拔了,要不就得检查INQUIRY指令所取得的manufacturer/product字符串。如果你想尝试使用VID/PID来探测这个小光驱,请参考[[https://stackoverflow.com/questions/4065473/find-and-eject-a-usb-device-based-on-its-vid-pid|stackoverflow.com上的这个页面]]。我在我的项目中留了一些来自这个页面的源码(函数"GetDrivesDevInstByDeviceNumber"和"matchDevInstToUsbDevice")。