很蛋疼的发现网上很多所谓的SDIO操作SHDC无意例外都是官方的那个烂玩意,完全没有修改过,所以很多时候根本无法初始化SHDC,我也在网上看到很多人关于这部分的疑问,虽然STM32的SDIO的确是可以这样操作。但是很佩服那群人,什么都没改就发上来,把哥我害惨了。。。。
经过查资料,追踪,最后运气可佳。我发现自己的金士顿4GSD卡(class4)不能初始化跟用4位总线dma操作的原因。。各位也可以上网去找别人的试试,很多人都说不能用4位总线操作,而且用1位总线也只能是在低速率以及开启流控的情况下。而且经常出错。而4位总线总是提示没有检测到起始位。
但是他们都只会问,都没有去想象为什么,我也是。。但是后来发现。STM32的SDIO是完全没问题的,可以读写SHDC,用4位总线24MHZ工作在DMA模式,大家看我修改出来的例程就知道了。看我改过的地方,对比下官方的。
首先:
在配置的时候, 一开始的时候sd卡需要有至少发74个时钟使它自己初始话,这是2.0规范要求的,但是你们自己看看官方的,完全没有这个,我一直追踪,发先在电源初始化那里就已经卡住了- -|||。
于是我在那里面加入了一个发送74个时钟的小代码。
SD_Error SD_PowerON(void)
{
SD_Error errorstatus = SD_OK;
uint32_t response = 0, count = 0;
bool validvoltage = FALSE;
uint32_t SDType = SD_STD_CAPACITY;
int16_t i = 0;
/* Power ON Sequence -------------------------------------------------------*/
/* Configure the SDIO peripheral */
SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV; /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
SDIO_Init(&SDIO_InitStructure);
/* Set Power State to ON */
SDIO_SetPowerState(SDIO_PowerState_ON);
/* Enable SDIO Clock */
SDIO_ClockCmd(ENABLE);
/* CMD0: GO_IDLE_STATE -------------------------------------------------------*/
/* No CMD response required */
SDIO_CmdInitStructure.SDIO_Argument = 0x0;
SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_GO_IDLE_STATE;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
for(;i < 74; i++)
{
SDIO_SendCommand(&SDIO_CmdInitStructure);
CmdError();
}
看到没有,就是画线的那个,这个用37也可以了。但是规范点用74、
接下来,自己初始化可以通过了,因为上电正确一般是可以初始化正常的,这个没什么问题,到了这个函数就开始有问题了。
status = SD_SelectDeselect((u32) (SDCardInfo.RCA << 16));
if (status != SD_OK)
{
rt_kprintf("SD_SelectDeselect is error./n");
goto __return;
}
rt_kprintf("SD set to transfer mode./n");
status = SD_EnableWideBusOperation(SDIO_BusWide_4b);
就是这个函数,使能四位总线模式的函数。事实上,在经过上一次的操作后,配置总线的时候我是这么配置的:
SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV +5 ;
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Enable;
SDIO_Init(&SDIO_InitStructure);
我发现加入流控是必须 的,避免出现数据出错,因为这个模式很不稳定,很多时候会出现overrun的错误,或者crc错误。这时候我们可以先不要调用SD_EnableWideBusOperation(SDIO_BusWide_4b);
这样的话,其实我们是可以直接操作读写SHDC的,但是会发现不仅速度低,而且经常出错。这就是我为什么拼命要搞出四位总线的原因。
所以首先要SD_SelectDeselect((u32) (SDCardInfo.RCA << 16));命令选择SD为传送模式后,要再调用SD_EnableWideBusOperation(SDIO_BusWide_4b)配置总线为四位总线模式;这是为了能够以更快的速度读取SHDC卡。事实上这个函数是有问题的。传送是命令是没有问题的,但是判断依据问题不小。
大家看我的代码