12.1 实验内容
通过本实验主要学习以下内容:
12.2 实验原理
SD卡是一种主要以Nand Flash作为存储介质,具有体积小、数据传输速度快以及支持热插拔的优点。如今,已被广泛应用于数码相机、便携式移动设备以及手机等多种设备中。SD卡的驱动一般有SPI接口或SDIO接口,本例程介绍使用GD32F4xx的SDIO接口驱动SD卡的实现。
12.2.1 SD卡基础知识
SD卡:secure digital memory card是一种安全存储器件。属性是快闪存储器(flash eeprom),功能用来存储数据。
SD卡虽然是薄薄的一片,但是它并不是一个整体,而是由大量的集成电路组成。SD卡的内部结构如下图所示,主要由信号端子,接口控制器和存储区组成。
SD卡主要有两种模式,SD模式和SPI模式。不同模式下,接口定义不同。下面是SD卡的引脚。
两种模式的接口定义如下
SD模式中,主要由VCC(电源),VSS(GND),CLK(时钟,由主控提供),CMD(命令),DAT0-3(数据输入输出),由6线制组成进行通信。SPI模式,主要采用4线制通信,除了电源地外,由MISO,MOSI,CLK,CS组成。下面简单介绍SD模式的操作。
要驱动SD卡工作,主要涉及两个步骤。第一个步骤是SD卡的识别过程。第二个步骤是对SD卡进行读写过程,即主机控制器和SD卡之间进行数据传输的过程。
要使SD卡能正常工作,一是要给SD卡供给稳定的电压,二是要SD卡按用户规定的方式工作。这两项工作的实现,都是主机控制器通过给SD卡发送控制命令来实现的。
主机(SDIO控制器)要驱动SD卡工作,要使用许多的命令,包括应用层命令ACMD 和 通用命令 CMD. 主机(SDIO控制器)把命令发送给SD卡,SD卡会作出回应,这里的回应叫做响应,响应命令分为6类,分别是R1、R1b、R2、R3、R6、R7。主机(SDIO控制器)给SD卡发送命令之后,SD卡会作出响应,响应中包含主机(SDIO控制器)需要的数据,这些数据有SD的信息,容量,和存储数据等等。上面已经提到了,SD卡工作,主要是识别和数据传输,它的识别过程有些复杂,写代码的时候,可以参考协议给的初始化流程图。数据传输包括读和写,单字节和多字节读写。下两节描述识别初始化流程图和数据读写时序图。
1、读写数据的时序图
SDIO与SD卡通信一般以数据块的形式进行传输,SDIO(多)数据块读操作,如下图所示。
SDIO(多)数据块写操作,如下图所示。
2、命令格式
SDIO所有的命令和响应都是在SDIO_CMD引脚上面传输的,命令长度固定为48位,SDIO命令格式如下表所示。
3、寄存器
SDIO控制器的寄存器,主要设置SDIO控制器和命令的索引与参数。SD卡有5个寄存器CID,RCA,CSD,SCR.OCR。SD卡的信息从SD卡寄存器中获取。
SD卡正常工作,就是根据SD卡初始化流程图,发送命令,收到回复,直到流程结束。传输数据,也是根据读写时序图,将要发送的数据放进命令中发送出去。
12.2.2 SDIO模块原理
SDIO为安全的数字输入输出接口,可以用于驱动SD卡、EMMC等,主要特征如下:
◼ e*MMC: 与多媒体卡系统规格书 V4.2 及之前的版本全兼容。有三种不同的数据总线模式:1 位(默认)、 4 位和 8 位;
◼ SD 卡: 与SD 存储卡规格版本3.0 全兼容;
◼ SD I/O: 与 SD I/O 卡规格版本 3.0 全兼容,有两种不同的数据总线模式: 1 位(默认)和4位(包括SDR和DDR);
◼ 104MHz 数据传输频率和8 位数据传输模式;
◼ 中断和 DMA 请求;
◼ 数据传输支持 DDR 模式。
SDIO模块结构框图如下所示。主要包括以下三个部分:SDIO 适配器:由控制单元、命令单元和数据单元组成,控制单元管理时钟信号,命令单元管理命令的传输,数据单元管理数据的传输; AHB 接口:包括通过 AHB 总线访问的寄存器、用于数据传输的 FIFO 单元以及产生中断和 DMA 请求信号; 内部 DMA(IDMA)以及 AHB 主机接口 。
SDIO模块可以实现对SD卡的完全驱动以及协议的实现,包括命令、响应等相关操作,本例程实现使用SDIO驱动SD卡初始化以及读写测试等相关操作,具体实现可以参考GD32H7用户手册以及代码解析等。
12.3 硬件设计
SD卡相关硬件电路如下图所示,实验板上具有SD卡卡座,信号线上有四根数据线,一根CMD命令线以及一根CLK时钟线,所有信号线通过10K电阻进行上拉,电源地信号线具有10uf以及100nf电容,SD卡插入时,金属接触点朝下插入。
12.4 代码解析
12.4.1 SDIO初始化配置函数
SDIO初始化配置在sd_io_init()函数中,其中包括sd_init()初始化、sd_card_information_get()SD卡信息获取、sd_card_select_deselect()SD卡选择、sd_cardstatus_get()SD卡状态获取、sd_bus_mode_config()SD卡总线宽度配置以及sd_transfer_mode_config()SD卡通信模式配置,历程中选择了4线查询模式。
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 */ #if (SDIO_BUSMODE == BUSMODE_4BIT) status = sd_bus_mode_config(SDIO_BUSMODE_4BIT, SDIO_SPEEDMODE); #else status = sd_bus_mode_config(SDIO_BUSMODE_1BIT, SDIO_SPEEDMODE); #endif } #ifdef USE_18V_SWITCH if(SD_OK == status) { /* UHS-I Hosts can perform sampling point tuning using tuning command */ status = sd_tuning(); } #endif /* USE_18V_SWITCH */ 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卡信息函数
获取SD卡信息的函数如下所示,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卡数据块写入函数
SD卡数据块写入函数如下所示,通过该函数可实现SD卡数据块的数据写入。
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卡数据块读取函数
SD卡数据块读取函数如下所示。
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配置函数
SD卡lock和unlock配置函数如下所示。通过形参实现对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擦除操作函数
SD卡擦除操作函数如下,其形参为擦除起始地址以及结束地址。
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 主函数
SD卡主函数如下,可实现对SD卡的擦写读以及加锁解锁操作。
C int main() { sd_error_enum sd_error; Drv_Err state = DRV_ERROR; uint16_t i = 5; #ifdef DATA_PRINT uint8_t *pdata; #endif /* DATA_PRINT */ /* 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!"); #ifdef DATA_PRINT 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"); } } #endif /* DATA_PRINT */ } /* 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!"); #ifdef DATA_PRINT 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"); } } #endif /* DATA_PRINT */ } } /* 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!"); #ifdef DATA_PRINT 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"); } } #endif /* DATA_PRINT */ } /* 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 实验结果
将SD卡读写实验例程烧录到海棠派开发板中,并在卡槽中插入SD卡,在液晶屏上,将会观察到SD卡相关操作结果。
本教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网
全部0条评论
快来发表一下你的评论吧 !