其实SD卡这个东西虽然可以用SPI接口来操作,但是毕竟SD卡的命令太多,状态机也特别复杂,如果自己来用SPI手写底层,开发效率不高,哪怕移植SPI的SDIO协议栈,也依然速率提不上去,毕竟SD卡本身就不是为SPI设计的,只是考虑到一些没有SDIO外设的芯片所保留的一种方法,
所以对于有SDIO外设的芯片来说,就不要考虑用SPI,关于SD卡的一些基本上知识可以自己搜一下网上一抓一大把,可以看完之后实际去使用SDIO操作SD卡的时候再来看
SDIO(顾名思义就是SD卡的接口)
如果有看过SPI怎么操作SD卡的应该会头皮发麻 ,不过我在学习嵌入式的时候总结出一个规则,一般复杂的东西特别烦人基本就需要自己去写底层,不过过于复杂的就完全不需要担心了,因为基本上都会被封装起来直接让人用,由于SD卡是不像一般的NANDFLASH一样需要自己手动配置寄存器来操作的,而是发送不同的命令(即使这样命令也不少),而当使用SDIO去调用HAL库的驱动时,这些发送命令相关的都被封装起来了,对于用户来说就是几个读写函数而已,毕竟本质上任何存储器的功能都只是读和写而已(第一性原理),我们根本不想关心其他的,因此只需要了解这几个接口就好了
HAL_StatusTypeDef HAL_SD_Init(SD_HandleTypeDef *hsd);//初始化函数,在初始化时会使用默认参数,1bit,400Khz,HAL_SD_Init调用->HAL_SD_InitCard调用->SDIO_Init
/* Blocking mode: Polling */ HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout); HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout); HAL_StatusTypeDef HAL_SD_Erase(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd);//普通的读写函数及擦除 /* Non-Blocking mode: IT */ HAL_StatusTypeDef HAL_SD_ReadBlocks_IT(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks); HAL_StatusTypeDef HAL_SD_WriteBlocks_IT(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);//中断式读写 /* Non-Blocking mode: DMA */ HAL_StatusTypeDef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks); HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);//DMA读写
相关的中断及回调函数:
void HAL_SD_IRQHandler(SD_HandleTypeDef *hsd); /* Callback in non blocking modes (DMA) */ void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd);
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd);
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd);
void HAL_SD_AbortCallback(SD_HandleTypeDef *hsd);
配置SD卡的总线宽度
HAL_StatusTypeDef HAL_SD_ConfigWideBusOperation(SD_HandleTypeDef *hsd, uint32_t WideMode);
获取SD卡相关的状态或信息
HAL_StatusTypeDef HAL_SD_SendSDStatus(SD_HandleTypeDef *hsd, uint32_t *pSDstatus); HAL_SD_CardStateTypeDef HAL_SD_GetCardState(SD_HandleTypeDef *hsd); HAL_StatusTypeDef HAL_SD_GetCardCID(SD_HandleTypeDef *hsd, HAL_SD_CardCIDTypeDef *pCID); HAL_StatusTypeDef HAL_SD_GetCardCSD(SD_HandleTypeDef *hsd, HAL_SD_CardCSDTypeDef *pCSD); HAL_StatusTypeDef HAL_SD_GetCardStatus(SD_HandleTypeDef *hsd, HAL_SD_CardStatusTypeDef *pStatus); HAL_StatusTypeDef HAL_SD_GetCardInfo(SD_HandleTypeDef *hsd, HAL_SD_CardInfoTypeDef *pCardInfo);
注意事项
一:
在我们擦除或写操作之后直接读大概率会失败,因为NandFlash的写或擦除操作通常需要一定时间,因此我们需要等到SD卡准备好之后再读,

二:
当读写SD卡时,使用常规的读写操作函数
HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout); HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
可能SD卡只能保持一个稍微低的速率(比如4.8Mhz),特别是在开启Cache之后,因为SD的速度越高对数据的准确率已经同步要求越高,也更耗CPU,如果中间有什么中断,或者其他事情打断了传输,就会导致读写失败(典型情况下是SDIO的FIFO要么欠载要么过载),因此最好使用DMA或IT的传输方式,不需要CPU干预,不过最好在这之前把Cache里的数据无效化,
三:
当我们打开硬件流控的情况下如果不使用DMA或者IT的传输方式,SDIO的SDIO_CK的频率只能支持更低的速度,因为HWFC 会在高速下产生 CLK 毛刺并导致 DCRCFAIL 等错误,经本人实测,开启硬件流控的情况下需要降低2Mhz左右,但是使用DMA传输就没问题
重点:STM32CubeMX问题

当我们配置为4线模式初始化时,CubeMx生成的代码将无法使用,

经过排查,问题在于hsd.Init.BusWide被赋值为4B的时候,再次用
HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B)
就会导致错误从而进入Error_Handler,但由于这是CUbeMX自动生成的,如果把这一行删了,就会在再次使用CUbeMX生成代码的时候被覆盖回去,因此我们只能使用单根数据线的模式来初始化

但当你使用单根数据线模式的时候,CubeMX又不会去生成其他三根数据线的引脚初始化代码,
解决方法就是在生成的MSP函数里面手动初始化其他三根引脚(淦)
然后再在初始化函数下面的用户代码区手动加上配置总线宽度的代码

1836

被折叠的 条评论
为什么被折叠?



