stm32 sdio除錯,修改官方例程bug,已成功調通SDIO讀取SD卡
這幾天研究stm32操作sd卡,使用的是ST給的例程stm32_eval_sdio_sd.c,版本V4.5.0,遇到了如下問題,現一一解決。
本文由Eric Qu撰寫,QQ:12430300。轉載請註明出處。
1:SD_WaitReadOperation()或者SD_WaitWriteOperation()函式死迴圈
原因:資料傳輸錯誤導致傳輸中斷,無法滿足退出等待的判斷條件。
程式碼分析:
SD_Error SD_WaitReadOperation(void)
{
SD_Error errorstatus = SD_OK;
while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
{}
if (TransferError != SD_OK)
{
return(TransferError);
}
return(errorstatus);
}
程式碼中用了while()是導致死迴圈的原因。TransferEnd ,TransferError 這兩個引數是在中斷中修改的, SD_DMAEndOfTransferStatus() 的結束條件是DMA傳輸結束。
看中斷例程:
SD_Error SD_ProcessIRQSrc(void)
{
if (StopCondition == 1)
{
SDIO->ARG = 0x0;
SDIO->CMD = 0x44C;
TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
}
else
{
TransferError = SD_OK;
}
SDIO_ClearITPendingBit(SDIO_IT_DATAEND);
SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE);
TransferEnd = 1;
return(TransferError);
}
中斷配置為資料傳輸結束中斷SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
資料傳輸出錯導致傳輸中斷的情況下,SD_DMAEndOfTransferStatus的判斷為false,TransferEnd 和TransferError 的狀態也不會改變,導致while迴圈無法退出。
解決:
中斷配置為SDIO_ITConfig(SDIO_IT_RXOVERR|SDIO_IT_DTIMEOUT|SDIO_IT_DCRCFAIL|SDIO_IT_DATAEND, ENABLE);
中斷例程修改:
SD_Error SD_ProcessIRQSrc(void)
{
if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
{
SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
TransferError = SD_DATA_TIMEOUT;
}
else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
{
SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
TransferError = SD_DATA_CRC_FAIL;
}
else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
{
SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
TransferError = SD_RX_OVERRUN;
}
else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
{
SDIO_ClearFlag(SDIO_FLAG_STBITERR);
TransferError = SD_START_BIT_ERR;
}
else
{
if (StopCondition == 1)
{
SDIO->ARG = 0x0;
SDIO->CMD = 0x44C;
TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
}
else
{
TransferError = SD_OK;
}
}
SDIO_ITConfig(SDIO_IT_RXOVERR|SDIO_IT_DTIMEOUT|SDIO_IT_DCRCFAIL|SDIO_IT_DATAEND, DISABLE);
TransferEnd = 1;
return(TransferError);
}
這麼做的作用是發生錯誤時也會進入中斷,即使dma沒有結束,也能退出while迴圈。
2:sd_init()過程失敗
可能性1:按照SD規範,初始化之前需要有74個或更多個clock讓sd卡同步,例程中把clock開起來後直接傳送cmd0,沒有同步clock,所以先修改SD_PowerON函式內SDIO_ClockCmd(ENABLE);呼叫之後增加200us延時。
可能性2:發生SDIO_FLAG_DCRCFAIL錯誤。
修改SDIO_TRANSFER_CLK_DIV 來修改資料傳輸速率。
按照ST例程的註釋,資料傳輸速率不能超過25M,但是原先配置SDIO_TRANSFER_CLK_DIV=0,按照72M主頻計算的話傳輸速度達到72/2=36M,不出錯就怪了。
我現在配置SDIO_TRANSFER_CLK_DIV=2後正常。
可能性3:韌體庫使用不正確。使用V4.5的例程,韌體庫需要用V3.5.0的,試過3.2的韌體庫會失敗。
3:SD_DMAEndOfTransferStatus函式內沒有清標誌位,按照datasheet,標誌位是由手動清除的。
4:SD_ReadBlock()發生SDIO_FLAG_DCRCFAIL錯誤。
一開始是實踐發現,先執行一下SD_ReadMultiBlocks函式,以後再執行SD_ReadBlock就正常了。很奇怪的現象吧。後來查閱了一些資料,發現SD卡的block大小並不是固定的,可以配置為512,1024等,於是懷疑是block大小配置不正確導致。檢查發現SD_ReadBlock操作之前沒有設定sd卡block大小,也就是cmd16,加入這個程式碼就像行了
/*!< Set Block Size for Card */
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
if (SD_OK != errorstatus)
{
return(errorstatus);
}
其實這個操作並不是每次讀操作都要執行的,如果中途不改變block大小,只要初始化的時候設定一次就可以了。