SDIO-SD卡
SD卡結(jié)構(gòu)
物理結(jié)構(gòu)
- 存儲(chǔ)單元是存儲(chǔ)數(shù)據(jù)部件,存儲(chǔ)單元通過(guò)存儲(chǔ)單元接口與卡控制單元進(jìn)行數(shù)據(jù)傳輸;
- 電源檢測(cè)單元保證SD卡工作在合適的電壓下,如出現(xiàn)掉電或上狀態(tài)時(shí),它會(huì)使控制單元和存儲(chǔ)單元接口復(fù)位;
- 卡及接口控制單元控制SD卡的運(yùn)行狀態(tài),它包括有8個(gè)寄存器;
- 接口驅(qū)動(dòng)器控制SD卡引腳的輸入輸出
- SD卡總共有8個(gè)寄存器,用于設(shè)定或表示SD卡信息。
SD卡寄存器列表
SDIO總線(xiàn)
SDIO總線(xiàn)拓?fù)?/h3>
SD卡與SDIO接口示意圖
推薦一個(gè)單獨(dú)SD總線(xiàn)應(yīng)該連接一個(gè)單獨(dú)的SD卡
SD卡使用9-pin接口通信,其中3根電源線(xiàn)、1根時(shí)鐘線(xiàn)、1根命令線(xiàn)和4根數(shù)據(jù)線(xiàn),具體如下:
? CLK:時(shí)鐘線(xiàn),由SDIO主機(jī)產(chǎn)生,即由STM32控制器輸出;
? CMD:命令控制線(xiàn),SDIO主機(jī)通過(guò)該線(xiàn)發(fā)送命令控制SD卡,如果命令要求SD卡提供應(yīng)答,SD卡也是通過(guò)該線(xiàn)傳輸應(yīng)答信息;
? D0-3:數(shù)據(jù)線(xiàn),傳輸讀寫(xiě)數(shù)據(jù);SD卡可將D0拉低表示忙狀態(tài);
? VDD、VSS1、VSS2:電源和地信號(hào);
SDIO總線(xiàn)
SDIO不管是從主機(jī)控制器向SD卡傳輸,還是SD卡向主機(jī)控制器傳輸都只以CLK時(shí)鐘線(xiàn)的上升沿為有效。
開(kāi)始通訊的時(shí)候要發(fā)送一個(gè)起始位"0",最后由一個(gè)停止位"1"停止。
SD通訊一般是主機(jī)發(fā)送一個(gè)命令,從設(shè)備接收到命令后做出響應(yīng)(有些命令沒(méi)有響應(yīng)可能)如果有數(shù)據(jù)傳輸,DATA線(xiàn)也需要用
SDIO總線(xiàn)協(xié)議
補(bǔ)充:
- SD數(shù)據(jù)是以塊(Black)形式傳輸?shù)?,SDHC卡數(shù)據(jù)塊長(zhǎng)度一般為512字節(jié)
- 數(shù)據(jù)塊需要CRC位來(lái)保證數(shù)據(jù)傳輸成功。
- CRC位由SD卡系統(tǒng)硬件生成。
- STM32控制器可以控制使用單線(xiàn)或4線(xiàn)傳輸,本開(kāi)發(fā)板(野火指南者)設(shè)計(jì)使用4線(xiàn)傳輸
協(xié)議:
SD數(shù)據(jù)傳輸支持單塊和多塊讀寫(xiě),它們分別對(duì)應(yīng)不同的操作命令,多塊寫(xiě)入還需要使用命令來(lái)停止整個(gè)寫(xiě)入操作。
數(shù)據(jù)寫(xiě)入前需要檢測(cè)SD卡忙狀態(tài),因?yàn)镾D卡在接收到數(shù)據(jù)后編程到存儲(chǔ)區(qū)過(guò)程需要一定操作時(shí)間。SD卡忙狀態(tài)通過(guò)把D0線(xiàn)拉低表示。
常規(guī)數(shù)據(jù)傳輸
使用4數(shù)據(jù)線(xiàn)傳輸時(shí),每次傳輸4bit數(shù)據(jù),每根數(shù)據(jù)線(xiàn)都必須有起始位、終止位以及CRC位,CRC位每根數(shù)據(jù)線(xiàn)都要分別檢查,并把檢查結(jié)果匯總?cè)缓笤跀?shù)據(jù)傳輸完后通過(guò)D0線(xiàn)反饋給主機(jī)
寬位數(shù)據(jù)包
對(duì)SD卡而言寬位數(shù)據(jù)包發(fā)送方式是針對(duì)SD卡SSR(SD狀態(tài))寄存器內(nèi)容發(fā)送的,SSR寄存器總共有512bit,在主機(jī)發(fā)出ACMD13命令后SD卡將SSR寄存器內(nèi)容通過(guò)DAT線(xiàn)發(fā)送給主機(jī)。
命令
命令格式
命令的類(lèi)型
? 無(wú)響應(yīng)廣播命令 (bc),發(fā)送到所有卡,不返回任務(wù)響應(yīng);
? 帶響應(yīng)廣播命令 (bcr),發(fā)送到所有卡,同時(shí)接收來(lái)自所有卡響應(yīng);
? 尋址命令 (ac),發(fā)送到選定卡,DAT 線(xiàn)無(wú)數(shù)據(jù)傳輸;
? 尋址數(shù)據(jù)傳輸命令 (adtc),發(fā)送到選定卡,DAT 線(xiàn)有數(shù)據(jù)傳輸。
在標(biāo)準(zhǔn)中定義了兩種類(lèi)型的通用命令:特定應(yīng)用命令 (ACMD) 和常規(guī)命令 (GEN_CMD)。要使用 SD 卡制造商特定的 ACMD 命令如 ACMD6,需要在發(fā)送該命令之前發(fā)送 CMD55 命令,告知 SD 卡接下來(lái)的命令為特定應(yīng)用命令。CMD55 命令只對(duì)緊接的第一個(gè)命令有效,SD 卡如果檢測(cè)到 CMD55 之后的第一條命令為 ACMD 則執(zhí)行其特定應(yīng)用功能,如果檢測(cè)發(fā)現(xiàn)不是 ACMD 命令,則執(zhí)行標(biāo)準(zhǔn)命令。
命令集
七個(gè)響應(yīng)類(lèi)型:
-
R1 Response (Normal Response): R1響應(yīng)是最基本的響應(yīng),包含一個(gè)字節(jié)的狀態(tài)位,用于指示命令是否成功執(zhí)行。
-
R1b Response (Normal with Busy): 類(lèi)似于R1,但在命令執(zhí)行完成后,卡會(huì)持續(xù)將忙位(busy)發(fā)送給主機(jī),直到卡準(zhǔn)備好執(zhí)行下一個(gè)命令。
-
R2 Response (CID or CSD Register Response): 包含兩個(gè)字節(jié)的數(shù)據(jù),用于讀取CID(Card ID)或CSD(Card Specific Data)寄存器內(nèi)容。
-
R3 Response (OCR Register Response): 包含四個(gè)字節(jié)的數(shù)據(jù),用于讀取OCR(Operating Conditions Register)寄存器內(nèi)容。
-
R6 Response (Published RCA Response): 包含一個(gè)字節(jié)的狀態(tài)位和一個(gè)字節(jié)的相對(duì)卡地址(RCA),用于獲取卡的相對(duì)地址。
-
R7 Response (Card Interface Condition Response): 包含一個(gè)字節(jié)的狀態(tài)位和一個(gè)字節(jié)的回應(yīng)信息,用于卡初始化階段。
-
Data Response (for Data Transfer Commands): 在讀/寫(xiě)數(shù)據(jù)時(shí),卡會(huì)響應(yīng)數(shù)據(jù)響應(yīng),用于指示數(shù)據(jù)是否成功接收。
注意:
- 短響應(yīng)是 48bit長(zhǎng)度,只有 R2 類(lèi)型是長(zhǎng)響應(yīng),其長(zhǎng)度為 136bit。
- 除了 R3 類(lèi)型之外,其他響應(yīng)都使用 CRC7 校驗(yàn)來(lái)校驗(yàn),對(duì)于 R2 類(lèi)型是使用 CID 和 CSD 寄存器內(nèi)部 CRC7。
SD卡的操作模式
- 主 機(jī) 上 電 后, 所 有 卡 處 于 空 閑 狀 態(tài), 包 括 當(dāng) 前 處 于 無(wú) 效 狀 態(tài) 的 卡。
- 主 機(jī) 也 可 以 發(fā) 送GO_IDLE_STATE(CMD0) 讓所有卡軟復(fù)位從而進(jìn)入空閑狀態(tài),但當(dāng)前處于無(wú)效狀態(tài)的卡并不會(huì)復(fù)位。
- 主機(jī)在開(kāi)始與卡通信前,需要先確定雙方在互相支持的電壓范圍內(nèi)。SD 卡有一個(gè)電壓支持范圍,主機(jī)當(dāng)前電壓必須在該范圍可能才能與卡正常通信。SEND_IF_COND(CMD8) 命令就是用于驗(yàn)證卡接口操作條件的 (主要是電壓支持)??〞?huì)根據(jù)命令的參數(shù)來(lái)檢測(cè)操作條件匹配性,如果卡支持主機(jī)電壓就產(chǎn)生響應(yīng),否則不響應(yīng)。而主機(jī)則根據(jù)響應(yīng)內(nèi)容確定卡的電壓匹配性。CMD8 是 SD卡標(biāo)準(zhǔn) V2.0 版本才有的新命令,所以如果主機(jī)有接收到響應(yīng),可以判斷卡為 V2.0 或更高版本SD 卡。
- SD_SEND_OP_COND(ACMD41) 命令可以識(shí)別或拒絕不匹配它的電壓范圍的卡。ACMD41 命令的 VDD 電壓參數(shù)用于設(shè)置主機(jī)支持電壓范圍,卡響應(yīng)會(huì)返回卡支持的電壓范圍。對(duì)于對(duì) CMD8有響應(yīng)的卡,把 ACMD41 命令的 HCS 位設(shè)置為 1,可以測(cè)試卡的容量類(lèi)型,如果卡響應(yīng)的 CCS 位為 1 說(shuō)明為高容量 SD 卡,否則為標(biāo)準(zhǔn)卡??ㄔ陧憫?yīng) ACMD41 之后進(jìn)入準(zhǔn)備狀態(tài),不響應(yīng) ACMD41的卡為不可用卡,進(jìn)入無(wú)效狀態(tài)。ACMD41 是應(yīng)用特定命令,發(fā)送該命令之前必須先發(fā) CMD55。
- ALL_SEND_CID(CMD2) 用來(lái)控制所有卡返回它們的卡識(shí)別號(hào) (CID),處于準(zhǔn)備狀態(tài)的卡在發(fā)送CID 之后就進(jìn)入識(shí)別狀態(tài)。
- 之后主機(jī)就發(fā)送 SEND_RELATIVE_ADDR(CMD3) 命令,讓卡自己推薦一個(gè)相對(duì)地址 (RCA) 并響應(yīng)命令。這個(gè) RCA 是 16bit 地址,而 CID 是 128bit 地址,使用 RCA簡(jiǎn)化通信??ㄔ诮邮盏?CMD3 并發(fā)出響應(yīng)后就進(jìn)入數(shù)據(jù)傳輸模式,并處于待機(jī)狀態(tài),主機(jī)在獲取所有卡 RCA 之后也進(jìn)入數(shù)據(jù)傳輸模式
數(shù)據(jù)傳輸模式
CMD7 用來(lái)選定和取消指定的卡,卡在待機(jī)狀態(tài)下還不能進(jìn)行數(shù)據(jù)通信,因?yàn)榭偩€(xiàn)上可能有多個(gè)卡都是出于待機(jī)狀態(tài),必須選擇一個(gè) RCA 地址目標(biāo)卡使其進(jìn)入傳輸狀態(tài)才可以進(jìn)行數(shù)據(jù)通信。同時(shí)通過(guò) CMD7 命令(選擇卡命令)也可以讓已經(jīng)被選擇的目標(biāo)卡返回到待機(jī)狀態(tài)。數(shù)據(jù)傳輸模式下的數(shù)據(jù)通信都是主機(jī)和目標(biāo)卡之間通過(guò)尋址命令點(diǎn)對(duì)點(diǎn)進(jìn)行的??ㄌ幱趥鬏敔顟B(tài)下可以使用表 SD 部分命令描述 中面向塊的讀寫(xiě)以及擦除命令對(duì)卡進(jìn)行數(shù)據(jù)讀寫(xiě)、擦除。CMD12可以中斷正在進(jìn)行的數(shù)據(jù)通信,讓卡返回到傳輸狀態(tài)。CMD0 和 CMD15 會(huì)中止任何數(shù)據(jù)編程操作,返回卡識(shí)別模式,這可能導(dǎo)致卡數(shù)據(jù)被損壞。
STM32 的 SDIO 功能框圖
時(shí)鐘的配置:
SDIO 使用兩個(gè)時(shí)鐘信號(hào),一個(gè)是 SDIO 適配器時(shí)鐘 (SDIOCLK=HCLK=72MHz),另外一個(gè)是 AHB總線(xiàn)時(shí)鐘的二分頻 (HCLK/2,一般為 36MHz)。適配器寄存器和 FIFO 使用 AHB 總線(xiàn)一側(cè)的時(shí)鐘(HCLK/2),控制單元、命令通道和數(shù)據(jù)通道使用 SDIO 適配器一側(cè)的時(shí)鐘 (SDIOCLK)。
SDIO_CK 是 SDIO 接口與 SD 卡用于同步的時(shí)鐘信號(hào)。它使用 SDIOCLK 作為 SDIO_CK 的時(shí)鐘來(lái)源,可以通過(guò)設(shè)置 BYPASS 模式直接得到,這時(shí) SDIO_CK = SDIOCLK=HCLK。若禁止 BYPASS 模式,可以通過(guò)配置時(shí)鐘寄存器的 CLKDIV 位控制分頻因子,即 SDIO_CK=SDIOCLK/(2+CLKDIV)= HCLK/(2+CLKDIV)。
配置時(shí)鐘時(shí)要注意,SD 卡普遍要求 SDIO_CK 時(shí)鐘頻率不能超過(guò) 25MHz。STM32 控制器的 SDIO 是針對(duì) MMC 卡和 SD 卡的主設(shè)備,所以預(yù)留有 8 根數(shù)據(jù)線(xiàn),對(duì)于 SD 卡最多用四根數(shù)據(jù)線(xiàn)。SDIO 適配器是 SD 卡系統(tǒng)主機(jī)部分,是 STM32 控制器與 SD 卡數(shù)據(jù)通信中間設(shè)備。SDIO 適配器由五個(gè)單元組成,分別是控制單元、命令路徑單元、數(shù)據(jù)路徑單元、寄存器單元以及 FIFO。
內(nèi)部適配器
控制單元
電源管理部件會(huì)在系統(tǒng)斷電和上電階段禁止 SD 卡總線(xiàn)輸出信號(hào)。時(shí)鐘管理部件控制 CLK 線(xiàn)時(shí)鐘信號(hào)生成。一般使用 SDIOCLK 分頻得到。
命令路徑
命令路徑控制命令發(fā)送,并接收卡的響應(yīng)。
CPSM 狀態(tài)機(jī)描述圖
STM32 控制器以命令路徑狀態(tài)機(jī) (CPSM) 來(lái)描述 SDIO適配器的狀態(tài)變化,并加入了等待超時(shí)檢測(cè)功能,以便退出永久等待的情況。
由stm32自己控制無(wú)需十分了解
數(shù)據(jù)路徑
數(shù)據(jù)路徑部件負(fù)責(zé)與 SD 卡相互數(shù)據(jù)傳輸
SD 卡系統(tǒng)數(shù)據(jù)傳輸狀態(tài)轉(zhuǎn)換參考圖數(shù)據(jù)傳輸模式卡狀態(tài)轉(zhuǎn)換 ,SDIO 適配器以數(shù)據(jù)路徑狀態(tài)機(jī)(DPSM) 來(lái)描述 SDIO 適配器狀態(tài)變化情況。并加入了等待超時(shí)檢測(cè)功能,以便退出永久等待情況。發(fā)送數(shù)據(jù)時(shí),DPSM 處于等待發(fā)送 (Wait_S) 狀態(tài),如果數(shù)據(jù) FIFO 不為空,DPSM 變成發(fā)送狀態(tài)并且數(shù)據(jù)路徑部件啟動(dòng)向卡發(fā)送數(shù)據(jù)。接收數(shù)據(jù)時(shí),DPSM 處于等待接收狀態(tài),當(dāng) DPSM 收到起始位時(shí)變成接收狀態(tài),并且數(shù)據(jù)路徑部件開(kāi)始從卡接收數(shù)據(jù)。
DPSM 狀態(tài)機(jī)
數(shù)據(jù) FIFO
數(shù)據(jù) FIFO(先進(jìn)先出) 部件是一個(gè)數(shù)據(jù)緩沖器,帶發(fā)送和接收單元??刂破鞯?FIFO 包含寬度為32bit、深度為 32 字的數(shù)據(jù)緩沖器和發(fā)送/接收邏輯。其中 SDIO 狀態(tài)寄存器 (SDIO_STA) 的 TXACT位用于指示當(dāng)前正在發(fā)送數(shù)據(jù),RXACT 位指示當(dāng)前正在接收數(shù)據(jù),這兩個(gè)位不可能同時(shí)為 1。? 當(dāng) TXACT 為 1 時(shí),可以通過(guò) AHB 接口將數(shù)據(jù)寫(xiě)入到傳輸 FIFO。? 當(dāng) RXACT 為 1 時(shí),接收 FIFO 存放從數(shù)據(jù)路徑部件接收到的數(shù)據(jù)。根據(jù) FIFO 空或滿(mǎn)狀態(tài)會(huì)把 SDIO_STA 寄存器位值 1,并可以產(chǎn)生中斷和 DMA 請(qǐng)求。
SDIO結(jié)構(gòu)體
SDIO初始化結(jié)構(gòu)體
typedef struct {
uint32_t SDIO_ClockEdge; // 時(shí)鐘沿
uint32_t SDIO_ClockBypass; // 旁路時(shí)鐘
uint32_t SDIO_ClockPowerSave; // 節(jié)能模式
uint32_t SDIO_BusWide; // 數(shù)據(jù)寬度
uint32_t SDIO_HardwareFlowControl; // 硬件流控制
uint8_t SDIO_ClockDiv; // 時(shí)鐘分頻
} SDIO_InitTypeDef;
SDIO 命令初始化結(jié)構(gòu)體
typedef struct {
uint32_t SDIO_Argument; // 命令參數(shù)
uint32_t SDIO_CmdIndex; // 命令號(hào)
uint32_t SDIO_Response; // 響應(yīng)類(lèi)型
uint32_t SDIO_Wait; // 等待使能
uint32_t SDIO_CPSM; // 命令路徑狀態(tài)機(jī)
} SDIO_CmdInitTypeDef;
SDIO 數(shù)據(jù)初始化結(jié)構(gòu)體
typedef struct {
uint32_t SDIO_DataTimeOut; // 數(shù)據(jù)傳輸超時(shí)
uint32_t SDIO_DataLength; // 數(shù)據(jù)長(zhǎng)度
uint32_t SDIO_DataBlockSize; // 數(shù)據(jù)塊大小
uint32_t SDIO_TransferDir; // 數(shù)據(jù)傳輸方向
uint32_t SDIO_TransferMode; // 數(shù)據(jù)傳輸模式
uint32_t SDIO_DPSM; // 數(shù)據(jù)路徑狀態(tài)機(jī)
} SDIO_DataInitTypeDef;
SD卡的讀寫(xiě)實(shí)驗(yàn)
準(zhǔn)備工作
#include "sdio/sdio_test.h"
#include "./led/bsp_led.h"
#include "./sdio/bsp_sdio_sdcard.h"
#include "./usart/bsp_usart.h"
/* Private typedef -----------------------------------------------------------*/
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
/* Private define ------------------------------------------------------------*/
#define BLOCK_SIZE 512 /* Block Size in Bytes */
#define NUMBER_OF_BLOCKS 10 /* For Multi Blocks operation (Read/Write) */
#define MULTI_BUFFER_SIZE (BLOCK_SIZE * NUMBER_OF_BLOCKS)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint8_t Buffer_Block_Tx[BLOCK_SIZE], Buffer_Block_Rx[BLOCK_SIZE];
uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE], Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE];
volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED;
SD_Error Status = SD_OK;
/* Private function prototypes -----------------------------------------------*/
static void SD_EraseTest(void);
static void SD_SingleBlockTest(void);
void SD_MultiBlockTest(void);
static void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset);
static TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength);
static TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength);
/* Private functions---------------------------------------------------------*/
void SD_EraseTest(void)
void SD_EraseTest(void)
{
/*------------------- Block Erase ------------------------------------------*/
if (Status == SD_OK)
{
/* Erase NumberOfBlocks Blocks of WRITE_BL_LEN(512 Bytes) */
Status = SD_Erase(0x00, (BLOCK_SIZE * NUMBER_OF_BLOCKS));
}
if (Status == SD_OK)
{
Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
/* Check if the Transfer is finished */
Status = SD_WaitReadOperation();
/* Wait until end of DMA transfer */
while(SD_GetStatus() != SD_TRANSFER_OK);
}
/* Check the correctness of erased blocks */
if (Status == SD_OK)
{
EraseStatus = eBuffercmp(Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
}
if(EraseStatus == PASSED)
{
LED_GREEN;
printf("SD卡擦除測(cè)試成功!\n");
}
else
{
LED_BLUE;
printf("SD卡擦除測(cè)試失??!\n");
printf("溫馨提示:部分SD卡不支持擦除測(cè)試,若SD卡能通過(guò)下面的single讀寫(xiě)測(cè)試,即表示SD卡能夠正常使用。\n");
}
}
-
擦除操作:
Status = SD_Erase(0x00, (BLOCK_SIZE * NUMBER_OF_BLOCKS));
這里調(diào)用了
SD_Erase
函數(shù),擦除了從地址0x00開(kāi)始,總共BLOCK_SIZE * NUMBER_OF_BLOCKS
大小的數(shù)據(jù)塊。Status
用于存儲(chǔ)函數(shù)執(zhí)行的狀態(tài)。 -
讀取操作:
Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
如果擦除操作成功,接下來(lái)進(jìn)行讀取操作。這里調(diào)用了
SD_ReadMultiBlocks
函數(shù),從地址0x00開(kāi)始讀取BLOCK_SIZE
大小的數(shù)據(jù)塊,總共讀取NUMBER_OF_BLOCKS
個(gè)塊。 -
等待讀取操作完成:
Status = SD_WaitReadOperation();
等待讀取操作完成,確保數(shù)據(jù)已被成功讀取。
-
檢查擦除的塊的正確性:
EraseStatus = eBuffercmp(Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
通過(guò)比較讀取的數(shù)據(jù)塊與期望的擦除狀態(tài),判斷擦除是否成功。
-
根據(jù)檢查結(jié)果輸出信息:
if (EraseStatus == PASSED) { LED_GREEN; printf("SD卡擦除測(cè)試成功!\n"); } else { LED_BLUE; printf("SD卡擦除測(cè)試失敗!\n"); printf("溫馨提示:部分SD卡不支持擦除測(cè)試,若SD卡能通過(guò)下面的single讀寫(xiě)測(cè)試,即表示SD卡能夠正常使用。\n"); }
根據(jù)擦除測(cè)試的結(jié)果,點(diǎn)亮相應(yīng)的LED并輸出信息。
void SD_MultiBlockTest(void)
{
/*--------------- Multiple Block Read/Write ---------------------*/
/* Fill the buffer to send */
Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);
if (Status == SD_OK)
{
/* Write multiple block of many bytes on address 0 */
Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
/* Check if the Transfer is finished */
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if (Status == SD_OK)
{
/* Read block of many bytes from address 0 */
Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
/* Check if the Transfer is finished */
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
/* Check the correctness of written data */
if (Status == SD_OK)
{
TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
}
if(TransferStatus2 == PASSED)
{
LED_GREEN;
printf("Multi block 測(cè)試成功!");
}
else
{
LED_RED;
printf("Multi block 測(cè)試失敗,請(qǐng)確保SD卡正確接入開(kāi)發(fā)板,或換一張SD卡測(cè)試!");
}
}
這是一個(gè)多塊數(shù)據(jù)讀寫(xiě)測(cè)試的C語(yǔ)言函數(shù)。讓我解釋一下主要的步驟:
-
填充發(fā)送緩沖區(qū):
Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);
這里調(diào)用了
Fill_Buffer
函數(shù),用0x0填充了Buffer_MultiBlock_Tx
緩沖區(qū),該緩沖區(qū)用于發(fā)送數(shù)據(jù)。 -
多塊數(shù)據(jù)寫(xiě)操作:
Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
如果填充緩沖區(qū)成功,接下來(lái)進(jìn)行多塊數(shù)據(jù)寫(xiě)入操作。調(diào)用了
SD_WriteMultiBlocks
函數(shù),將填充的數(shù)據(jù)寫(xiě)入從地址0x00開(kāi)始的多個(gè)數(shù)據(jù)塊。 -
等待寫(xiě)操作完成:
Status = SD_WaitWriteOperation(); while(SD_GetStatus() != SD_TRANSFER_OK);
等待寫(xiě)入操作完成,確保數(shù)據(jù)已成功寫(xiě)入SD卡。
-
多塊數(shù)據(jù)讀操作:
Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
如果寫(xiě)入操作成功,接下來(lái)進(jìn)行多塊數(shù)據(jù)讀取操作。調(diào)用了
SD_ReadMultiBlocks
函數(shù),從地址0x00開(kāi)始讀取多個(gè)數(shù)據(jù)塊。 -
等待讀操作完成:
Status = SD_WaitReadOperation(); while(SD_GetStatus() != SD_TRANSFER_OK);
等待讀取操作完成,確保數(shù)據(jù)已成功讀取。
-
檢查寫(xiě)入的數(shù)據(jù)的正確性:
TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
通過(guò)比較寫(xiě)入的數(shù)據(jù)和讀取的數(shù)據(jù),判斷寫(xiě)入是否成功。
-
根據(jù)檢查結(jié)果輸出信息:
if(TransferStatus2 == PASSED) { LED_GREEN; printf("Multi block 測(cè)試成功!"); } else { LED_RED; printf("Multi block 測(cè)試失敗,請(qǐng)確保SD卡正確接入開(kāi)發(fā)板,或換一張SD卡測(cè)試!"); }
根據(jù)多塊數(shù)據(jù)讀寫(xiě)測(cè)試的結(jié)果,點(diǎn)亮相應(yīng)的LED并輸出信息。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-826163.html
這段代碼主要用于測(cè)試SD卡的多塊數(shù)據(jù)讀寫(xiě)功能,并通過(guò)比較寫(xiě)入和讀取的數(shù)據(jù)來(lái)驗(yàn)證操作的正確性。如果測(cè)試成功,LED變綠并輸出成功信息,否則LED變紅并輸出失敗信息。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-826163.html
比較函數(shù)和填充函數(shù)補(bǔ)充
/**
* @brief Compares two buffers.
* @param pBuffer1, pBuffer2: buffers to be compared.
* @param BufferLength: buffer's length
* @retval PASSED: pBuffer1 identical to pBuffer2
* FAILED: pBuffer1 differs from pBuffer2
*/
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength)
{
while (BufferLength--)
{
if (*pBuffer1 != *pBuffer2)
{
return FAILED;
}
pBuffer1++;
pBuffer2++;
}
return PASSED;
}
/**
* @brief Fills buffer with user predefined data.
* @param pBuffer: pointer on the Buffer to fill
* @param BufferLength: size of the buffer to fill
* @param Offset: first value to fill on the Buffer
* @retval None
*/
void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
{
uint16_t index = 0;
/* Put in global buffer same values */
for (index = 0; index < BufferLength; index++)
{
pBuffer[index] = index + Offset;
}
}
/**
* @brief Checks if a buffer has all its values are equal to zero.
* @param pBuffer: buffer to be compared.
* @param BufferLength: buffer's length
* @retval PASSED: pBuffer values are zero
* FAILED: At least one value from pBuffer buffer is different from zero.
*/
TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength)
{
while (BufferLength--)
{
/* In some SD Cards the erased state is 0xFF, in others it's 0x00 */
if ((*pBuffer != 0xFF) && (*pBuffer != 0x00))
{
return FAILED;
}
pBuffer++;
}
return PASSED;
}
/*********************************************END OF FILE**********************/
測(cè)試程序
void SD_Test(void)
{
LED_BLUE;
/*------------------------------ SD Init ---------------------------------- */
/* SD卡使用SDIO中斷及DMA中斷接收數(shù)據(jù),中斷服務(wù)程序位于bsp_sdio_sd.c文件尾*/
if((Status = SD_Init()) != SD_OK)
{
LED_RED;
printf("SD卡初始化失敗,請(qǐng)確保SD卡已正確接入開(kāi)發(fā)板,或換一張SD卡測(cè)試!\n");
}
else
{
printf("SD卡初始化成功!\n");
}
if(Status == SD_OK)
{
LED_BLUE;
/*擦除測(cè)試*/
SD_EraseTest();
LED_BLUE;
/*single block 讀寫(xiě)測(cè)試*/
SD_SingleBlockTest();
//暫不支持直接多塊讀寫(xiě),多塊讀寫(xiě)可用多個(gè)單塊讀寫(xiě)流程代替
LED_BLUE;
/*muti block 讀寫(xiě)測(cè)試*/
SD_MultiBlockTest();
}
}
main.c
int main(void)
{
/* 初始化LED燈 */
LED_GPIO_Config();
LED_BLUE;
/* 初始化獨(dú)立按鍵 */
Key_GPIO_Config();
/*初始化USART1*/
USART_Config();
printf("\r\n歡迎使用野火 STM32 開(kāi)發(fā)板。\r\n");
printf("在開(kāi)始進(jìn)行SD卡基本測(cè)試前,請(qǐng)給開(kāi)發(fā)板插入32G以?xún)?nèi)的SD卡\r\n");
printf("本程序會(huì)對(duì)SD卡進(jìn)行 非文件系統(tǒng) 方式讀寫(xiě),會(huì)刪除SD卡的文件系統(tǒng)\r\n");
printf("實(shí)驗(yàn)后可通過(guò)電腦格式化或使用SD卡文件系統(tǒng)的例程恢復(fù)SD卡文件系統(tǒng)\r\n");
printf("\r\n 但sd卡內(nèi)的原文件不可恢復(fù),實(shí)驗(yàn)前務(wù)必備份SD卡內(nèi)的原文件?。?!\r\n");
printf("\r\n 若已確認(rèn),請(qǐng)按開(kāi)發(fā)板的KEY1按鍵,開(kāi)始SD卡測(cè)試實(shí)驗(yàn)....\r\n");
/* Infinite loop */
while (1)
{
/*按下按鍵開(kāi)始進(jìn)行SD卡讀寫(xiě)實(shí)驗(yàn),會(huì)損壞SD卡原文件*/
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON)
{
printf("\r\n開(kāi)始進(jìn)行SD卡讀寫(xiě)實(shí)驗(yàn)\r\n");
SD_Test();
}
}
到了這里,關(guān)于stm32中的SDIO的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!