STM32F103 USB Mass Storage实战:SCSI命令解析与调试技巧

张开发
2026/4/17 6:36:26 15 分钟阅读

分享文章

STM32F103 USB Mass Storage实战:SCSI命令解析与调试技巧
STM32F103 USB Mass Storage实战SCSI命令解析与调试技巧当你在STM32F103上实现USB Mass Storage功能时SCSI命令的处理往往是项目成败的关键。作为嵌入式开发者我们不仅要理解协议规范更需要掌握实际调试中可能遇到的各种坑。本文将从一个实战开发者的视角分享SCSI命令解析的核心要点和调试技巧帮助你快速定位和解决开发过程中的典型问题。1. SCSI命令基础与STM32F103实现框架在USB Mass Storage协议中SCSI命令通过CBWCommand Block Wrapper包传输设备处理后通过CSWCommand Status Wrapper返回状态。STM32F103的USB外设虽然功能完备但在处理Mass Storage协议时需要特别注意以下几点端点配置至少需要两个Bulk端点IN和OUT建议使用双缓冲机制数据传输阶段包括命令阶段、数据阶段可选和状态阶段错误处理需要正确处理STALL和NAK状态典型的命令处理流程如下void MSC_BOT_ProcessCmd(void) { switch (gMscCBW.CB[0]) { case SCSI_TEST_UNIT_READY: scsiCmdTestUnitReady(gMscCBW.bLUN); break; case SCSI_REQUEST_SENSE: scsiCmdRequestSense(gMscCBW.bLUN); break; // 其他命令处理... default: scsiCmdNotSupported(gMscCBW.bLUN); } }2. 关键SCSI命令的深度解析2.1 TEST UNIT READY0x00与设备就绪状态这是主机最频繁发送的命令之一用于检测设备是否就绪。在实际调试中常见的设备未就绪原因包括存储介质未初始化或初始化失败文件系统挂载错误物理连接问题对应的处理函数应该包含完善的错误检测void scsiCmdTestUnitReady(uint8_t lun) { if(massMalGetStatus(lun) ! 0) { scsiSetSenseData(lun, SCSI_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT); mscBotSendCSW(MSC_BOT_CSW_CMD_FAILED, MSC_BOT_SEND_CSW_ENABLE); return; } mscBotSendCSW(MSC_BOT_CSW_CMD_PASSED, MSC_BOT_SEND_CSW_ENABLE); }2.2 REQUEST SENSE0x03与错误诊断当设备返回检查条件状态时主机会发送REQUEST SENSE命令获取详细错误信息。Sense数据的结构如下偏移长度描述01响应代码0x70表示当前错误21Sense Key错误类别121ASC附加Sense代码131ASCQASC限定符典型的Sense Key包括0x00(NO SENSE)无错误0x02(NOT READY)设备未就绪0x05(ILLEGAL REQUEST)非法命令或参数设置Sense数据的函数实现void scsiSetSenseData(uint8_t lun, uint8_t sensKey, uint8_t asc) { gScsiSenseData[2] sensKey; // Sense Key gScsiSenseData[12] asc; // ASC gScsiSenseData[13] 0x00; // ASCQ }3. 存储操作相关命令实战3.1 READ CAPACITY0x25与介质容量报告READ CAPACITY命令返回存储设备的最后一个逻辑块地址和块大小。在STM32F103上实现时需要注意块地址和块大小都采用大端格式返回的块地址是最后一个有效块的地址总块数-1块大小通常为512字节与SD卡标准一致实现代码示例void scsiCmdReadCapacity10(uint8_t lun) { uint8_t data[8]; // 最后一个逻辑块地址大端 data[0] (gMassMemory.blockCount[lun] - 1) 24; data[1] (gMassMemory.blockCount[lun] - 1) 16; data[2] (gMassMemory.blockCount[lun] - 1) 8; data[3] (gMassMemory.blockCount[lun] - 1); // 块大小大端 data[4] gMassMemory.blockSize[lun] 24; data[5] gMassMemory.blockSize[lun] 16; data[6] gMassMemory.blockSize[lun] 8; data[7] gMassMemory.blockSize[lun]; mscBotSendData(data, sizeof(data)); }3.2 READ100x28/WRITE100x2A与数据传输READ10和WRITE10是最核心的数据传输命令实现时需要考虑逻辑块地址转换将SCSI命令中的LBA转换为实际存储地址数据分块处理当传输大量数据时需要分多次完成错误恢复处理传输中断和错误的情况关键实现片段void handleReadWrite(uint8_t lun, uint8_t cmd) { uint32_t lba (gMscCBW.CB[2] 24) | (gMscCBW.CB[3] 16) | (gMscCBW.CB[4] 8) | gMscCBW.CB[5]; uint16_t blockNbr (gMscCBW.CB[7] 8) | gMscCBW.CB[8]; if(!scsiAddressManagement(lun, cmd, lba, blockNbr)) { // 地址无效处理 return; } if(cmd SCSI_READ10) { if((gMscCBW.bmFlags 0x80) ! 0) { gMscBotState MSC_BOT_DATA_IN; massReadMemory(lun, lba, blockNbr); } else { // 方向错误处理 } } else { // WRITE10 // 类似处理写入逻辑 } }4. 调试技巧与问题排查4.1 使用逻辑分析仪抓取USB协议当SCSI命令处理出现问题时逻辑分析仪是最直接的调试工具。重点关注CBW/CSW结构验证CBW签名应为0x43425355CSW签名应为0x53425355数据传输长度与dDataLength字段一致时序问题排查命令响应时间是否符合要求数据阶段与状态阶段的转换是否正确4.2 常见错误代码与解决方法错误现象可能原因解决方案主机不识别设备未正确响应INQUIRY命令检查INQUIRY返回数据格式读写操作失败逻辑块地址超出范围验证地址转换逻辑设备频繁断开未及时响应命令优化处理流程减少延迟数据传输错误端点缓冲区配置不当检查端点最大包大小设置4.3 调试代码示例在开发过程中可以添加调试输出帮助定位问题void debugPrintCBW(void) { printf(CBW: Signature%08X, Tag%08X, DataLen%u, Flags%02X, LUN%u\n, gMscCBW.dSignature, gMscCBW.dTag, gMscCBW.dDataLength, gMscCBW.bmFlags, gMscCBW.bLUN); printf(CB: ); for(int i0; i16; i) { printf(%02X , gMscCBW.CB[i]); } printf(\n); }5. 性能优化与高级实现技巧5.1 双缓冲机制实现为提高吞吐量可以在USB端点上实现双缓冲配置两个缓冲区交替使用当一个缓冲区正在传输时准备下一个缓冲区的数据利用USB传输完成中断切换缓冲区5.2 DMA加速数据传输对于大量数据传输启用DMA可以显著降低CPU负载// 配置USB端点DMA USB_OTG_INEndpointTypeDef *ep USB_OTG_FS_regs.DIEP[ep_num]; ep-DIEPCTL | USB_OTG_DIEPCTL_SD0PID_SEVNFRM; ep-DIEPDMA (uint32_t)buffer; ep-DIEPTSIZ (size USB_OTG_DIEPTSIZ_XFRSIZ_Pos) | (1 USB_OTG_DIEPTSIZ_PKTCNT_Pos); ep-DIEPCTL | USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_USBAEP;5.3 异步处理与状态机复杂的SCSI命令处理可以采用状态机模型typedef enum { MSC_IDLE, MSC_CMD_RECEIVED, MSC_DATA_IN, MSC_DATA_OUT, MSC_STATUS_SEND } MSC_StateTypeDef; void MSC_StateMachine(void) { static MSC_StateTypeDef state MSC_IDLE; switch(state) { case MSC_IDLE: if(USB收到CBW) { state MSC_CMD_RECEIVED; } break; // 其他状态处理... } }在STM32F103上实现稳定的USB Mass Storage功能需要深入理解SCSI命令协议并结合芯片特性进行优化。通过合理的调试方法和系统设计即使是资源有限的MCU也能实现可靠的存储设备功能。

更多文章