運(yùn)動控制器27:SD的配置和讀寫程序解讀

初始化和去初始化SD卡

SD_Init

  • 該函數(shù)首先將SDIO模塊設(shè)置為SPI模式SD_LowLevel_Init,在初始化結(jié)束以后,再將數(shù)據(jù)總線設(shè)置為4位數(shù)據(jù)模式。
  • 然后調(diào)用SD_PowerON對SD卡進(jìn)行上電,進(jìn)行時鐘設(shè)置和相對位置的設(shè)置,以及卡的類型判斷。
  • 第三步調(diào)用SD_InitializeCards初始化,讀出CID和CSD,這些是卡的身份證號碼。
  • 身份證驗證通過以后,我們將讀取的CID和CSD賦值給結(jié)構(gòu)體,用于后面的程序調(diào)用和輸出顯示。
  • 選中該卡以后,我們配置數(shù)據(jù)總線為4位,SPI完成了任務(wù),1位模式退出。
SD_Error SD_Init(void)
{
   SD_Error errorstatus = SD_OK;
   //外設(shè)的端口設(shè)置,打開各引腳的時鐘,配置為SPI,CLK,MO,MI和CS引腳,以及一個檢測引腳。
   //初始化SPI以及打開SPI模塊。
   SD_LowLevel_Init();
 
    //SD上電,配置SD卡正常工作的所有參數(shù),主要是時鐘和電壓檢測
    //第一個調(diào)用的函數(shù)SDIO_Init,其中總線為1位寬度,并配置好時鐘。
    //然后調(diào)用 SDIO_SetPowerState給SDIO模塊上電,然后 SDIO_ClockCmd打開時鐘開關(guān),此時可以正常工作了。
    //發(fā)送CMD0,把卡進(jìn)入閑置模式,發(fā)送的是 SDIO_SendCommand命令,先將結(jié)構(gòu)體進(jìn)行填充
    //用CmdError獲取一下SD的狀態(tài),判斷返回為SD_OK后,發(fā)送下一條命令
    //CMD8用來獲取卡的一些基本信息,返回的是SDIO_Response_Short響應(yīng)。
    //用CmdResp7Error來獲取SD的狀態(tài)信息,如果是OK,則將卡的版本和容量類型進(jìn)行讀取。
    //發(fā)送CMD55表示下面要發(fā)送的是APP命令,然后發(fā)送ACMD41帶RCA=0的應(yīng)用命令,將SD的RCA進(jìn)行配置。
    //判斷一些狀態(tài)以后,此函數(shù)完成上電操作。
   SDIO_DeInit();
   errorstatus = SD_PowerON();
   if (errorstatus != SD_OK)
   {
     /*!< CMD Response TimeOut (wait for CMDSENT flag) */
     return(errorstatus);
   }
   
   //第三個重要的操作,初始化卡,具體可以參考手冊的20.4.4卡識別過程
   //通知所有的激活卡卡發(fā)送CID:ALL_SEND_CID,然后等待R2的響應(yīng),響應(yīng)會發(fā)回到SDIO的寄存器SDIO_RESP1
   //我們將寄存器的值進(jìn)行讀出后放到  CID_Tab中
   //然后將激活的卡賦予一個地址,用CMD3給此卡分配CRA地址
   //如果卡設(shè)置了保護(hù)區(qū)域,則還需要配置CSD,用CMD9發(fā)送以后等待響應(yīng),響應(yīng)的格式還是R2
   //這樣,我們就獲取了CSD,同CID存入  CSD_Tab中SD_InitializeCards執(zhí)行完畢。
   errorstatus = SD_InitializeCards();
   if (errorstatus != SD_OK)
   {
     /*!< CMD Response TimeOut (wait for CMDSENT flag) */
     return(errorstatus);
   }
 
   //再調(diào)用一次SDIO的初始化配置,前面已經(jīng)設(shè)置過一次了,SPI模式。
   SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; 
   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);
 
   //獲取卡信息,包括SD_cid和SD_csd,具體來說,就是收到的127位CID和CSD中獲取數(shù)據(jù),并賦值給CID和CSD的結(jié)構(gòu)體。
   if (errorstatus == SD_OK)
   {
     errorstatus = SD_GetCardInfo(&SDCardInfo);
   }
 
   //然后發(fā)送CMD7命令選擇該卡
   if (errorstatus == SD_OK)
   {
     errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
   }
 
    //用SPI配置完成以后,再使能總線寬度為4位,里面對  SDIO_Init進(jìn)行了重新的初始化。
   if (errorstatus == SD_OK)
   {
     errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
   }  
 
   return(errorstatus);
 }

SD卡讀操作

讀單塊ReadBlock

  • 讀單塊我們必須設(shè)置為512字節(jié),這是SD卡決定的,不能修改。
  • 入口參數(shù)包括:從那個地址開始,連續(xù)讀512字節(jié)ReadAddr,以及讀取的數(shù)據(jù)存放的地址readbuff
  • 在進(jìn)行任何步驟的時候,我們都要雙向的查詢SD卡和SDIO模塊的狀態(tài),是否可以進(jìn)行下一步
 SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
 {
   SD_Error errorstatus = SD_OK;
   TransferError = SD_OK;
   TransferEnd = 0;
   StopCondition = 0;
   SDIO->DCTRL = 0x0;
   if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
   {
     BlockSize = 512;
     ReadAddr /= 512;
   }
   
    //SDIO的數(shù)據(jù)這里是第一次用,進(jìn)行一次配置,用BLOCK模式,將SD卡中的數(shù)據(jù)讀入SDIO中。 
   SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
   SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
   SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
   SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
   SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
   SDIO_DataConfig(&SDIO_DataInitStructure);
 
   //發(fā)送CMD17進(jìn)行連續(xù)一塊的命令,CMD18為連續(xù)多塊的操作,入口參數(shù)當(dāng)然是塊的首地址
   SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
   SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
   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);
 
    //然后獲取響應(yīng),響應(yīng)為短響應(yīng)
   errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);
   if (errorstatus != SD_OK)
   {
     return(errorstatus);
   }
 
    //確認(rèn)接收的各個狀態(tài)位都沒有錯誤后,獲取FIFO的狀態(tài),如果也沒有錯誤,則將8個FIFO中的數(shù)據(jù)全部讀出以后,保存到tempbuff,并且將tempbuff的位置+8。    
    while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
   {
     if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
     {
       for (count = 0; count < 8; count++)
       {
         *(tempbuff + count) = SDIO_ReadData();
       }
       tempbuff += 8;
     }
   }
 
   if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
   {
     SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
     errorstatus = SD_DATA_TIMEOUT;
     return(errorstatus);
   }
   else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
   {
     SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
     errorstatus = SD_DATA_CRC_FAIL;
     return(errorstatus);
   }
   else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
   {
     SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
     errorstatus = SD_RX_OVERRUN;
     return(errorstatus);
   }
   else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
   {
     SDIO_ClearFlag(SDIO_FLAG_STBITERR);
     errorstatus = SD_START_BIT_ERR;
     return(errorstatus);
   }
   //最后一次操作,如果FIFO中還有數(shù)據(jù),也繼續(xù)讀出來,一般8位讀完以后,當(dāng)然就沒數(shù)據(jù)啦。
   while (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)
   {
     *tempbuff = SDIO_ReadData();
     tempbuff++;
   }
    //成功讀取以后,將所有的標(biāo)志都清空,等待下一次傳輸。
   SDIO_ClearFlag(SDIO_STATIC_FLAGS);

    //如果配置為DMA模式,則512數(shù)據(jù)收到以后,將自動存入到readbuff中。
 #elif defined (SD_DMA_MODE)
     SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
     SDIO_DMACmd(ENABLE);
     SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
 #endif
 
   return(errorstatus);
 }

讀多塊ReadMultiBlocks

和讀單塊稍微有不同的是,單塊可以用輪詢方式,多塊我們用DMA方式更好,程序如下:

 SD_Error SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
 {
   SD_Error errorstatus = SD_OK;
   TransferError = SD_OK;
   TransferEnd = 0;
   StopCondition = 1;
   SDIO->DCTRL = 0x0;
 
   if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
   {
     BlockSize = 512;
     ReadAddr /= 512;
   }
 
   //上面的參數(shù)基本上和單塊是一樣的,這里我們先發(fā)送讀多塊的命令,塊大小還是512.
   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);
   }
     
    //將數(shù)據(jù)結(jié)構(gòu)體進(jìn)行配置,這里配置的數(shù)據(jù)長度為NumberOfBlocks * BlockSize;
   SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
   SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
   SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
   SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
   SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
   SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
   SDIO_DataConfig(&SDIO_DataInitStructure);
 
    //如上文,讀多塊用的是CMD18命令
   SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
   SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_MULT_BLOCK;
   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_READ_MULT_BLOCK);
   if (errorstatus != SD_OK)
   {
     return(errorstatus);
   }
 
   //中斷配置,所有的數(shù)據(jù)傳輸完成后進(jìn)行中斷,開啟DMA配置
   SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
   SDIO_DMACmd(ENABLE);
   SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, (NumberOfBlocks * BlockSize));
 
   return(errorstatus);
 }

DMA的配置如下:在中斷函數(shù)中,我們設(shè)置了清SDIO_IT_DATAEND標(biāo)志和中斷配置關(guān)閉,并返回卡的狀態(tài)SD_OK;

 void SD_LowLevel_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
 {
   DMA_InitTypeDef DMA_InitStructure;
   DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);
   DMA_Cmd(DMA2_Channel4, DISABLE);
 
   //確認(rèn)SDIO對應(yīng)的DMA通道為DMA2_Channel4
   DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
   DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
   DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
   DMA_InitStructure.DMA_Priority = DMA_Priority_High;
   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
   DMA_Init(DMA2_Channel4, &DMA_InitStructure);
 
   DMA_Cmd(DMA2_Channel4, ENABLE); 
 }

寫單塊和寫多塊

和讀單塊與多塊的流程差不多,不再介紹

SD擦除

先檢查CSD中的擦除命令,是否可以擦除
if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
然后獲取響應(yīng)寄存器中的值
if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
如果SD卡為大容量卡,則按照512一塊進(jìn)行操作,然后發(fā)送開始地址和截止地址
然后用CMD32和CMD33發(fā)送收尾地址,首地址發(fā)送的代碼如下:

/*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
     SDIO_CmdInitStructure.SDIO_Argument = startaddr;
     SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
     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_SD_ERASE_GRP_START);
     if (errorstatus != SD_OK)
     {
       return(errorstatus);
     }

然后發(fā)送一個擦除的命令,如下:

 /*!< Send CMD38 ERASE */
   SDIO_CmdInitStructure.SDIO_Argument = 0;
   SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
   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_ERASE);

然后進(jìn)入等待狀態(tài),我們這里有一個延時函數(shù),如果不用此延時,直接用
errorstatus = IsCardProgramming(&cardstate);
不知道是否OK,延時的計算如下:

maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);
 for (delay = 0; delay < maxdelay; delay++)
   {}

IsCardProgramming用來檢查看是否在編程狀態(tài),也就是在進(jìn)行擦除運(yùn)算中。
此時發(fā)送CMD13,并發(fā)送CRA后,等待響應(yīng)后,用respR1 = SDIO_GetResponse(SDIO_RESP1);獲取狀態(tài),然后我們根據(jù)SDIO_RESP1的內(nèi)容進(jìn)行判斷SD卡的運(yùn)行狀態(tài)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容