12.1 實(shí)驗(yàn)內(nèi)容
通過(guò)本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容:
? SDIO操作原理
? SD卡讀寫(xiě)實(shí)現(xiàn)
12.2 實(shí)驗(yàn)原理
SD卡是一種主要以Nand Flash作為存儲(chǔ)介質(zhì),具有體積小、數(shù)據(jù)傳輸速度快以及支持熱插拔的優(yōu)點(diǎn)。如今,已被廣泛應(yīng)用于數(shù)碼相機(jī)、便攜式移動(dòng)設(shè)備以及手機(jī)等多種設(shè)備中。SD卡的驅(qū)動(dòng)一般有SPI接口或SDIO接口,本例程介紹使用GD32F4xx的SDIO接口驅(qū)動(dòng)SD卡的實(shí)現(xiàn)。
12.2.1 SD卡基礎(chǔ)知識(shí)
SD卡:secure digital memory card是一種安全存儲(chǔ)器件。屬性是快閃存儲(chǔ)器(flash eeprom),功能用來(lái)存儲(chǔ)數(shù)據(jù)。
SD卡雖然是薄薄的一片,但是它并不是一個(gè)整體,而是由大量的集成電路組成。SD卡的內(nèi)部結(jié)構(gòu)如下圖所示,主要由信號(hào)端子,接口控制器和存儲(chǔ)區(qū)組成。
SD卡主要有兩種模式,SD模式和SPI模式。不同模式下,接口定義不同。下面是SD卡的引腳。
兩種模式的接口定義如下
SD模式中,主要由VCC(電源),VSS(GND),CLK(時(shí)鐘,由主控提供),CMD(命令),DAT0-3(數(shù)據(jù)輸入輸出),由6線(xiàn)制組成進(jìn)行通信。SPI模式,主要采用4線(xiàn)制通信,除了電源地外,由MISO,MOSI,CLK,CS組成。下面簡(jiǎn)單介紹SD模式的操作。
要驅(qū)動(dòng)SD卡工作,主要涉及兩個(gè)步驟。第一個(gè)步驟是SD卡的識(shí)別過(guò)程。第二個(gè)步驟是對(duì)SD卡進(jìn)行讀寫(xiě)過(guò)程,即主機(jī)控制器和SD卡之間進(jìn)行數(shù)據(jù)傳輸?shù)倪^(guò)程。
要使SD卡能正常工作,一是要給SD卡供給穩(wěn)定的電壓,二是要SD卡按用戶(hù)規(guī)定的方式工作。這兩項(xiàng)工作的實(shí)現(xiàn),都是主機(jī)控制器通過(guò)給SD卡發(fā)送控制命令來(lái)實(shí)現(xiàn)的。
主機(jī)(SDIO控制器)要驅(qū)動(dòng)SD卡工作,要使用許多的命令,包括應(yīng)用層命令A(yù)CMD 和 通用命令 CMD. 主機(jī)(SDIO控制器)把命令發(fā)送給SD卡,SD卡會(huì)作出回應(yīng),這里的回應(yīng)叫做響應(yīng),響應(yīng)命令分為6類(lèi),分別是R1、R1b、R2、R3、R6、R7。主機(jī)(SDIO控制器)給SD卡發(fā)送命令之后,SD卡會(huì)作出響應(yīng),響應(yīng)中包含主機(jī)(SDIO控制器)需要的數(shù)據(jù),這些數(shù)據(jù)有SD的信息,容量,和存儲(chǔ)數(shù)據(jù)等等。上面已經(jīng)提到了,SD卡工作,主要是識(shí)別和數(shù)據(jù)傳輸,它的識(shí)別過(guò)程有些復(fù)雜,寫(xiě)代碼的時(shí)候,可以參考協(xié)議給的初始化流程圖。數(shù)據(jù)傳輸包括讀和寫(xiě),單字節(jié)和多字節(jié)讀寫(xiě)。下兩節(jié)描述識(shí)別初始化流程圖和數(shù)據(jù)讀寫(xiě)時(shí)序圖。
1、讀寫(xiě)數(shù)據(jù)的時(shí)序圖
SDIO與SD卡通信一般以數(shù)據(jù)塊的形式進(jìn)行傳輸,SDIO(多)數(shù)據(jù)塊讀操作,如下圖所示。
SDIO(多)數(shù)據(jù)塊寫(xiě)操作,如下圖所示。
2、命令格式
SDIO所有的命令和響應(yīng)都是在SDIO_CMD引腳上面?zhèn)鬏數(shù)?,命令長(zhǎng)度固定為48位,SDIO命令格式如下表所示。
3、寄存器
SDIO控制器的寄存器,主要設(shè)置SDIO控制器和命令的索引與參數(shù)。SD卡有5個(gè)寄存器CID,RCA,CSD,SCR.OCR。SD卡的信息從SD卡寄存器中獲取。
SD卡正常工作,就是根據(jù)SD卡初始化流程圖,發(fā)送命令,收到回復(fù),直到流程結(jié)束。傳輸數(shù)據(jù),也是根據(jù)讀寫(xiě)時(shí)序圖,將要發(fā)送的數(shù)據(jù)放進(jìn)命令中發(fā)送出去。
12.2.2 SDIO模塊原理
SDIO為安全的數(shù)字輸入輸出接口,可以用于驅(qū)動(dòng)SD卡、EMMC等,主要特征如下:
? e*MMC: 與多媒體卡系統(tǒng)規(guī)格書(shū) V4.2 及之前的版本全兼容。有三種不同的數(shù)據(jù)總線(xiàn)模式:1 位(默認(rèn))、 4 位和 8 位;
? SD 卡: 與SD 存儲(chǔ)卡規(guī)格版本3.0 全兼容;
? SD I/O: 與 SD I/O 卡規(guī)格版本 3.0 全兼容,有兩種不同的數(shù)據(jù)總線(xiàn)模式: 1 位(默認(rèn))和4位(包括SDR和DDR);
? 104MHz 數(shù)據(jù)傳輸頻率和8 位數(shù)據(jù)傳輸模式;
? 中斷和 DMA 請(qǐng)求;
? 數(shù)據(jù)傳輸支持 DDR 模式。
SDIO模塊結(jié)構(gòu)框圖如下所示。主要包括以下三個(gè)部分:SDIO 適配器:由控制單元、命令單元和數(shù)據(jù)單元組成,控制單元管理時(shí)鐘信號(hào),命令單元管理命令的傳輸,數(shù)據(jù)單元管理數(shù)據(jù)的傳輸; AHB 接口:包括通過(guò) AHB 總線(xiàn)訪(fǎng)問(wèn)的寄存器、用于數(shù)據(jù)傳輸?shù)?FIFO 單元以及產(chǎn)生中斷和 DMA 請(qǐng)求信號(hào); 內(nèi)部 DMA(IDMA)以及 AHB 主機(jī)接口 ?。
SDIO模塊可以實(shí)現(xiàn)對(duì)SD卡的完全驅(qū)動(dòng)以及協(xié)議的實(shí)現(xiàn),包括命令、響應(yīng)等相關(guān)操作,本例程實(shí)現(xiàn)使用SDIO驅(qū)動(dòng)SD卡初始化以及讀寫(xiě)測(cè)試等相關(guān)操作,具體實(shí)現(xiàn)可以參考GD32H7用戶(hù)手冊(cè)以及代碼解析等。
12.3 硬件設(shè)計(jì)
SD卡相關(guān)硬件電路如下圖所示,實(shí)驗(yàn)板上具有SD卡卡座,信號(hào)線(xiàn)上有四根數(shù)據(jù)線(xiàn),一根CMD命令線(xiàn)以及一根CLK時(shí)鐘線(xiàn),所有信號(hào)線(xiàn)通過(guò)10K電阻進(jìn)行上拉,電源地信號(hào)線(xiàn)具有10uf以及100nf電容,SD卡插入時(shí),金屬接觸點(diǎn)朝下插入。
12.4 代碼解析
12.4.1 SDIO初始化配置函數(shù)
SDIO初始化配置在sd_io_init()函數(shù)中,其中包括sd_init()初始化、sd_card_information_get()SD卡信息獲取、sd_card_select_deselect()SD卡選擇、sd_cardstatus_get()SD卡狀態(tài)獲取、sd_bus_mode_config()SD卡總線(xiàn)寬度配置以及sd_transfer_mode_config()SD卡通信模式配置,歷程中選擇了4線(xiàn)查詢(xún)模式。
C
sd_error_enum sd_io_init(void)
{
sd_error_enum status = SD_OK;
uint32_t cardstate = 0;
status = sd_init();
if(SD_OK == status) {
status = sd_card_information_get(&sd_cardinfo);
}
if(SD_OK == status) {
status = sd_card_select_deselect(sd_cardinfo.card_rca);
}
status = sd_cardstatus_get(&cardstate);
if(cardstate & 0x02000000) {
printf_log("\r\n the card is locked!");
status = sd_lock_unlock(SD_UNLOCK);
if(status != SD_OK) {
return SD_LOCK_UNLOCK_FAILED;
} else {
printf_log("\r\n the card is unlocked successfully!");
}
}
if((SD_OK == status) && (!(cardstate & 0x02000000))) {
/* set bus mode */
status = sd_bus_mode_config(SDIO_BUSMODE_4BIT, SDIO_SPEEDMODE);
status = sd_bus_mode_config(SDIO_BUSMODE_1BIT, SDIO_SPEEDMODE);
}
if(SD_OK == status) {
/* UHS-I Hosts can perform sampling point tuning using tuning command ??*/
status = sd_tuning();
}
if(SD_OK == status) {
/* set data transfer mode */
/* if use 1.8V high speed mode, please select the DMA mode */
status = sd_transfer_mode_config(SDIO_DTMODE);
}
return status;
}
12.4.2 獲取SD卡信息函數(shù)
獲取SD卡信息的函數(shù)如下所示,card_info_get()。
C
void card_info_get(void)
{
uint8_t sd_spec, sd_spec3, sd_spec4, sd_security;
uint32_t block_count, block_size;
uint16_t temp_ccc;
printf_log("\r\n Card information:");
sd_spec = (sd_scr[1] & 0x0F000000) >> 24;
sd_spec3 = (sd_scr[1] & 0x00008000) >> 15;
sd_spec4 = (sd_scr[1] & 0x00000400) >> 10;
if(2 == sd_spec) {
if(1 == sd_spec3) {
if(1 == sd_spec4) {
printf_log("\r\n## Card version 4.xx ##");
} else {
printf_log("\r\n## Card version 3.0x ##");
}
} else {
printf_log("\r\n## Card version 2.00 ##");
}
} else if(1 == sd_spec) {
printf_log("\r\n## Card version 1.10 ##");
} else if(0 == sd_spec) {
printf_log("\r\n## Card version 1.0x ##");
}
sd_security = (sd_scr[1] & 0x00700000) >> 20;
if(2 == sd_security) {
printf_log("\r\n## security v1.01 ##");
} else if(3 == sd_security) {
printf_log("\r\n## security v2.00 ##");
} else if(4 == sd_security) {
printf_log("\r\n## security v3.00 ##");
}
block_count = (sd_cardinfo.card_csd.c_size + 1) * 1024;
block_size = 512;
printf_log("\r\n## Device size is %dKB ##", sd_card_capacity_get());
printf_log("\r\n## Block size is %dB ##", block_size);
printf_log("\r\n## Block count is %d ##", block_count);
if(sd_cardinfo.card_csd.read_bl_partial) {
printf_log("\r\n## Partial blocks for read allowed ##");
}
if(sd_cardinfo.card_csd.write_bl_partial) {
printf_log("\r\n## Partial blocks for write allowed ##");
}
temp_ccc = sd_cardinfo.card_csd.ccc;
printf_log("\r\n## CardCommandClasses is: %x ##", temp_ccc);
if((SD_CCC_BLOCK_READ & temp_ccc) && (SD_CCC_BLOCK_WRITE & temp_ccc)) {
printf_log("\r\n## Block operation supported ##");
}
if(SD_CCC_ERASE & temp_ccc) {
printf_log("\r\n## Erase supported ##");
}
if(SD_CCC_WRITE_PROTECTION & temp_ccc) {
printf_log("\r\n## Write protection supported ##");
}
if(SD_CCC_LOCK_CARD & temp_ccc) {
printf_log("\r\n## Lock unlock supported ##");
}
if(SD_CCC_APPLICATION_SPECIFIC & temp_ccc) {
printf_log("\r\n## Application specific supported ##");
}
if(SD_CCC_IO_MODE & temp_ccc) {
printf_log("\r\n## I/O mode supported ##");
}
if(SD_CCC_SWITCH & temp_ccc) {
printf_log("\r\n## Switch function supported ##");
}
}
12.4.3 SD卡數(shù)據(jù)塊寫(xiě)入函數(shù)
SD卡數(shù)據(jù)塊寫(xiě)入函數(shù)如下所示,通過(guò)該函數(shù)可實(shí)現(xiàn)SD卡數(shù)據(jù)塊的數(shù)據(jù)寫(xiě)入。
C
sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_t blocksize)
{
/* initialize the variables */
sd_error_enum status = SD_OK;
uint8_t cardstate = 0U;
uint32_t count = 0U, align = 0U, datablksize = SDIO_DATABLOCKSIZE_1BYTE, *ptempbuff = pwritebuffer;
uint32_t transbytes = 0U, restwords = 0U, response = 0U;
__IO uint32_t timeout = 0U;
if(NULL == pwritebuffer) {
status = SD_PARAMETER_INVALID;
return status;
}
transerror = SD_OK;
transend = 0U;
totalnumber_bytes = 0U;
/* clear all DSM configuration */
sdio_data_config(SDIO, 0U, 0U, SDIO_DATABLOCKSIZE_1BYTE);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOCARD);
sdio_dsm_disable(SDIO);
sdio_idma_disable(SDIO);
/* check whether the card is locked */
if(sdio_response_get(SDIO, SDIO_RESPONSE0) & SD_CARDSTATE_LOCKED) {
status = SD_LOCK_UNLOCK_FAILED;
return status;
}
/* blocksize is fixed in 512B for SDHC card */
if(SDIO_HIGH_CAPACITY_SD_CARD != cardtype) {
writeaddr *= 512U;
} else {
blocksize = 512U;
}
align = blocksize & (blocksize - 1U);
if((blocksize > 0U) && (blocksize <= 2048U) && (0U == align)) {
datablksize = sd_datablocksize_get(blocksize);
/* send CMD16(SET_BLOCKLEN) to set the block length */
sdio_command_response_config(SDIO, SD_CMD_SET_BLOCKLEN, (uint32_t)blocksize, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SET_BLOCKLEN);
if(SD_OK != status) {
return status;
}
} else {
status = SD_PARAMETER_INVALID;
return status;
}
/* send CMD13(SEND_STATUS), addressed card sends its status registers */
sdio_command_response_config(SDIO, SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SEND_STATUS);
if(SD_OK != status) {
return status;
}
response = sdio_response_get(SDIO, SDIO_RESPONSE0);
timeout = 100000U;
while((0U == (response & SD_R1_READY_FOR_DATA)) && (timeout > 0U)) {
/* continue to send CMD13 to polling the state of card until buffer empty or timeout */
--timeout;
/* send CMD13(SEND_STATUS), addressed card sends its status registers */
sdio_command_response_config(SDIO, SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SEND_STATUS);
if(SD_OK != status) {
return status;
}
response = sdio_response_get(SDIO, SDIO_RESPONSE0);
}
if(0U == timeout) {
return SD_ERROR;
}
stopcondition = 0U;
totalnumber_bytes = blocksize;
/* configure the SDIO data transmisson */
sdio_data_config(SDIO, SD_DATATIMEOUT, totalnumber_bytes, datablksize);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOCARD);
sdio_trans_start_enable(SDIO);
if(SD_POLLING_MODE == transmode) {
/* send CMD24(WRITE_BLOCK) to write a block */
sdio_command_response_config(SDIO, SD_CMD_WRITE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_WRITE_BLOCK);
if(SD_OK != status) {
return status;
}
/* polling mode */
while(!sdio_flag_get(SDIO, SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_DTEND)) {
if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_TFH)) {
/* at least 8 words can be written into the FIFO */
if((totalnumber_bytes - transbytes) < SD_FIFOHALF_BYTES) {
restwords = (totalnumber_bytes - transbytes) / 4U + (((totalnumber_bytes - transbytes) % 4U == 0U) ? 0U : 1U);
for(count = 0U; count < restwords; count++) {
sdio_data_write(SDIO, *ptempbuff);
++ptempbuff;
transbytes += 4U;
}
} else {
for(count = 0U; count < SD_FIFOHALF_WORDS; count++) {
sdio_data_write(SDIO, *(ptempbuff + count));
}
/* 8 words(32 bytes) has been transferred */
ptempbuff += SD_FIFOHALF_WORDS;
transbytes += SD_FIFOHALF_BYTES;
}
}
}
sdio_trans_start_disable(SDIO);
/* whether some error occurs and return it */
if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTCRCERR)) {
status = SD_DATA_CRC_ERROR;
sdio_flag_clear(SDIO, SDIO_FLAG_DTCRCERR);
return status;
} else if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTTMOUT)) {
status = SD_DATA_TIMEOUT;
sdio_flag_clear(SDIO, SDIO_FLAG_DTTMOUT);
return status;
} else if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_TXURE)) {
status = SD_TX_UNDERRUN_ERROR;
sdio_flag_clear(SDIO, SDIO_FLAG_TXURE);
return status;
} else {
/* if else end */
}
} else if(SD_DMA_MODE == transmode) {
/* DMA mode */
/* enable the SDIO corresponding interrupts and DMA */
sdio_interrupt_enable(SDIO, SDIO_INT_DTCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_TXURE | SDIO_INT_DTEND);
dma_config(pwritebuffer, (uint32_t)(blocksize >> 5));
sdio_idma_enable(SDIO);
/* send CMD24(WRITE_BLOCK) to write a block */
sdio_command_response_config(SDIO, SD_CMD_WRITE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_WRITE_BLOCK);
if(SD_OK != status) {
return status;
}
while((0U == transend) && (SD_OK == transerror)) {
}
if(SD_OK != transerror) {
return transerror;
}
} else {
status = SD_PARAMETER_INVALID;
return status;
}
/* clear the DATA_FLAGS flags */
sdio_flag_clear(SDIO, SDIO_MASK_DATA_FLAGS);
/* get the card state and wait the card is out of programming and receiving state */
status = sd_card_state_get(&cardstate);
while((SD_OK == status) && ((SD_CARDSTATE_PROGRAMMING == cardstate) || (SD_CARDSTATE_RECEIVING == cardstate))) {
status = sd_card_state_get(&cardstate);
}
return status;
}
12.4.4 SD卡數(shù)據(jù)塊讀取函數(shù)
SD卡數(shù)據(jù)塊讀取函數(shù)如下所示。
C
sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t blocksize)
{
/* initialize the variables */
sd_error_enum status = SD_OK;
uint32_t count = 0U, align = 0U, datablksize = SDIO_DATABLOCKSIZE_1BYTE, *ptempbuff = preadbuffer;
__IO uint32_t timeout = 0U;
if(NULL == preadbuffer) {
status = SD_PARAMETER_INVALID;
return status;
}
transerror = SD_OK;
transend = 0U;
totalnumber_bytes = 0U;
/* clear all DSM configuration */
sdio_data_config(SDIO, 0U, 0U, SDIO_DATABLOCKSIZE_1BYTE);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOCARD);
sdio_dsm_disable(SDIO);
sdio_idma_disable(SDIO);
/* check whether the card is locked */
if(sdio_response_get(SDIO, SDIO_RESPONSE0) & SD_CARDSTATE_LOCKED) {
status = SD_LOCK_UNLOCK_FAILED;
return status;
}
/* blocksize is fixed in 512B for SDHC card */
if(SDIO_HIGH_CAPACITY_SD_CARD != cardtype) {
readaddr *= 512U;
} else {
blocksize = 512U;
}
align = blocksize & (blocksize - 1U);
if((blocksize > 0U) && (blocksize <= 2048U) && (0U == align)) {
datablksize = sd_datablocksize_get(blocksize);
/* send CMD16(SET_BLOCKLEN) to set the block length */
sdio_command_response_config(SDIO, SD_CMD_SET_BLOCKLEN, (uint32_t)blocksize, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SET_BLOCKLEN);
if(SD_OK != status) {
return status;
}
} else {
status = SD_PARAMETER_INVALID;
return status;
}
stopcondition = 0U;
totalnumber_bytes = (uint32_t)blocksize;
if(SD_POLLING_MODE == transmode) {
/* configure SDIO data transmisson */
sdio_data_config(SDIO, SD_DATATIMEOUT, totalnumber_bytes, datablksize);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOSDIO);
sdio_trans_start_enable(SDIO);
/* send CMD17(READ_SINGLE_BLOCK) to read a block */
sdio_command_response_config(SDIO, SD_CMD_READ_SINGLE_BLOCK, (uint32_t)readaddr, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_READ_SINGLE_BLOCK);
if(SD_OK != status) {
return status;
}
/* polling mode */
while(!sdio_flag_get(SDIO, SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_DTEND)) {
if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_RFH)) {
/* at least 8 words can be read in the FIFO */
for(count = 0U; count < SD_FIFOHALF_WORDS; count++) {
*(ptempbuff + count) = sdio_data_read(SDIO);
}
ptempbuff += SD_FIFOHALF_WORDS;
}
}
sdio_trans_start_disable(SDIO);
/* whether some error occurs and return it */
if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTCRCERR)) {
status = SD_DATA_CRC_ERROR;
sdio_flag_clear(SDIO, SDIO_FLAG_DTCRCERR);
return status;
} else if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTTMOUT)) {
status = SD_DATA_TIMEOUT;
sdio_flag_clear(SDIO, SDIO_FLAG_DTTMOUT);
return status;
} else if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_RXORE)) {
status = SD_RX_OVERRUN_ERROR;
sdio_flag_clear(SDIO, SDIO_FLAG_RXORE);
return status;
} else {
/* if else end */
}
while((SET != sdio_flag_get(SDIO, SDIO_FLAG_RFE)) && (SET == sdio_flag_get(SDIO, SDIO_FLAG_DATSTA))) {
*ptempbuff = sdio_data_read(SDIO);
++ptempbuff;
}
/* clear the DATA_FLAGS flags */
sdio_flag_clear(SDIO, SDIO_MASK_DATA_FLAGS);
} else if(SD_DMA_MODE == transmode) {
/* DMA mode */
/* enable the SDIO corresponding interrupts and DMA function */
sdio_interrupt_enable(SDIO, SDIO_INT_CCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_RXORE | SDIO_INT_DTEND);
dma_config(preadbuffer, (uint32_t)(blocksize >> 5));
sdio_idma_enable(SDIO);
/* configure SDIO data transmisson */
sdio_data_config(SDIO, SD_DATATIMEOUT, totalnumber_bytes, datablksize);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOSDIO);
sdio_trans_start_enable(SDIO);
/* send CMD17(READ_SINGLE_BLOCK) to read a block */
sdio_command_response_config(SDIO, SD_CMD_READ_SINGLE_BLOCK, (uint32_t)readaddr, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_READ_SINGLE_BLOCK);
if(SD_OK != status) {
return status;
}
while((0U == transend) && (SD_OK == transerror)) {
}
if(SD_OK != transerror) {
return transerror;
}
} else {
status = SD_PARAMETER_INVALID;
}
return status;
}
12.4.5 SD卡lock和unlock配置函數(shù)
SD卡lock和unlock配置函數(shù)如下所示。通過(guò)形參實(shí)現(xiàn)對(duì)SD卡的lock和unlock,若希望lock SD卡,lcokstate配置為SD_LOCK;若希望unlock SD卡,lockstate配置為SD_UNLOCK.
C
sd_error_enum sd_lock_unlock(uint8_t lockstate)
{
sd_error_enum status = SD_OK;
uint8_t cardstate = 0U, tempbyte = 0U;
uint32_t pwd1 = 0U, pwd2 = 0U, response = 0U, timeout = 0U;
uint16_t tempccc = 0U;
/* get the card command classes from CSD */
tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_24_31BITS) >> 24U);
tempccc = ((uint16_t)tempbyte << 4U);
tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_16_23BITS) >> 16U);
tempccc |= (((uint16_t)tempbyte & 0xF0U) >> 4U);
if(0U == (tempccc & SD_CCC_LOCK_CARD)) {
/* don't support the lock command */
status = SD_FUNCTION_UNSUPPORTED;
return status;
}
/* password pattern */
pwd1 = (0x01020600U | lockstate);
pwd2 = 0x03040506U;
/* clear all DSM configuration */
sdio_data_config(SDIO, 0U, 0U, SDIO_DATABLOCKSIZE_1BYTE);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOCARD);
sdio_dsm_disable(SDIO);
sdio_idma_disable(SDIO);
/* send CMD16(SET_BLOCKLEN) to set the block length */
sdio_command_response_config(SDIO, SD_CMD_SET_BLOCKLEN, (uint32_t)8U, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SET_BLOCKLEN);
if(SD_OK != status) {
return status;
}
/* send CMD13(SEND_STATUS), addressed card sends its status register */
sdio_command_response_config(SDIO, SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SEND_STATUS);
if(SD_OK != status) {
return status;
}
response = sdio_response_get(SDIO, SDIO_RESPONSE0);
timeout = 100000U;
while((0U == (response & SD_R1_READY_FOR_DATA)) && (timeout > 0U)) {
/* continue to send CMD13 to polling the state of card until buffer empty or timeout */
--timeout;
/* send CMD13(SEND_STATUS), addressed card sends its status registers */
sdio_command_response_config(SDIO, SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_SEND_STATUS);
if(SD_OK != status) {
return status;
}
response = sdio_response_get(SDIO, SDIO_RESPONSE0);
}
if(0U == timeout) {
status = SD_ERROR;
return status;
}
/* send CMD42(LOCK_UNLOCK) to set/reset the password or lock/unlock the card */
sdio_command_response_config(SDIO, SD_CMD_LOCK_UNLOCK, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_LOCK_UNLOCK);
if(SD_OK != status) {
return status;
}
response = sdio_response_get(SDIO, SDIO_RESPONSE0);
/* configure the SDIO data transmisson */
sdio_data_config(SDIO, SD_DATATIMEOUT, (uint32_t)8, SDIO_DATABLOCKSIZE_8BYTES);
sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOCARD);
sdio_dsm_enable(SDIO);
/* write password pattern */
sdio_data_write(SDIO, pwd1);
sdio_data_write(SDIO, pwd2);
/* whether some error occurs and return it */
if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTCRCERR)) {
status = SD_DATA_CRC_ERROR;
sdio_flag_clear(SDIO, SDIO_FLAG_DTCRCERR);
return status;
} else if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTTMOUT)) {
status = SD_DATA_TIMEOUT;
sdio_flag_clear(SDIO, SDIO_FLAG_DTTMOUT);
return status;
} else if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_TXURE)) {
status = SD_TX_UNDERRUN_ERROR;
sdio_flag_clear(SDIO, SDIO_FLAG_TXURE);
return status;
} else {
/* if else end */
}
/* clear the SDIO_INTC flags */
sdio_flag_clear(SDIO, SDIO_MASK_INTC_FLAGS);
/* get the card state and wait the card is out of programming and receiving state */
status = sd_card_state_get(&cardstate);
while((SD_OK == status) && ((SD_CARDSTATE_PROGRAMMING == cardstate) || (SD_CARDSTATE_RECEIVING == cardstate))) {
status = sd_card_state_get(&cardstate);
}
return status;
}
12.4.6 SD卡erase擦除操作函數(shù)
SD卡擦除操作函數(shù)如下,其形參為擦除起始地址以及結(jié)束地址。
C
sd_error_enum sd_erase(uint32_t startaddr, uint32_t endaddr)
{
/* initialize the variables */
sd_error_enum status = SD_OK;
uint32_t count = 0U, clkdiv = 0U;
__IO uint32_t delay = 0U;
uint8_t cardstate = 0U, tempbyte = 0U;
uint16_t tempccc = 0U;
/* get the card command classes from CSD */
tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_24_31BITS) >> 24U);
tempccc = (uint16_t)((uint16_t)tempbyte << 4U);
tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_16_23BITS) >> 16U);
tempccc |= ((uint16_t)tempbyte & 0xF0U) >> 4U;
if(0U == (tempccc & SD_CCC_ERASE)) {
/* don't support the erase command */
status = SD_FUNCTION_UNSUPPORTED;
return status;
}
clkdiv = (SDIO_CLKCTL(SDIO) & SDIO_CLKCTL_DIV);
clkdiv *= 2U;
delay = 168000U / clkdiv;
/* check whether the card is locked */
if(sdio_response_get(SDIO, SDIO_RESPONSE0) & SD_CARDSTATE_LOCKED) {
status = SD_LOCK_UNLOCK_FAILED;
return(status);
}
/* blocksize is fixed in 512B for SDHC card */
if(SDIO_HIGH_CAPACITY_SD_CARD != cardtype) {
startaddr *= 512U;
endaddr *= 512U;
}
if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype) ||
(SDIO_HIGH_CAPACITY_SD_CARD == cardtype)) {
/* send CMD32(ERASE_WR_BLK_START) to set the address of the first write block to be erased */
sdio_command_response_config(SDIO, SD_CMD_ERASE_WR_BLK_START, startaddr, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_ERASE_WR_BLK_START);
if(SD_OK != status) {
return status;
}
/* send CMD33(ERASE_WR_BLK_END) to set the address of the last write block of the continuous range to be erased */
sdio_command_response_config(SDIO, SD_CMD_ERASE_WR_BLK_END, endaddr, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_ERASE_WR_BLK_END);
if(SD_OK != status) {
return status;
}
}
/* send CMD38(ERASE) to set the address of the first write block to be erased */
sdio_command_response_config(SDIO, SD_CMD_ERASE, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
sdio_csm_enable(SDIO);
/* check if some error occurs */
status = r1_error_check(SD_CMD_ERASE);
if(SD_OK != status) {
return status;
}
/* loop until the counter is reach to the calculated time */
for(count = 0U; count < delay; count++) {
}
/* get the card state and wait the card is out of programming and receiving state */
status = sd_card_state_get(&cardstate);
while((SD_OK == status) && ((SD_CARDSTATE_PROGRAMMING == cardstate) || (SD_CARDSTATE_RECEIVING == cardstate))) {
status = sd_card_state_get(&cardstate);
}
return status;
}
12.4.7 主函數(shù)
SD卡主函數(shù)如下,可實(shí)現(xiàn)對(duì)SD卡的擦寫(xiě)讀以及加鎖解鎖操作。
C
int main()
{
sd_error_enum sd_error;
Drv_Err state = DRV_ERROR;
uint16_t i = 5;
uint8_t *pdata;
/* enable the CPU Cache */
driver_init();
/* configure the NVIC and USART */
nvic_config();
bsp_led_group_init();
/* turn off all the LEDs */
bsp_led_off(&LED1);
bsp_led_off(&LED2);
/* initialize the card */
do {
sd_error = sd_io_init();
} while((SD_OK != sd_error) && (--i));
if(i) {
printf_log("\r\n Card init success!\r\n");
} else {
printf_log("\r\n Card init failed!\r\n");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
}
/* get the information of the card and print it out by USART */
card_info_get();
/* init the write buffer */
for(i = 0; i < 512; i++) {
buf_write[i] = i;
}
/* clean and invalidate buffer in D-Cache */
SCB_CleanInvalidateDCache_by_Addr(buf_write, 512 * 4);
printf_log("\r\n\r\n Card test:");
/* single block operation test */
sd_error = sd_block_write(buf_write, 100, 512);
if(SD_OK != sd_error) {
printf_log("\r\n Block write fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n Block write success!");
}
sd_error = sd_block_read(buf_read, 100, 512);
if(SD_OK != sd_error) {
printf_log("\r\n Block read fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n Block read success!");
SCB_CleanInvalidateDCache_by_Addr(buf_read, 512 * 4);
pdata = (uint8_t *)buf_read;
/* print data by USART */
printf_log("\r\n");
for(i = 0; i < 128; i++) {
printf_log(" %3d %3d %3d %3d ", *pdata, *(pdata + 1), *(pdata + 2), *(pdata + 3));
pdata += 4;
if(0 == (i + 1) % 4) {
printf_log("\r\n");
}
}
}
/* compare the write date and the read data */
state = memory_compare((uint8_t *)buf_write, (uint8_t *)buf_read, 128*4);
if(SUCCESS == state) {
printf_log("\r\n Single block read compare successfully!");
} else {
printf_log("\r\n Single block read compare fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
}
/* lock and unlock operation test */
if(SD_CCC_LOCK_CARD & sd_cardinfo.card_csd.ccc) {
/* lock the card */
sd_error = sd_lock_unlock(SD_LOCK);
if(SD_OK != sd_error) {
printf_log("\r\n Lock failed!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n The card is locked!");
}
sd_error = sd_erase(100, 101);
if(SD_OK != sd_error) {
printf_log("\r\n Erase failed!");
} else {
printf_log("\r\n Erase success!");
}
/* unlock the card */
sd_error = sd_lock_unlock(SD_UNLOCK);
if(SD_OK != sd_error) {
printf_log("\r\n Unlock failed!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n The card is unlocked!");
}
sd_error = sd_erase(100, 101);
if(SD_OK != sd_error) {
printf_log("\r\n Erase failed!");
} else {
printf_log("\r\n Erase success!");
}
sd_error = sd_block_read(buf_read, 100, 512);
if(SD_OK != sd_error) {
printf_log("\r\n Block read fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n Block read success!");
SCB_CleanInvalidateDCache_by_Addr(buf_read, 512 * 4);
pdata = (uint8_t *)buf_read;
/* print data by USART */
printf_log("\r\n");
for(i = 0; i < 128; i++) {
printf_log(" %3d %3d %3d %3d ", *pdata, *(pdata + 1), *(pdata + 2), *(pdata + 3));
pdata += 4;
if(0 == (i + 1) % 4) {
printf_log("\r\n");
}
}
}
}
/* multiple blocks operation test */
sd_error = sd_multiblocks_write(buf_write, 200, 512, 3);
if(SD_OK != sd_error) {
printf_log("\r\n Multiple block write fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n Multiple block write success!");
}
sd_error = sd_multiblocks_read(buf_read, 200, 512, 3);
if(SD_OK != sd_error) {
printf_log("\r\n Multiple block read fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
} else {
printf_log("\r\n Multiple block read success!");
SCB_CleanInvalidateDCache_by_Addr(buf_read, 512 * 4);
pdata = (uint8_t *)buf_read;
/* print data by USART */
printf_log("\r\n");
for(i = 0; i < 512; i++) {
printf_log(" %3d %3d %3d %3d ", *pdata, *(pdata + 1), *(pdata + 2), *(pdata + 3));
pdata += 4;
if(0 == (i + 1) % 4) {
printf_log("\r\n");
}
}
}
/* compare the write date and the read data */
state = memory_compare((uint8_t *)buf_write, (uint8_t *)buf_read, 128*3*4);
if(SUCCESS == state) {
printf_log("\r\n Multiple block read compare successfully!");
} else {
printf_log("\r\n Multiple block read compare fail!");
/* turn on LED1, LED2 */
bsp_led_on(&LED1);
bsp_led_on(&LED2);
while(1) {
}
}
printf_log("\r\n SD card test successfully!");
while(1) {};
}
12.5 實(shí)驗(yàn)結(jié)果
將SD卡讀寫(xiě)實(shí)驗(yàn)例程燒錄到海棠派開(kāi)發(fā)板中,并在卡槽中插入SD卡,在液晶屏上,將會(huì)觀(guān)察到SD卡相關(guān)操作結(jié)果。