1. 程式人生 > >stm32 sdio除錯,修改官方例程bug,已成功調通SDIO讀取SD卡

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大小,只要初始化的時候設定一次就可以了。