盈钰H753板子中,有一颗512MB的SD NAND。因为无法拆卸,所以只能通过板载的USB接口完成对SD的读写。
使用CUBE IDE,只需要3步即可配置:
另外可以在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即可。
在查询资料的时候,发现诸多帖子都在故弄玄虚,很多细节没有相应的解释,贴主也是知其然不知其所以然,人云亦云。针对这些疑问,逐一做了验证,记录如下:
如果不这么做,USB主机将不认盘或者卡死。因为SD状态判断和读写都是在USB中断中触发或者进行的,若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。
不加延迟的情况下,极有可能上电后不认盘,这个很可能与USB硬件电路的设计、主从通信的延迟等相关,由于手头没有仪器设备,无法验证。根据测试,80~100ms即可。
有些教程特别强调要做一些事,但并未说明为什么要这么做。带着怀疑的态度对其验证,得出以下结论:
有些帖子强调要将MSC_MEDIA_PACKET配置为单个扇区大小,或者为1024,或者在配置完这个参数之后要增加Project Heap Size的值,确保其大于MSC_MEDIA_PACKET,但这些都没有依据。
MSC_MEDIA_PACKET是USB media io的缓冲区,当配置得较大时,单次与主机通信可以携带更多数据;此时可以通过连续读写扇区方式与SD通信,由于连续读写的效率通常高于单扇区的读写,因此宏观上看速率将更大。
这个值必须大于或等于单个扇区的容量,且必须为扇区字节大小的整数倍,最大32KB。如果不是整数倍,则最后一个扇区的数据无法处理,USB与主机的通讯将产生异常。它与 Porject Linker Settings中堆栈的大小并无直接关系,二者可单独配置互不关联。
这里也是重要误区,很多帖子在没有说明硬件的情况下,一律强调要将其配置为上拉。虽然没有副作用,但其实并无必要,SDMMC除了勾选中断外和配置优先级外,其它条目保持默认即可。
以上情况均基于裸机程序,如果引入FreeRTOS,可能会有差别,届时再做验证。