打印
[研电赛技术支持]

【GD32F470紫藤派使用手册】第十二讲 SDIO-SD卡读写实验

[复制链接]
6280|52
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

12.1 实验内容
通过本实验主要学习以下内容:
• SDIO操作原理
• SD卡读写实现
12.2 实验原理
SD卡是一种主要以Nand Flash作为存储介质,具有体积小、数据传输速度快以及支持热插拔的优点。如今,已被广泛应用于数码相机、便携式移动设备以及手机等多种设备中。SD卡的驱动一般有SPI接口或SDIO接口,本例程介绍使用GD32F4xxSDIO接口驱动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线制通信,除了电源地外,由MISOMOSICLKCS组成。下面简单介绍SD模式的操作。
要驱动SD卡工作,主要涉及两个步骤。第一个步骤是SD卡的识别过程。第二个步骤是对SD卡进行读写过程,即主机控制器和SD卡之间进行数据传输的过程。
要使SD卡能正常工作,一是要给SD卡供给稳定的电压,二是要SD卡按用户规定的方式工作。这两项工作的实现,都是主机控制器通过给SD卡发送控制命令来实现的。
主机(SDIO控制器)要驱动SD卡工作,要使用许多的命令,包括应用层命令ACMD 和 通用命令 CMD. 主机(SDIO控制器)把命令发送给SD卡,SD卡会作出回应,这里的回应叫做响应,响应命令分为6类,分别是R1R1bR2R3R6R7。主机(SDIO控制器)给SD卡发送命令之后,SD卡会作出响应,响应中包含主机(SDIO控制器)需要的数据,这些数据有SD的信息,容量,和存储数据等等。上面已经提到了,SD卡工作,主要是识别和数据传输,它的识别过程有些复杂,写代码的时候,可以参考协议给的初始化流程图。数据传输包括读和写,单字节和多字节读写。下两节描述识别初始化流程图和数据读写时序图。
1、读写数据的时序图
SDIOSD卡通信一般以数据块的形式进行传输,SDIO()数据块读操作,如下图所示。
SDIO()数据块写操作,如下图所示。
2、命令格式
SDIO所有的命令和响应都是在SDIO_CMD引脚上面传输的,命令长度固定为48位,SDIO命令格式如下表所示。
3、寄存器
SDIO控制器的寄存器,主要设置SDIO控制器和命令的索引与参数。SD卡有5个寄存器CID,RCA,CSD,SCR.OCRSD卡的信息从SD卡寄存器中获取。
SD卡正常工作,就是根据SD卡初始化流程图,发送命令,收到回复,直到流程结束。传输数据,也是根据读写时序图,将要发送的数据放进命令中发送出去。
12.2.2 SDIO模块原理
SDIO为安全的数字输入输出接口,可以用于驱动SD卡、EMMC等,主要特征如下:
◼ MMC: 与多媒体卡系统规格书 V4.2 及之前的版本全兼容。有三种不同的数据总线模式:1 (默认)4 位和 8 位;
◼ SD 卡: 与SD 存储卡规格版本2.0 全兼容;
◼ SD I/O: 与 SD I/O 卡规格版本 2.0 全兼容,有两种不同的数据总线模式: 1 (默认)4位;
◼ CE-ATA: 与 CE-ATA 数字协议版本 1.1 全兼容;
◼ 48MHz 数据传输频率和8 位数据传输模式;
中断和 DMA 请求;
完成信号使能和失能(CE-ATA)。  
SDIO模块结构框图如下所示。主要包含两大部分:SDIO 适配器:由控制单元、命令单元和数据单元组成,控制单元管理时钟信号,命令单元管理命令的传输,数据单元管理数据的传输;AHB 接口:包括通过 AHB 总线访问的寄存器、用于数据传输的 FIFO 单元以及产生中断和DMA 请求信号。  
SDIO模块可以实现对SD卡的完全驱动以及协议的实现,包括命令、响应等相关操作,本例程实现使用SDIO驱动SD卡初始化以及读写测试等相关操作,具体实现可以参考GD32F4xx用户手册以及代码解析等。
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("\r\n the card is locked!");
        while (1){
        }
    }
    if ((SD_OK == status) && (!(cardstate & 0x02000000)))
    {
        /* set bus mode */
        status = sd_bus_mode_config(SDIO_BUSMODE_4BIT);
//        status = sd_bus_mode_config( SDIO_BUSMODE_1BIT );
    }
    if (SD_OK == status)
    {
        /* set data transfer mode */
//        status = sd_transfer_mode_config( SD_DMA_MODE );
        status = sd_transfer_mode_config( SD_POLLING_MODE );
    }
    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("\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("\r\n## Card version 4.xx ##");
            }
            else
            {
              //  printf("\r\n## Card version 3.0x ##");
            }
        }
        else
        {
           // printf("\r\n## Card version 2.00 ##");
        }
    }
    else if(1 == sd_spec)
    {
       // printf("\r\n## Card version 1.10 ##");
    }
    else if(0 == sd_spec)
    {
       // printf("\r\n## Card version 1.0x ##");
    }
   
    sd_security = (sd_scr[1] & 0x00700000) >> 20;
    if(2 == sd_security)
    {
      //  printf("\r\n## SDSC card ##");
    }
    else if(3 == sd_security)
    {   
       // printf("\r\n## SDHC card ##");
    }
    else if(4 == sd_security)
    {
       // printf("\r\n## SDXC card ##");
    }
   
    block_count = (sd_cardinfo.card_csd.c_size + 1)*1024;
    block_size = 512;
//    printf("\r\n## Device size is %dKB ##", sd_card_capacity_get());
//    printf("\r\n## Block size is %dB ##", block_size);
//    printf("\r\n## Block count is %d ##", block_count);
   
    if(sd_cardinfo.card_csd.read_bl_partial){
      //  printf("\r\n## Partial blocks for read allowed ##" );
    }
    if(sd_cardinfo.card_csd.write_bl_partial){
       // printf("\r\n## Partial blocks for write allowed ##" );
    }
    temp_ccc = sd_cardinfo.card_csd.ccc;
    //printf("\r\n## CardCommandClasses is: %x ##", temp_ccc);
    if((SD_CCC_BLOCK_READ & temp_ccc) && (SD_CCC_BLOCK_WRITE & temp_ccc)){
      //  printf("\r\n## Block operation supported ##");
    }
    if(SD_CCC_ERASE & temp_ccc){
      //  printf("\r\n## Erase supported ##");
    }
    if(SD_CCC_WRITE_PROTECTION & temp_ccc){
      //  printf("\r\n## Write protection supported ##");
    }
    if(SD_CCC_LOCK_CARD & temp_ccc){
     //   printf("\r\n## Lock unlock supported ##");
    }
    if(SD_CCC_APPLICATION_SPECIFIC & temp_ccc){
     //   printf("\r\n## Application specific supported ##");
    }
    if(SD_CCC_IO_MODE & temp_ccc){
      //  printf("\r\n## I/O mode supported ##");
    }
    if(SD_CCC_SWITCH & temp_ccc){
      //  printf("\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 = 0;
    uint32_t count = 0, align = 0, datablksize = SDIO_DATABLOCKSIZE_1BYTE, *ptempbuff = pwritebuffer;
    uint32_t transbytes = 0, restwords = 0, response = 0;
    __IO uint32_t timeout = 0;
   
    if(NULL == pwritebuffer){
        status = SD_PARAMETER_INVALID;
        return status;
    }
   
    transerror = SD_OK;
    transend = 0;
    totalnumber_bytes = 0;
    /* clear all DSM configuration */
    sdio_data_config(0, 0, SDIO_DATABLOCKSIZE_1BYTE);
    sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOCARD);
    sdio_dsm_disable();
    sdio_dma_disable();
   
    /* check whether the card is locked */
    if(sdio_response_get(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){
        blocksize = 512;
        writeaddr /= 512;
    }
   
    align = blocksize & (blocksize - 1);
    if((blocksize > 0) && (blocksize <= 2048) && (0 == align)){
        datablksize = sd_datablocksize_get(blocksize);
        /* send CMD16(SET_BLOCKLEN) to set the block length */
        sdio_command_response_config(SD_CMD_SET_BLOCKLEN, (uint32_t)blocksize, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO_WAITTYPE_NO);
        sdio_csm_enable();
        
        /* 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(SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* check if some error occurs */
    status = r1_error_check(SD_CMD_SEND_STATUS);
    if(SD_OK != status){
        return status;
    }
   
    response = sdio_response_get(SDIO_RESPONSE0);
    timeout = 100000;

    while((0 == (response & SD_R1_READY_FOR_DATA)) && (timeout > 0)){
        /* 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(SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO_WAITTYPE_NO);
        sdio_csm_enable();
        /* check if some error occurs */
        status = r1_error_check(SD_CMD_SEND_STATUS);
        if(SD_OK != status){
            return status;
        }
        response = sdio_response_get(SDIO_RESPONSE0);
    }
    if(0 == timeout){
        return SD_ERROR;
    }
   
    /* send CMD24(WRITE_BLOCK) to write a block */
    sdio_command_response_config(SD_CMD_WRITE_BLOCK, writeaddr, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* check if some error occurs */
    status = r1_error_check(SD_CMD_WRITE_BLOCK);
    if(SD_OK != status){
        return status;
    }
   
    stopcondition = 0;
    totalnumber_bytes = blocksize;
   
    /* configure the SDIO data transmission */
    sdio_data_config(SD_DATATIMEOUT, totalnumber_bytes, datablksize);
    sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOCARD);
    sdio_dsm_enable();
   
    if(SD_POLLING_MODE == transmode){
        /* polling mode */
        while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_TXURE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE)){
            if(RESET != sdio_flag_get(SDIO_FLAG_TFH)){
                /* at least 8 words can be written into the FIFO */
                if((totalnumber_bytes - transbytes) < SD_FIFOHALF_BYTES){
                    restwords = (totalnumber_bytes - transbytes)/4 + (((totalnumber_bytes - transbytes)%4 == 0) ? 0 : 1);
                    for(count = 0; count < restwords; count++){
                        sdio_data_write(*ptempbuff);
                        ++ptempbuff;
                        transbytes += 4;
                    }
                }else{
                    for(count = 0; count < SD_FIFOHALF_WORDS; count++){
                        sdio_data_write(*(ptempbuff + count));
                    }
                    /* 8 words(32 bytes) has been transferred */
                    ptempbuff += SD_FIFOHALF_WORDS;
                    transbytes += SD_FIFOHALF_BYTES;
                }
            }
        }
        
        /* whether some error occurs and return it */
        if(RESET != sdio_flag_get(SDIO_FLAG_DTCRCERR)){
            status = SD_DATA_CRC_ERROR;
            sdio_flag_clear(SDIO_FLAG_DTCRCERR);
            return status;
        }else if(RESET != sdio_flag_get(SDIO_FLAG_DTTMOUT)){
            status = SD_DATA_TIMEOUT;
            sdio_flag_clear(SDIO_FLAG_DTTMOUT);
            return status;
        }else if(RESET != sdio_flag_get(SDIO_FLAG_TXURE)){
            status = SD_TX_UNDERRUN_ERROR;
            sdio_flag_clear(SDIO_FLAG_TXURE);
            return status;
        }else if(RESET != sdio_flag_get(SDIO_FLAG_STBITE)){
            status = SD_START_BIT_ERROR;
            sdio_flag_clear(SDIO_FLAG_STBITE);
            return status;
        }
    }else if(SD_DMA_MODE == transmode){
        /* DMA mode */
        /* enable the SDIO corresponding interrupts and DMA */
        sdio_interrupt_enable(SDIO_INT_DTCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_TXURE | SDIO_INT_DTEND | SDIO_INT_STBITE);
        dma_transfer_config(pwritebuffer, blocksize);
        sdio_dma_enable();
        
        timeout = 100000;
        while((RESET == dma_flag_get(DMA1, DMA_CH3, DMA_FLAG_FTF)) && (timeout > 0)){
            timeout--;
            if(0 == timeout){
                return SD_ERROR;
            }
        }
        while ((0 == transend) && (SD_OK == transerror)){
        }

        if(SD_OK != transerror){
            return transerror;
        }
    }else{
        status = SD_PARAMETER_INVALID;
        return status;
    }
   
    /* clear the SDIO_INTC flags */
    sdio_flag_clear(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.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 = 0, align = 0, datablksize = SDIO_DATABLOCKSIZE_1BYTE, *ptempbuff = preadbuffer;
    __IO uint32_t timeout = 0;
   
    if(NULL == preadbuffer){
        status = SD_PARAMETER_INVALID;
        return status;
    }
   
    transerror = SD_OK;
    transend = 0;
    totalnumber_bytes = 0;
    /* clear all DSM configuration */
    sdio_data_config(0, 0, SDIO_DATABLOCKSIZE_1BYTE);
    sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOCARD);
    sdio_dsm_disable();
    sdio_dma_disable();
   
    /* check whether the card is locked */
    if(sdio_response_get(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){
        blocksize = 512;
        readaddr /= 512;
    }
   
    align = blocksize & (blocksize - 1);
    if((blocksize > 0) && (blocksize <= 2048) && (0 == align)){
        datablksize = sd_datablocksize_get(blocksize);
        /* send CMD16(SET_BLOCKLEN) to set the block length */
        sdio_command_response_config(SD_CMD_SET_BLOCKLEN, (uint32_t)blocksize, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO_WAITTYPE_NO);
        sdio_csm_enable();
        
        /* 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 = 0;
    totalnumber_bytes = blocksize;
   
    /* configure SDIO data transmission */
    sdio_data_config(SD_DATATIMEOUT, totalnumber_bytes, datablksize);
    sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOSDIO);
    sdio_dsm_enable();
   
    /* send CMD17(READ_SINGLE_BLOCK) to read a block */
    sdio_command_response_config(SD_CMD_READ_SINGLE_BLOCK, (uint32_t)readaddr, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* check if some error occurs */
    status = r1_error_check(SD_CMD_READ_SINGLE_BLOCK);
    if(SD_OK != status){
        return status;
    }
   
    if(SD_POLLING_MODE == transmode){
        /* polling mode */
        while(!sdio_flag_get(SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_STBITE)){
            if(RESET != sdio_flag_get(SDIO_FLAG_RFH)){
                /* at least 8 words can be read in the FIFO */
                for(count = 0; count < SD_FIFOHALF_WORDS; count++){
                    *(ptempbuff + count) = sdio_data_read();
                }
                ptempbuff += SD_FIFOHALF_WORDS;
            }
        }
        
        /* whether some error occurs and return it */
        if(RESET != sdio_flag_get(SDIO_FLAG_DTCRCERR)){
            status = SD_DATA_CRC_ERROR;
            sdio_flag_clear(SDIO_FLAG_DTCRCERR);
            return status;
        }else if(RESET != sdio_flag_get(SDIO_FLAG_DTTMOUT)){
            status = SD_DATA_TIMEOUT;
            sdio_flag_clear(SDIO_FLAG_DTTMOUT);
            return status;
        }else if(RESET != sdio_flag_get(SDIO_FLAG_RXORE)){
            status = SD_RX_OVERRUN_ERROR;
            sdio_flag_clear(SDIO_FLAG_RXORE);
            return status;
        }else if(RESET != sdio_flag_get(SDIO_FLAG_STBITE)){
            status = SD_START_BIT_ERROR;
            sdio_flag_clear(SDIO_FLAG_STBITE);
            return status;
        }
        while(RESET != sdio_flag_get(SDIO_FLAG_RXDTVAL)){
            *ptempbuff = sdio_data_read();
            ++ptempbuff;
        }
        /* clear the SDIO_INTC flags */
        sdio_flag_clear(SDIO_MASK_INTC_FLAGS);
    }else if(SD_DMA_MODE == transmode){
        /* DMA mode */
        /* enable the SDIO corresponding interrupts and DMA function */
        sdio_interrupt_enable(SDIO_INT_CCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_RXORE | SDIO_INT_DTEND | SDIO_INT_STBITE);
        sdio_dma_enable();
        dma_receive_config(preadbuffer, blocksize);
        timeout = 100000;
        while((RESET == dma_flag_get(DMA1, DMA_CH3, DMA_FLAG_FTF)) && (timeout > 0)){
            timeout--;
            if(0 == timeout){
                return SD_ERROR;
            }
        }
    }else{
        status = SD_PARAMETER_INVALID;
    }
    return status;
}
12.4.5 SDlockunlock配置函数
SDlockunlock配置函数如下所示。通过形参实现对SD卡的lockunlock,若希望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 = 0, tempbyte = 0;
    uint32_t pwd1 = 0, pwd2 = 0, response = 0;
    __IO uint32_t timeout = 0;
    uint16_t tempccc = 0;
   
    /* get the card command classes from CSD */
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_24_31BITS) >> 24);
    tempccc = (uint16_t)((uint16_t)tempbyte << 4);
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_16_23BITS) >> 16);
    tempccc |= (uint16_t)((uint16_t)(tempbyte & 0xF0) >> 4);
   
    if(0 == (tempccc & SD_CCC_LOCK_CARD)){
        /* don't support the lock command */
        status = SD_FUNCTION_UNSUPPORTED;
        return status;
    }
    /* password pattern */
    pwd1 = (0x01020600|lockstate);
    pwd2 = 0x03040506;
   
    /* clear all DSM configuration */
    sdio_data_config(0, 0, SDIO_DATABLOCKSIZE_1BYTE);
    sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOCARD);
    sdio_dsm_disable();
    sdio_dma_disable();
   
    /* send CMD16(SET_BLOCKLEN) to set the block length */
    sdio_command_response_config(SD_CMD_SET_BLOCKLEN, (uint32_t)8, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* 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(SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* check if some error occurs */
    status = r1_error_check(SD_CMD_SEND_STATUS);
    if(SD_OK != status){
        return status;
    }
   
    response = sdio_response_get(SDIO_RESPONSE0);
    timeout = 100000;
    while((0 == (response & SD_R1_READY_FOR_DATA)) && (timeout > 0)){
        /* 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(SD_CMD_SEND_STATUS, (uint32_t)sd_rca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO_WAITTYPE_NO);
        sdio_csm_enable();
        /* check if some error occurs */
        status = r1_error_check(SD_CMD_SEND_STATUS);
        if(SD_OK != status){
            return status;
        }
        response = sdio_response_get(SDIO_RESPONSE0);
    }
    if(0 == timeout){
        return SD_ERROR;
    }
   
    /* send CMD42(LOCK_UNLOCK) to set/reset the password or lock/unlock the card */
    sdio_command_response_config(SD_CMD_LOCK_UNLOCK, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* check if some error occurs */
    status = r1_error_check(SD_CMD_LOCK_UNLOCK);
    if(SD_OK != status){
        return status;
    }
   
    response = sdio_response_get(SDIO_RESPONSE0);
   
    /* configure the SDIO data transmission */
    sdio_data_config(SD_DATATIMEOUT, (uint32_t)8, SDIO_DATABLOCKSIZE_8BYTES);
    sdio_data_transfer_config(SDIO_TRANSMODE_BLOCK, SDIO_TRANSDIRECTION_TOCARD);
    sdio_dsm_enable();
   
    /* write password pattern */
    sdio_data_write(pwd1);
    sdio_data_write(pwd2);
   
    /* whether some error occurs and return it */
    if(RESET != sdio_flag_get(SDIO_FLAG_DTCRCERR)){
        status = SD_DATA_CRC_ERROR;
        sdio_flag_clear(SDIO_FLAG_DTCRCERR);
        return status;
    }else if(RESET != sdio_flag_get(SDIO_FLAG_DTTMOUT)){
        status = SD_DATA_TIMEOUT;
        sdio_flag_clear(SDIO_FLAG_DTTMOUT);
        return status;
    }else if(RESET != sdio_flag_get(SDIO_FLAG_TXURE)){
        status = SD_TX_UNDERRUN_ERROR;
        sdio_flag_clear(SDIO_FLAG_TXURE);
        return status;
    }else if(RESET != sdio_flag_get(SDIO_FLAG_STBITE)){
        status = SD_START_BIT_ERROR;
        sdio_flag_clear(SDIO_FLAG_STBITE);
        return status;
    }
   
    /* clear the SDIO_INTC flags */
    sdio_flag_clear(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 SDerase擦除操作函数
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 = 0, clkdiv = 0;
    __IO uint32_t delay = 0;
    uint8_t cardstate = 0, tempbyte = 0;
    uint16_t tempccc = 0;
   
    /* get the card command classes from CSD */
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_24_31BITS) >> 24);
    tempccc = (uint16_t)((uint16_t)tempbyte << 4);
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_16_23BITS) >> 16);
    tempccc |= (uint16_t)((uint16_t)(tempbyte & 0xF0) >> 4);
    if(0 == (tempccc & SD_CCC_ERASE)){
        /* don't support the erase command */
        status = SD_FUNCTION_UNSUPPORTED;
        return status;
    }
    clkdiv = (SDIO_CLKCTL & SDIO_CLKCTL_DIV);
    clkdiv += ((SDIO_CLKCTL & SDIO_CLKCTL_DIV8)>>31)*256;
    clkdiv += 2;
    delay = 120000 / clkdiv;
   
    /* check whether the card is locked */
    if (sdio_response_get(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 /= 512;
        endaddr /= 512;
    }
   
    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(SD_CMD_ERASE_WR_BLK_START, startaddr, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO_WAITTYPE_NO);
        sdio_csm_enable();
        /* 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(SD_CMD_ERASE_WR_BLK_END, endaddr, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO_WAITTYPE_NO);
        sdio_csm_enable();
        /* 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(SD_CMD_ERASE, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO_WAITTYPE_NO);
    sdio_csm_enable();
    /* 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 = 0; 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(void)
{
    sd_error_enum sd_error;
    uint16_t i = 5;
#ifdef DATA_PRINT
    uint8_t *pdata;
#endif /* DATA_PRINT */
   
    /* configure the NVIC, USART and LED */
    nvic_config();
   
    driver_init();   
         
    bsp_uart_init(&BOARD_UART);
   
    /* 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");
        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;
    }
   
    printf_log("\r\n\r\n Card test:");
   
    /* single block operation test */
    sd_error = sd_block_write(buf_write, 100*512, 512);
    if(SD_OK != sd_error){
                        
        printf_log("\r\n Block write fail!");
        while (1){
        }
    }else{
        printf_log("\r\n Block write success!");
    }
    sd_error = sd_block_read(buf_read, 100*512, 512);
    if(SD_OK != sd_error){
        printf_log("\r\n Block read fail!");

        while (1){
        }
    }else{
        //printf_log("\r\n Block read success!");
#ifdef DATA_PRINT
        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 */
    }
   
    /* 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!");
            while (1){
            }
        }else{
            printf_log("\r\n The card is locked!");
        }
        sd_error = sd_erase(100*512, 101*512);
        if(SD_OK != sd_error){
            printf_log("\r\n Erase failed!");
        }else{
                                        __NOP();
            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!");
            while (1){
            }
        }else{
            printf_log("\r\n The card is unlocked!");
        }
        sd_error = sd_erase(100*512, 101*512);
        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, 512);
        if(SD_OK != sd_error){
            printf_log("\r\n Block read fail!");
            while (1){
            }
        }else{
            printf_log("\r\n Block read success!");
#ifdef DATA_PRINT
        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, 512, 3);
    if(SD_OK != sd_error){
        printf_log("\r\n Multiple block write fail!");
        while (1){
        }
    }else{
        printf_log("\r\n Multiple block write success!");
    }
    sd_error = sd_multiblocks_read(buf_read, 200*512, 512, 3);
    if(SD_OK != sd_error){
        printf_log("\r\n Multiple block read fail!");

        while (1){
        }
    }else{
        printf_log("\r\n Multiple block read success!");
#ifdef DATA_PRINT
        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 */
    }
   
    while (1){
    }
}
12.5 实验结果
SD卡读写实验例程烧录到紫藤派开发板中,并在卡槽中插入SD卡,在液晶屏上,将会观察到SD卡相关操作结果。

本教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462


使用特权

评论回复
沙发
tpgf| | 2024-6-4 15:19 | 只看该作者
为什么sd卡的识别经常表现的不稳定呢

使用特权

评论回复
板凳
八层楼| | 2024-6-4 16:00 | 只看该作者
sd卡的寿命收到哪些因素的限制影响呢

使用特权

评论回复
地板
观海| | 2024-6-4 19:37 | 只看该作者
从sd卡中读取数据的速度会不会比较的慢呢

使用特权

评论回复
5
晓伍| | 2024-6-4 20:09 | 只看该作者
可以从软件上对sd卡的读写进行一定的限制保护吗

使用特权

评论回复
6
磨砂| | 2024-6-4 20:41 | 只看该作者
在对sd卡进行读写的过程中 是不是不能响应其他的中断啊

使用特权

评论回复
7
木木guainv| | 2024-6-4 21:13 | 只看该作者
sd卡一次能写入多少数据呀

使用特权

评论回复
8
uytyu| | 2024-6-5 21:59 | 只看该作者
SD卡的CD(卡检测)、CMD(命令)、D0-D3(数据线)和CLK(时钟)需要连接到相应的GD32F470的SDIO引脚上。

使用特权

评论回复
9
wengh2016| | 2024-6-6 08:28 | 只看该作者
电源地信号线具有10uf以及100nf电容, 滤除噪声。

使用特权

评论回复
10
bestwell| | 2024-6-7 08:09 | 只看该作者
所有信号线通过10K电阻进行上拉,以稳定信号。

使用特权

评论回复
11
hearstnorman323| | 2024-6-7 11:15 | 只看该作者
注意SD卡与微控制器之间的兼容性问题,以及时序匹配问题。部分高速SD卡可能需要更严格的时序配置。

使用特权

评论回复
12
hilahope| | 2024-6-7 14:21 | 只看该作者
学习如何通过SDIO接口实现SD卡的读写操作。

使用特权

评论回复
13
robertesth| | 2024-6-7 17:30 | 只看该作者
在初始化SD卡之前,先初始化SDIO接口。

使用特权

评论回复
14
sanfuzi| | 2024-6-7 20:39 | 只看该作者
过高可能导致通信错误,过低则影响数据传输速度。

使用特权

评论回复
15
belindagraham| | 2024-6-8 09:53 | 只看该作者
在长时间读写操作期间,注意系统功耗,适时进行功耗管理和散热,特别是在高频率数据交换时。

使用特权

评论回复
16
deliahouse887| | 2024-6-8 13:12 | 只看该作者
了解SDIO接口的基本工作原理和操作方式。

使用特权

评论回复
17
eefas| | 2024-6-8 16:20 | 只看该作者
在进行读写操作时,需要遵循SD卡的命令和数据传输协议,包括发送命令、等待响应、数据块的读写等。

使用特权

评论回复
18
mmbs| | 2024-6-8 19:28 | 只看该作者
充分理解和参考GD32F470紫藤派的用户手册、数据手册和SD卡的规格说明书

使用特权

评论回复
19
linfelix| | 2024-6-8 22:36 | 只看该作者
SD卡可能需要独立的电源供应,注意检查电源是否稳定,以及是否满足SD卡的工作电压要求。

使用特权

评论回复
20
alvpeg| | 2024-6-9 09:48 | 只看该作者
为提高数据传输效率,可以考虑使用DMA(直接存储器访问)模式进**的大批量数据读写。正确配置DMA通道和传输模式,避免DMA冲突和数据覆盖。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

170

主题

190

帖子

10

粉丝