七.SDIO外設(shè)結(jié)構(gòu)體
其實前面關(guān)于SDIO寄存器的講解已經(jīng)比較詳細(xì)了,這里再借助于關(guān)于SDIO結(jié)構(gòu)體再進(jìn)行總結(jié)一遍。
標(biāo)準(zhǔn)庫函數(shù)對 SDIO 外設(shè)建立了三個初始化結(jié)構(gòu)體,分別為 SDIO 初始化結(jié)構(gòu)體SDIO_InitTypeDef、SDIO 命令初始化結(jié)構(gòu)體 SDIO_CmdInitTypeDef 和 SDIO 數(shù)據(jù)初始化結(jié)
構(gòu)體 SDIO_DataInitTypeDef。這些結(jié)構(gòu)體成員用于設(shè)置 SDIO 工作環(huán)境參數(shù),并由 SDIO 相應(yīng)初始化配置函數(shù)或功能函數(shù)調(diào)用,這些參數(shù)將會被寫入到 SDIO 相應(yīng)的寄存器,達(dá)到配置 SDIO 工作環(huán)境的目的。
至于為什么需要一個命令結(jié)構(gòu)體與數(shù)據(jù)結(jié)構(gòu)體,就是為了方便我們配置SDIO關(guān)于寄存器位,因為發(fā)送命令或者數(shù)據(jù)需要很多參數(shù)配置。
1.SDIO初始化結(jié)構(gòu)體
SDIO 初始化結(jié)構(gòu)體用于配置 SDIO 基本工作環(huán)境,比如時鐘分頻、時鐘沿、數(shù)據(jù)寬度等等。它被 SDIO_Init 函數(shù)使用。
1) SDIO_ClockEdge:主時鐘 SDIOCLK 產(chǎn)生 CLK 引腳時鐘有效沿選擇,可選上升沿或下降沿。
2) SDIO_ClockBypass:時鐘分頻旁路使用,可選使能或禁用,如果使能旁路,SDIOCLK (72MHZ )直接驅(qū)動 CLK 線輸出時鐘(不滿足最高25HZ的要求),如果禁用,使用 SDIO_CLKCR 寄存器的 CLKDIV 位值分頻 SDIOCLK,然后輸出到 CLK 線。一般選擇禁用時鐘分頻旁路。
3) SDIO_ClockPowerSave:節(jié)能模式選擇,可選使能或禁用,它設(shè)定 SDIO_CLKCR 寄存器的 PWRSAV 位的值。如果使能節(jié)能模式,CLK 線只有在總線激活時才有時鐘輸出;如果禁用節(jié)能模式,始終使能 CLK 線輸出時鐘。
4) SDIO_BusWide:數(shù)據(jù)線寬度選擇,可選 1 位數(shù)據(jù)總線、4 位數(shù)據(jù)總線或 8 為數(shù)據(jù)總線,系統(tǒng)默認(rèn)使用 1 位數(shù)據(jù)總線,操作 SD 卡時在數(shù)據(jù)傳輸模式下一般選擇 4 位數(shù)據(jù)總線。它設(shè)定 SDIO_CLKCR 寄存器的 WIDBUS 位的值。
5) SDIO_HardwareFlowControl:硬件流控制選擇,可選使能或禁用,它設(shè)定SDIO_CLKCR 寄存器的 HWFC_EN 位的值。硬件流控制功能可以避免 FIFO 發(fā)送上溢和下溢錯誤。
6) SDIO_ClockDiv:時鐘分頻系數(shù),它設(shè)定 SDIO_CLKCR 寄存器的 CLKDIV 位的值,設(shè)置 SDIOCLK 與 CLK 線輸出時鐘分頻系數(shù):
CLK 線時鐘頻率=SDIOCLK/([CLKDIV+2])。
2.SDIO命令初始化結(jié)構(gòu)體
1) SDIO_Argument:作為命令的一部分發(fā)送到卡的命令參數(shù),它設(shè)定 SDIO 參數(shù)寄存器(SDIO_ARG)的值。
(2) SDIO_CmdIndex:命令號選擇,它設(shè)定 SDIO 命令寄存器(SDIO_CMD)的 CMDINDEX位的值。
(3) SDIO_Response:響應(yīng)類型,SDIO 定義兩個響應(yīng)類型:長響應(yīng)和短響應(yīng)。根據(jù)命令號選擇對應(yīng)的響應(yīng)類型。SDIO 定義了四個 32 位的 SDIO 響應(yīng)寄存器(SDIO_RESPx,x=1…4),短響應(yīng)只用SDIO_RESP1,長響應(yīng)使用4個(SDIO_RESPx,x=1…4)。
1)命令響應(yīng)寄存器
2)SDIO響應(yīng)寄存器1~4
4) SDIO_Wait:等待類型選擇,有三種狀態(tài)可選,一種是無等待狀態(tài),超時檢測功能啟動,一種是等待中斷,另外一種是等待傳輸完成。
5) SDIO_CPSM:命令路徑狀態(tài)機(jī)控制,可選使能或禁用 CPSM。它設(shè)定 SDIO_CMD 寄存器的 CPSMEN 位的值
只要我們使能的了命令狀態(tài)機(jī),則下面發(fā)送命令和接收響應(yīng)的過程中的狀態(tài)轉(zhuǎn)換就不用我們管了
當(dāng)我們要發(fā)送命令,我們只需要配置這個命令初始化結(jié)構(gòu)體的成員,然后調(diào)用下圖這個函數(shù),則我們配置的參數(shù)寫入對應(yīng)的寄存器位中。
3.SDIO數(shù)據(jù)初始化結(jié)構(gòu)體
1) SDIO_DataTimeOut:設(shè)置數(shù)據(jù)傳輸以卡總線時鐘周期表示的超時周期,它設(shè)定 SDIO數(shù)據(jù)定時器寄存器(SDIO_DTIMER)的值。在 DPSM 進(jìn)入 Wait_R 或繁忙狀態(tài)后開始遞減,直到 0 還處于以上兩種狀態(tài)則將超時狀態(tài)標(biāo)志置 1(詳情前面的數(shù)據(jù)通道小節(jié))。
2) SDIO_DataLength:設(shè)置傳輸數(shù)據(jù)長度。
3) SDIO_DataBlockSize:設(shè)置數(shù)據(jù)塊大小,有多種尺寸可選,不同命令要求的數(shù)據(jù)塊可能不同。
4) SDIO_TransferDir:數(shù)據(jù)傳輸方向,可選從主機(jī)到卡的寫操作,或從卡到主機(jī)的讀操作。
5) SDIO_TransferMode:數(shù)據(jù)傳輸模式,可選數(shù)據(jù)塊或數(shù)據(jù)流模式。對于 SD 卡操作使用數(shù)據(jù)塊類型。
6) SDIO_DPSM:數(shù)據(jù)路徑狀態(tài)機(jī)控制,可選使能或禁用 DPSM。它設(shè)定 SDIO_DCTRL寄存器的 DTEN 位的值。要實現(xiàn)數(shù)據(jù)傳輸都必須使能 SDIO_DPSM。
與命令一樣使能了數(shù)據(jù)路徑狀態(tài)機(jī),就不用高那么多麻煩的狀態(tài)轉(zhuǎn)換了
八.SD卡讀寫測試實驗
我們平時使用的SD 卡都是已經(jīng)包含有文件系統(tǒng)的,一般不會使用本實驗的操作方式讀寫 SD 卡,但是對學(xué)習(xí)SD卡的驅(qū)動原理非常重要!!!
本實驗是進(jìn)行 SD卡最底層的數(shù)據(jù)讀寫操作,直接使用 SDIO 對 SD 卡進(jìn)行讀寫,會損壞 SD 卡的文件系統(tǒng),導(dǎo)致數(shù)據(jù)丟失,所以做這個實驗之前需要備份SD卡數(shù)據(jù)。
主要是學(xué)習(xí)SD卡的卡識別過程,以及數(shù)據(jù)傳輸工過程,其實就是完全依照前面的兩個流程圖來實現(xiàn)代碼的。
卡識別模式流程圖
數(shù)據(jù)傳輸流程圖
1.硬件設(shè)計
原理圖:
實物圖:
我這里用的是CS創(chuàng)世的貼片式SD卡,也稱之為SD NAND , 內(nèi)部存儲單元架構(gòu)為SLC,適合存代碼。直接上板時相比于拔插式SD卡在抗震和抗PIN氧化方面更有優(yōu)勢,對于縮小整板體積也有一定幫助。
詳情請參考:雷龍官網(wǎng)
2.代碼講解
先看主函數(shù):
SD_Terst函數(shù):
我們主要講解的就是SD卡的初始化
SD_Init()函數(shù):
/**
*函數(shù)名:SD_Init
*描述:初始化SD卡,使卡處于就緒狀態(tài)(準(zhǔn)備傳輸數(shù)據(jù))
*輸入:無
*輸出:-SD_ErrorSD卡錯誤代碼
*成功時則為SD_OK
*調(diào)用:外部調(diào)用
*/SD_ErrorSD_Init(void){
/*重置SD_Error狀態(tài)*/
SD_Errorerrorstatus=SD_OK;
NVIC_Configuration();
/*SDIO外設(shè)底層引腳初始化*/
GPIO_Configuration();
/*對SDIO的所有寄存器進(jìn)行復(fù)位*/
SDIO_DeInit();
/*上電并進(jìn)行卡識別流程,確認(rèn)卡的操作電壓*/
errorstatus=SD_PowerON();
/*如果上電,識別不成功,返回“響應(yīng)超時”錯誤*/
if(errorstatus!=SD_OK)
{
/*!
return(errorstatus);
}
/*卡識別成功,進(jìn)行卡初始化*/
errorstatus=SD_InitializeCards();
if(errorstatus!=SD_OK) //失敗返回
{
/*!
return(errorstatus);
}
/*配置SDIO外設(shè)
*上電識別,卡初始化都完成后,進(jìn)入數(shù)據(jù)傳輸模式,提高讀寫速度
*/
/*SDIOCLK=HCLK,SDIO_CK=HCLK/(2+SDIO_TRANSFER_CLK_DIV)*/
SDIO_InitStructure.SDIO_ClockDiv=SDIO_TRANSFER_CLK_DIV;
/*上升沿采集數(shù)據(jù)*/
SDIO_InitStructure.SDIO_ClockEdge=SDIO_ClockEdge_Rising;
/*Bypass模式使能的話,SDIO_CK不經(jīng)過SDIO_ClockDiv分頻*/
SDIO_InitStructure.SDIO_ClockBypass=SDIO_ClockBypass_Disable;
/*若開啟此功能,在總線空閑時關(guān)閉sd_clk時鐘*/
SDIO_InitStructure.SDIO_ClockPowerSave=SDIO_ClockPowerSave_Disable;
/*暫時配置成1bit模式*/
SDIO_InitStructure.SDIO_BusWide=SDIO_BusWide_1b;
/*硬件流,若開啟,在FIFO不能進(jìn)行發(fā)送和接收數(shù)據(jù)時,數(shù)據(jù)傳輸暫停*/
SDIO_InitStructure.SDIO_HardwareFlowControl=SDIO_HardwareFlowControl_Disable;
SDIO_Init(&SDIO_InitStructure);
if(errorstatus==SD_OK)
{
/*用來讀取csd/cid寄存器*/
errorstatus=SD_GetCardInfo(&SDCardInfo);
}
if(errorstatus==SD_OK)
{
/*通過cmd7,rca選擇要操作的卡*/
errorstatus=SD_SelectDeselect((uint32_t)(SDCardInfo.RCA<16));
}
if(errorstatus==SD_OK)
{
/*最后為了提高讀寫,開啟4bits模式*/
errorstatus=SD_EnableWideBusOperation(SDIO_BusWide_4b);
}
return(errorstatus);}
接下來逐段代碼來分析一下:
errorstatus其實是一個SD_Error類型的枚舉變量,SD_Error 是
列舉了控制器可能出現(xiàn)的錯誤、比如 CRC 校驗錯誤、CRC 校驗錯誤、通信等待超時、FIFO 上溢或下溢、擦除命令錯誤等等。這些錯誤類型部分是控制器系統(tǒng)寄存器的標(biāo)志位,部分是通過命令的響應(yīng)內(nèi)容得到的,如果是SD_OK則代表沒有發(fā)送錯誤,
配置SDIO中斷:
SDIO 外設(shè)底層引腳初始化
復(fù)位所有SDIO寄存器
重點來了:調(diào)用SD_PowerON()進(jìn)入卡識別模式
/*
*函數(shù)名:SD_PowerON
*描述:確保SD卡的工作電壓和配置控制時鐘
*輸入:無
*輸出:-SD_ErrorSD卡錯誤代碼
*成功時則為SD_OK
*調(diào)用:在SD_Init()調(diào)用
*/SD_ErrorSD_PowerON(void){
SD_Errorerrorstatus=SD_OK;
uint32_tresponse=0,count=0,validvoltage=0;
uint32_tSDType=SD_STD_CAPACITY;
/********************************************************************************************************/
/*上電初始化
*配置SDIO的外設(shè)
*SDIOCLK=HCLK,SDIO_CK=HCLK/(2+SDIO_INIT_CLK_DIV)
*初始化時的時鐘不能大于400KHz
*/
/*HCLK=72MHz,SDIOCLK=72MHz,SDIO_CK=HCLK/(178+2)=400KHz*/
SDIO_InitStructure.SDIO_ClockDiv=SDIO_INIT_CLK_DIV;
SDIO_InitStructure.SDIO_ClockEdge=SDIO_ClockEdge_Rising;
/*不使用bypass模式,直接用HCLK進(jìn)行分頻得到SDIO_CK*/
SDIO_InitStructure.SDIO_ClockBypass=SDIO_ClockBypass_Disable;
/*空閑時不關(guān)閉時鐘電源*/
SDIO_InitStructure.SDIO_ClockPowerSave=SDIO_ClockPowerSave_Disable;
/*初始化的時候暫時先把數(shù)據(jù)線配置成1根*/
SDIO_InitStructure.SDIO_BusWide=SDIO_BusWide_1b;
/*失能硬件流控制*/
SDIO_InitStructure.SDIO_HardwareFlowControl=SDIO_HardwareFlowControl_Disable;
SDIO_Init(&SDIO_InitStructure);
/*開啟SDIO外設(shè)的電源*/
SDIO_SetPowerState(SDIO_PowerState_ON);
/*使能SDIO時鐘*/
SDIO_ClockCmd(ENABLE);/********************************************************************************************************/
/*下面發(fā)送一系列命令,開始卡識別流程
*CMD0:GO_IDLE_STATE(復(fù)位所以SD卡進(jìn)入空閑狀態(tài))
*沒有響應(yīng)
*/
SDIO_CmdInitStructure.SDIO_Argument=0x0;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_GO_IDLE_STATE;
/*沒有響應(yīng)*/
SDIO_CmdInitStructure.SDIO_Response=SDIO_Response_No;
/*關(guān)閉等待中斷*/
SDIO_CmdInitStructure.SDIO_Wait=SDIO_Wait_No;
/*CPSM在開始發(fā)送命令之前等待數(shù)據(jù)傳輸結(jié)束*/
SDIO_CmdInitStructure.SDIO_CPSM=SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
/*檢測是否正確接收到cmd0*/
errorstatus=CmdError();
/*命令發(fā)送出錯,返回*/
if(errorstatus!=SD_OK)
{
/*CMDResponseTimeOut(waitforCMDSENTflag)*/
return(errorstatus);
}/********************************************************************************************************/
/*CMD8:SEND_IF_COND
*發(fā)送CMD8檢查SD卡的電壓操作條件
*
*參數(shù):-[31:12]:保留(要被設(shè)置為'0')
*-[11:8]:支持的電壓(VHS)0x1(范圍:2.7-3.6V)
*-[7:0]:校驗?zāi)J?推薦0xAA)
*響應(yīng)類型:R7
*/
/*接收到命令sd會返回這個參數(shù)*/
SDIO_CmdInitStructure.SDIO_Argument=SD_CHECK_PATTERN;
SDIO_CmdInitStructure.SDIO_CmdIndex=SDIO_SEND_IF_COND;
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=CmdResp7Error();
/*有響應(yīng)則card遵循sd協(xié)議2.0版本*/
if(errorstatus==SD_OK)
{
/*SDCard2.0,先把它定義會sdsc類型的卡*/
CardType=SDIO_STD_CAPACITY_SD_CARD_V2_0;
/*這個變量用作ACMD41的參數(shù),用來詢問是sdsc卡還是sdhc卡*/
SDType=SD_HIGH_CAPACITY;
}
else /*無響應(yīng),說明是1.x的或mmc的卡*/
{
/*發(fā)命令CMD55*/
SDIO_CmdInitStructure.SDIO_Argument=0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_APP_CMD;
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_APP_CMD);
}
/*CMD55
*發(fā)送cmd55,用于檢測是sd卡還是mmc卡,或是不支持的卡
*CMD響應(yīng):R1
*/
SDIO_CmdInitStructure.SDIO_Argument=0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_APP_CMD;
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)的是mmc或不支持的卡*/
errorstatus=CmdResp1Error(SD_CMD_APP_CMD); /********************************************************************************************************/
/*若errorstatus為CommandTimeOut,說明是MMC卡
*若errorstatus為SD_OK,說明是SDcard:SD卡2.0(電壓范圍不匹配)
*或SD卡1.x
*/
if(errorstatus==SD_OK) //響應(yīng)了cmd55,是sd卡,可能為1.x,可能為2.0
{
/*下面開始循環(huán)地發(fā)送sdio支持的電壓范圍,循環(huán)一定次數(shù)*/
/*SDCARD
*發(fā)送ACMD41SD_APP_OP_COND,帶參數(shù)0x80100000
*/
while((!validvoltage)&&(count
{
/*在發(fā)送ACMD命令前都要先向卡發(fā)送CMD55
*發(fā)送CMD55APP_CMD,RCA為0
*/
SDIO_CmdInitStructure.SDIO_Argument=0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_APP_CMD;
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_APP_CMD);
if(errorstatus!=SD_OK)
{
return(errorstatus);
}
/*ACMD41
*命令參數(shù)由支持的電壓范圍及HCS位組成,HCS位置一來區(qū)分卡是SDSC還是SDHC
*0:SDSC
*1:SDHC
*響應(yīng):R3,對應(yīng)的是OCR寄存器
*/
SDIO_CmdInitStructure.SDIO_Argument=SD_VOLTAGE_WINDOW_SD|SDType;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_SD_APP_OP_COND;
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=CmdResp3Error();
if(errorstatus!=SD_OK)
{
return(errorstatus);
}
/*若卡需求電壓在SDIO的供電電壓范圍內(nèi),會自動上電并標(biāo)志pwr_up位
*讀取卡寄存器,卡狀態(tài)
*/
response=SDIO_GetResponse(SDIO_RESP1);
/*讀取卡的ocr寄存器的pwr_up位,看是否已工作在正常電壓*/
validvoltage=(((response>>31)==1)?1:0);
count++; /*計算循環(huán)次數(shù)*/
}
if(count>=SD_MAX_VOLT_TRIAL) /*循環(huán)檢測超過一定次數(shù)還沒上電*/
{
errorstatus=SD_INVALID_VOLTRANGE; /*SDIO不支持card的供電電壓*/
return(errorstatus);
}
/*檢查卡返回信息中的HCS位*/
/*判斷ocr中的ccs位,如果是sdsc卡則不執(zhí)行下面的語句*/
if(response&=SD_HIGH_CAPACITY)
{
CardType=SDIO_HIGH_CAPACITY_SD_CARD;/*把卡類型從初始化的sdsc型改為sdhc型*/
}
}/*elseMMCCard*/
return(errorstatus); }
1.配置SDIO初始化結(jié)構(gòu)體**
配置 SDIO_InitStructure 結(jié)構(gòu)體變量成員并調(diào)用 SDIO_Init 庫函數(shù)完成 SDIO 外設(shè)的基本配置,注意此處的 SDIO 時鐘分頻,由于處于卡識別階段,其時鐘不能超過 400KHz。
2.發(fā)送CMD0命令:要SD卡回到空閑狀態(tài)
那些檢測標(biāo)志全是來源與下圖:
3.發(fā)送CMD8: 用來識別不同版本的卡和檢測卡是否能在主機(jī)提供的電壓下工作。
如果發(fā)送CMD8無響應(yīng):
1.電壓不匹配的 2.0 以上 SD 卡
2.1.0 的 SD 卡
3.不是 SD 卡
如果發(fā)送CMD8有響應(yīng):
電壓匹配的 2.0 以上 SD 卡(就是我們即將要使用的SD卡)
4.使用 ACMD41 命令判斷卡的具體類型。因為是 A 類命令,所以在發(fā)送 ACMD41之前必須先發(fā)送 CMD55,CMD55 命令的響應(yīng)類型的 R1。如果 CMD55 命令都沒有響應(yīng)說明是 MMC 卡或不可用卡。在正確發(fā)送 CMD55 之后就可以送ACMD41,并根據(jù)響應(yīng)判斷卡類型,ACMD41 的響應(yīng)號為 R3,CmdResp3Error 函數(shù)用于檢測命令正確發(fā)送并帶有超時檢測功能,但并不具備響應(yīng)內(nèi)容接收功能,需要在判定命令正確發(fā)送之后調(diào)用 SDIO_GetResponse 函數(shù)才能獲取響應(yīng)的內(nèi)容。
實際上,在有響應(yīng)時,SDIO 外設(shè)會自動把響應(yīng)存放在 SDIO_RESPx 寄存器中,SDIO_GetResponse 函數(shù)只是根據(jù)形參返回對應(yīng)響應(yīng)寄存器的值。通過判定響應(yīng)內(nèi)容值即可確定 SD 卡類型。
總結(jié):執(zhí)行 SD_PowerON 函數(shù)無錯誤后就已經(jīng)確定了 SD 卡類型,并說明卡和主機(jī)電壓是匹配的,SD 卡處于卡識別模式下的準(zhǔn)備狀態(tài)。退出 SD_PowerON 函數(shù)返回SD_Init 函數(shù),執(zhí)行接下來代碼。
執(zhí)行 SD_PowerON 函數(shù)沒有錯誤后:SD 卡處于卡識別模式下的準(zhǔn)備狀態(tài)
SD_InitializeCards()函數(shù):
/*
*函數(shù)名:SD_InitializeCards
*描述:初始化所有的卡或者單個卡進(jìn)入就緒狀態(tài)
*輸入:無
*輸出:-SD_ErrorSD卡錯誤代碼
*成功時則為SD_OK
*調(diào)用:在SD_Init()調(diào)用,在調(diào)用power_on()上電卡識別完畢后,調(diào)用此函數(shù)進(jìn)行卡初始化
*/SD_ErrorSD_InitializeCards(void){
SD_Errorerrorstatus=SD_OK;
uint16_trca=0x01;
if(SDIO_GetPowerState()==SDIO_PowerState_OFF)
{
errorstatus=SD_REQUEST_NOT_APPLICABLE;
return(errorstatus);
}
/*判斷卡的類型*/
if(SDIO_SECURE_DIGITAL_IO_CARD!=CardType)
{
/*SendCMD2ALL_SEND_CID
*響應(yīng):R2,對應(yīng)CID寄存器
*/
SDIO_CmdInitStructure.SDIO_Argument=0x0;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_ALL_SEND_CID;
SDIO_CmdInitStructure.SDIO_Response=SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait=SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM=SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp2Error();
if(SD_OK!=errorstatus)
{
return(errorstatus);
}
/*將返回的CID信息存儲起來*/
CID_Tab[0]=SDIO_GetResponse(SDIO_RESP1);
CID_Tab[1]=SDIO_GetResponse(SDIO_RESP2);
CID_Tab[2]=SDIO_GetResponse(SDIO_RESP3);
CID_Tab[3]=SDIO_GetResponse(SDIO_RESP4);
}/********************************************************************************************************/
if((SDIO_STD_CAPACITY_SD_CARD_V1_1==CardType)
||(SDIO_STD_CAPACITY_SD_CARD_V2_0==CardType)
||(SDIO_SECURE_DIGITAL_IO_COMBO_CARD==CardType)
||(SDIO_HIGH_CAPACITY_SD_CARD==CardType)) /*使用的是2.0的卡*/
{
/*SendCMD3SET_REL_ADDRwithargument0
*SDCardpublishesitsRCA.
*響應(yīng):R6,對應(yīng)RCA寄存器
*/
SDIO_CmdInitStructure.SDIO_Argument=0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_SET_REL_ADDR;
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=CmdResp6Error(SD_CMD_SET_REL_ADDR,&rca);
if(SD_OK!=errorstatus)
{
return(errorstatus);
}
}/********************************************************************************************************/
if(SDIO_SECURE_DIGITAL_IO_CARD!=CardType)
{
RCA=rca;
/*SendCMD9SEND_CSDwithargumentascard'sRCA
*響應(yīng):R2對應(yīng)寄存器CSD(Card-SpecificData)
*/
SDIO_CmdInitStructure.SDIO_Argument=(uint32_t)(rca<16);
SDIO_CmdInitStructure.SDIO_CmdIndex=SD_CMD_SEND_CSD;
SDIO_CmdInitStructure.SDIO_Response=SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait=SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM=SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp2Error();
if(SD_OK!=errorstatus)
{
return(errorstatus);
}
CSD_Tab[0]=SDIO_GetResponse(SDIO_RESP1);
CSD_Tab[1]=SDIO_GetResponse(SDIO_RESP2);
CSD_Tab[2]=SDIO_GetResponse(SDIO_RESP3);
CSD_Tab[3]=SDIO_GetResponse(SDIO_RESP4);
}/********************************************************************************************************/
/*全部卡初始化成功*/
errorstatus=SD_OK;
return(errorstatus);}
1.判斷 SDIO 電源是否啟動,如果沒有啟動電源返回錯誤。
2.發(fā)送CMD2命令 :是用于通知所有卡通過 CMD 線返回 CID 值,執(zhí)行 CMD2 發(fā)送之后就可以使用 CmdResp2Error 函數(shù)獲取 CMD2 命令發(fā)送情況,發(fā)送無錯誤后即可以使用 SDIO_GetResponse 函數(shù)獲取響應(yīng)內(nèi)容,它是個長響應(yīng),我們把 CMD2 響應(yīng)內(nèi)容存放在 CID_Tab 數(shù)組內(nèi)。
3.發(fā)送CMD3命令,用于指示 SD 卡自行推薦 RCA 地址,CMD3 的響應(yīng)為 R6 類型,CmdResp6Error 函數(shù)用于檢查 R6 響應(yīng)錯誤,它有兩個形參,一個是命令號,這里為 CMD3,另外一個是 RCA 數(shù)據(jù)指針,這里使用 rca變量的地址賦值給它,使得在 CMD3 正確響應(yīng)之后 rca 變量即存放 SD 卡的 RCA。
CmdResp6Error 函數(shù)通用會對每個錯誤位進(jìn)行必要的檢測,如果發(fā)現(xiàn)有錯誤存在則直接返回對應(yīng)錯誤類型。
執(zhí)行完CmdResp6Error 函數(shù)之后返回到 SD_InitializeCards 函數(shù)中,如果判斷無錯誤說明此刻 SD 卡已經(jīng)處于數(shù)據(jù)傳輸模式。
4.發(fā)送 CMD9 給指定 RCA 的 SD 卡使其發(fā)送返回其 CSD 寄存器內(nèi)容,這里的 RCA就是在 CmdResp6Error 函數(shù)獲取得到的 rca。最后把響應(yīng)內(nèi)容存放在 CSD_Tab 數(shù)組中。
執(zhí)行 SD_InitializeCards 函數(shù)無錯誤后 SD 卡就已經(jīng)處于數(shù)據(jù)傳輸模式下的待機(jī)狀態(tài),退出 SD_InitializeCards 后會返回前面的 SD_Init 函數(shù),執(zhí)行接下來代碼,以下是 SD_Init 函數(shù)的后續(xù)執(zhí)行過程:
1) 重新配置 SDIO 外設(shè),提高時鐘頻率,之前的卡識別模式都設(shè)定 CMD 線時鐘為小于 400KHz,進(jìn)入數(shù)據(jù)傳輸模式可以把時鐘設(shè)置為小于 25MHz,以便提高數(shù)據(jù)傳輸速率。
(2) 調(diào)用 SD_GetCardInfo 函數(shù)獲取 SD 卡信息,它需要一個指向 SD_CardInfo 類型變量地址的指針形參,這里賦值為 SDCardInfo 變量的地址。SD 卡信息主要是 CID和 CSD 寄存器內(nèi)容,這兩個寄存器內(nèi)容在 SD_InitializeCards 函數(shù)中都完成讀取過程并將其分別存放在CID_Tab 數(shù)組和CSD_Tab 數(shù)組中,SD_GetCardInfo 函數(shù)只是簡單的把這兩個數(shù)組內(nèi)容整合復(fù)制到 SDCardInfo 變量對應(yīng)成員內(nèi)。正確執(zhí)行 SD_GetCardInfo 函數(shù)后,SDCardInfo 變量就存放了 SD 卡的很多狀態(tài)信息,這在之后應(yīng)用中使用頻率是很高的。
結(jié)構(gòu)體類型定義:有 SD_CSD、SD_CID、SD_CardStatus 以及 SD_CardInfo。SD_CSD 定義了 SD 卡的特定數(shù)據(jù)(CSD)寄存器位,一般提供 R2 類型的響應(yīng)可以獲取得到 CSD 寄存器內(nèi)容。SD_CID 結(jié)構(gòu)體類似 SD_CSD 結(jié)構(gòu)體,它定義 SD 卡CID 寄存器內(nèi)容,也是通過 R2 響應(yīng)類型獲取得到。SD_CardStatus 結(jié)構(gòu)體定義了SD 卡狀態(tài),有數(shù)據(jù)寬度、卡類型、速度等級、擦除寬度、傳輸偏移地址等等 SD卡狀態(tài)。SD_CardInfo 結(jié)構(gòu)體定義了 SD 卡信息,包括了 SD_CSD 類型和 SD_CID類型成員,還有定義了卡容量、卡塊大小、卡相對地址 RCA 和卡類型成員。
主要是存儲卡的容量,卡的大小,RCA地址,卡的類型(這些是關(guān)鍵信息,由命令響應(yīng)返回然后存入這個結(jié)構(gòu)體中)
3) 調(diào)用 SD_SelectDeselect 函數(shù)用于選擇特定 RCA 的 SD 卡,它實際是向 SD 卡發(fā)送CMD7。執(zhí)行之后,卡就從待機(jī)狀態(tài)轉(zhuǎn)變?yōu)閭鬏斈J?,可以說數(shù)據(jù)傳輸已經(jīng)是萬事俱備了
4) 擴(kuò)展數(shù)據(jù)線寬度,之前的所有操作都是使用一根數(shù)據(jù)線傳輸完成的,使用 4 根數(shù)據(jù)線可以提高傳輸性能,調(diào)用可以設(shè)置數(shù)據(jù)線寬度,函數(shù)只有一個形參,用于指定數(shù)據(jù)線寬度。
至此,SD_Init 函數(shù)已經(jīng)全部執(zhí)行完成。如果程序可以正確執(zhí)行,接下來就可以進(jìn)行SD 卡讀寫以及擦除等操作。
SD_EraseTest()函數(shù)
SD_Erase()函數(shù):
1) 檢查 SD 卡是否支持擦除功能,如果不支持則直接返回錯誤。為保證擦除指令正常進(jìn)行,要求主機(jī)一個遵循下面的命令序列發(fā)送指令:CMD32->CMD33->CMD38。如果發(fā)送順序不對,SD 卡會設(shè)置 ERASE_SEQ_ERROR 位到狀態(tài)寄存器
2) SD_Erase 函數(shù)發(fā)送 CMD32 指令用于設(shè)定擦除塊開始地址,在執(zhí)行無錯誤后發(fā)送CMD33 設(shè)置擦除塊的結(jié)束地址。
3) 發(fā)送擦除命令 CMD38,使得 SD 卡進(jìn)行擦除操作。SD 卡擦除操作由 SD 卡內(nèi)部控制完成,不同卡擦除后是 0xff 還是 0x00 由廠家決定。擦除操作需要花費一定時間,這段時間不能對 SD 卡進(jìn)行其他操作。
4) 通過 IsCardProgramming 函數(shù)可以檢測 SD 卡是否處于編程狀態(tài)(即卡內(nèi)部的擦寫狀態(tài)),需要確保 SD 卡擦除完成才退出 SD_Erase 函數(shù)。IsCardProgramming 函數(shù)先通過發(fā)送CMD13 命令 SD 卡發(fā)送它的狀態(tài)寄存器內(nèi)容,并對響應(yīng)內(nèi)容進(jìn)行分析得出當(dāng)前 SD 卡的狀態(tài)以及可能發(fā)送的錯誤。
數(shù)據(jù)寫入操作
SD_WriteBlock 函數(shù)用于向指定的目標(biāo)地址寫入一個塊的數(shù)據(jù),它有三個形參,分別為指向待寫入數(shù)據(jù)的首地址的指針變量、目標(biāo)寫入地址和塊大小。塊大小一般都設(shè)置為512 字節(jié)。SD_WriteBlock 寫入函數(shù)的執(zhí)行流程如下:
1) SD_WriteBlock 函數(shù)開始時將 SDIO 數(shù)據(jù)控制寄存器 (SDIO_DCTRL)清零,復(fù)位之前的傳輸設(shè)置。
2) 對 SD 卡進(jìn)行數(shù)據(jù)讀寫之前,都必須發(fā)送 CMD16 指定塊的大小,對于標(biāo)準(zhǔn)卡,要寫入BlockSize 長度字節(jié)的塊;對于 SDHC 卡,寫入固定為 512 字節(jié)的塊。接下來就可以發(fā)送塊寫入命令 CMD24 通知 SD 卡要進(jìn)行數(shù)據(jù)寫入操作,并指定待寫入數(shù)據(jù)的目標(biāo)地址。
3) 利用 SDIO_DataInitTypeDef 結(jié)構(gòu)體類型變量配置數(shù)據(jù)傳輸?shù)某瑫r、塊數(shù)量、數(shù)據(jù)塊大小、數(shù)據(jù)傳輸方向等參數(shù)并使用 SDIO_DataConfig 函數(shù)完成數(shù)據(jù)傳輸環(huán)境配置。
4) 調(diào)用 SDIO_ITConfig 函數(shù)使能 SDIO 數(shù)據(jù)結(jié)束傳輸結(jié)束中斷,傳輸結(jié)束時,會跳轉(zhuǎn)到SDIO 的中斷服務(wù)函數(shù)運行。
5)SD_DMA_TxConfig 函數(shù),配置使能 SDIO 數(shù)據(jù)向 SD 卡的數(shù)據(jù)傳輸?shù)腄MA 請求,為使 SDIO 發(fā)送 DMA 請求,需要調(diào)用
SDIO_DMACmd 函數(shù)使能。對于高容量的 SD 卡要求塊大小必須為 512 字節(jié),SDIO 外設(shè)會自動生成 DMA 發(fā)送請求,將指定數(shù)據(jù)使用 DMA 傳輸寫入到 SD 卡內(nèi)。
普通模式需要自己去處理那些溢出什么的太麻煩了,用DMA傳輸數(shù)據(jù)就好了
DMA外設(shè)配置(不清楚的參考:DMA外設(shè)詳解):
寫入操作等待函數(shù)
SD_WaitWriteOperation 函數(shù)用于檢測和等待數(shù)據(jù)寫入完成,在調(diào)用數(shù)據(jù)寫入函數(shù)之后一般都需要調(diào)用,SD_WaitWriteOperation 函數(shù)適用于單塊及多塊寫入函數(shù)。
SDIO 中斷服務(wù)函數(shù)
在進(jìn)行數(shù)據(jù)傳輸操作時都會使能相關(guān)標(biāo)志中斷,用于跟蹤傳輸進(jìn)程和錯誤檢測。
SD_ProcessIRQSrc 函數(shù)首先判斷全局變量 StopCondition 變量是否為 1,該全局變量在SDIO 的多塊讀寫函數(shù)中被置 1(前面分析的單塊讀寫函數(shù)中 StopCondition 均為 0),因為根據(jù) SD 卡的要求,多塊讀寫命令由 CMD12 結(jié)束,SD 卡在接收到該命令時才停止多塊的傳輸,此處正是根據(jù) StopCondition 的情況控制是否發(fā)送 CMD12 命令,它發(fā)送命令時直接采用往寄存器寫入命令和參數(shù)的方式。
調(diào)用庫函數(shù) SD_DMAEndOfTransferStatus 一直檢測 DMA 的傳輸完成標(biāo)志,當(dāng) DMA 傳輸結(jié)束時,該函數(shù)會返回 SET 值。另外,while 循環(huán)中的判斷條件使用的TransferEnd 和 TransferError 是全局變量,它們會在 SDIO 的中斷服務(wù)函數(shù)根據(jù)傳輸情況被設(shè)置,傳輸結(jié)束后,根據(jù) TransferError 的值來確認(rèn)是否正確傳輸,若不正確則直接返回錯
誤代碼。SD_WaitWriteOperation 函數(shù)最后是清除相關(guān)標(biāo)志位并返回錯誤。
數(shù)據(jù)讀取操作
同向 SD 卡寫入數(shù)據(jù)類似,從 SD 卡讀取數(shù)據(jù)可分為單塊讀取和多塊讀取。這里僅介紹單塊讀操作函數(shù),多塊讀操作類似。
這一部分自己看代碼吧,操作差不多,已經(jīng)人麻了太多了。
還有多塊讀取與多塊寫入,其實是一樣的,只不過傳輸結(jié)束需要發(fā)送CMD12來結(jié)束傳輸。
總結(jié):代碼太多了,但是核心的東西已經(jīng)講完了,自己去看代碼悟一下,其實前面的理論部分懂了,代碼部分是完全按照理論來走的,只不過多了一點點細(xì)節(jié),就這樣咯,那些邊邊角角留給你們。
3.實驗結(jié)果
【本文轉(zhuǎn)載自CSDN,作者:rivencode】
-
FlaSh
+關(guān)注
關(guān)注
10文章
1635瀏覽量
148035 -
閃存芯片
+關(guān)注
關(guān)注
1文章
120瀏覽量
19619 -
存儲芯片
+關(guān)注
關(guān)注
11文章
897瀏覽量
43147 -
emmc
+關(guān)注
關(guān)注
7文章
216瀏覽量
52746
發(fā)布評論請先 登錄
相關(guān)推薦
評論