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