初始化和去初始化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)。