MADL!AR
Code is cheap, show me the PPT!
首页
分类
Fragment
关于
STM32H7 USB TF读卡器
分类:
硬件
发布于: 2024-01-11
盈钰H753板子中,有一颗512MB的SD NAND。因为无法拆卸,所以只能通过板载的USB接口完成对SD的读写。 使用CUBE IDE,只需要3步即可配置: * SDMMC: 选"SD 4 bits Wide bus",勾选“SDMMCX global interrupt”。中断优先级需要高于USB; * USB OTG选“Device_Only”,“USB FS global interrupt”应该为默认开启状态,配置中断优先级,需要低于SD即可; * Middleware中USB_DEVICE选择“Mass Storage Class”,配置MSC_MEDIA_PACKET即可,这里可以直接配置32KB。 另外可以在Project Manager中适当配置项目堆栈大小。在生成代码之后,编写USB与SD连接层代码,使用DMA方式,可以参考H7B3I-DK板卡的官方例程` FatFs_uSD_DMA_Standalone`。以读为例: ``` // usbd_storage_if.c中,读接口 int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { return HAL_OK == CL_sdReadBlocks(buf, blk_addr, blk_len) ? (USBD_OK) : (USBD_FAIL); } // sd 读写层 HAL_StatusTypeDef CL_sdReadBlocks(uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks){ HAL_StatusTypeDef status; uint32_t start_time; // 等待SD状态由忙转为就绪态 start_time = HAL_GetTick(); while(HAL_SD_GetCardState(&opHsd) != HAL_SD_CARD_TRANSFER) { if ((HAL_GetTick() - start_time) >= SD_TIMEOUT) { return HAL_ERROR; } } // 启动传输 CL_sdIsTransmiting = 1; status = HAL_SD_ReadBlocks_DMA(&opHsd, pData, BlockAdd, NumberOfBlocks); if (status != HAL_OK) { return status; } // 启动DMA成功,等待读完成。在DMA完成中断HAL_SD_RxCpltCallback中,会将CL_sdIsTransmiting置为0 start_time = HAL_GetTick(); while(CL_sdIsTransmiting == 1) { if((HAL_GetTick() - start_time) >= SD_TIMEOUT) { return HAL_ERROR; } } return HAL_OK; } ``` 照例完成其它API即可。 在查询资料的时候,发现诸多帖子都在故弄玄虚,很多细节没有相应的解释,贴主也是知其然不知其所以然,人云亦云。针对这些疑问,逐一做了验证,记录如下: ## 需要注意的: #### 1. USB全局中断的优先级需低于SD 如果不这么做,USB主机将不认盘或者卡死。因为SD状态判断和读写都是在USB中断中触发或者进行的,若SD中断优先级低,将陷入死锁状态。 #### 2. 在sd read或write blocks时,需要判断SD卡状态 即HAL_SD_GetCardState()返回值为HAL_SD_CARD_TRANSFER时,代表SD为空闲状态。DMA传输完毕之后,SD卡可能要过一段时间才会变为HAL_SD_CARD_TRANSFER状态,这段时间内,TF主控将数据由缓存写入存储器。如果不等待就立即断电,数据可能会丢失;如果不等待就执行其他的HAL_SD函数,可能会抛出HAL_ERROR。 上述的示例是在执行读写操作前进行判断的,在写入之后没有等待HAL_SD_CARD_TRANSFER状态就返回,这样虽然会略微提高程序执行速度,但也因此有数据丢失风险。 很多教程会在STORAGE_IsReady_FS()接口中,调用HAL_SD_GetCardState() 返回相应状态,这没有问题,但要注意的是,在这个接口中一旦返回了USBD_FAIL,USB主机会立即中断并停止操作,不管是在读、写,还是格式化等过程当中。建议HAL_SD_GetCardState的返回值为HAL_SD_CARD_ERROR时返回USBD_FAIL,其它情况返回USBD_OK。 #### 3. 系统时钟初始化之后,需要延迟一段时间再初始化SD和USB 不加延迟的情况下,极有可能上电后不认盘,这个很可能与USB硬件电路的设计、主从通信的延迟等相关,由于手头没有仪器设备,无法验证。根据测试,80~100ms即可。 ## 无所谓的 有些教程特别强调要做一些事,但并未说明为什么要这么做。带着怀疑的态度对其验证,得出以下结论: #### 1. USB Device和SD初始化的顺序无所谓 #### 2. 无论是否开启硬件流控制(SDMMC hardware flow control),SD都能正确读写,且速率无差异 #### 3. MSC_MEDIA_PACKET与Project的堆栈大小的配置无关 有些帖子强调要将MSC_MEDIA_PACKET配置为单个扇区大小,或者为1024,或者在配置完这个参数之后要增加Project Heap Size的值,确保其大于MSC_MEDIA_PACKET,但这些都没有依据。 MSC_MEDIA_PACKET是USB media io的缓冲区,当配置得较大时,单次与主机通信可以携带更多数据;此时可以通过连续读写扇区方式与SD通信,由于连续读写的效率通常高于单扇区的读写,因此宏观上看速率将更大。 这个值必须大于或等于单个扇区的容量,且必须为扇区字节大小的整数倍,最大32KB。如果不是整数倍,则最后一个扇区的数据无法处理,USB与主机的通讯将产生异常。它与 Porject Linker Settings中堆栈的大小并无直接关系,二者可单独配置互不关联。 #### 4. 无需为USB或SD勾选NVIC中Code generation中的init sequence ordering(无需配置 interrupt unmasking order table) #### 5. 硬件电路已有上拉电阻时,SD GPIO无需配置上拉 这里也是重要误区,很多帖子在没有说明硬件的情况下,一律强调要将其配置为上拉。虽然没有副作用,但其实并无必要,SDMMC除了勾选中断外和配置优先级外,其它条目保持默认即可。 #### 6. SD无需配置DMA、添加DMA变量、配置通道等,可以直接使用HAL_SD_ReadBlocks_DMA等函数操作读写SD 以上情况均基于裸机程序,如果引入FreeRTOS,可能会有差别,届时再做验证。