1079|5

441

帖子

3

TA的资源

纯净的硅(高级)

楼主
 

【兆易GD32H759I-EVAL】 FatFs测试 [复制链接]

  本帖最后由 TL-LED 于 2024-6-6 09:16 编辑

移植FatFs文件系统到开发板,在SD卡上创建文本文件,并写入信息。

 

一、FatFs源码下载

 

       FatFs 是适用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。FatFs 模块是按照 ANSI C (C89) 编写的,完全与磁盘 I/O 层分离。因此它独立于平台。它可以集成到资源有限的小型微控制器中,例如 8051、PIC、AVR、ARM、Z80、RX 等。此外,这里还提供适用于微型微控制器的 Petit FatFs 模块。

 

       官网下载地址:http://elm-chan.org/fsw/ff/00index_e.html

 

 

二、添加代码

 

2.1、添加源码到工程文件

 

 

2.2、添加源码路径

 

 

2.3、diskio.c

移植FatFs主要是diskio.c下面这个几个函数

disk_status();

disk_initialize();

disk_read();

disk_write();

disk_ioctl();

diskio.c

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "sdcard/sdcard.h"
#include "sdcard/sd.h"


/* Definitions of physical drive number for each drive */
#define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */

/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/



DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat;
	int result;

	switch (pdrv) {
	case DEV_RAM :
		return STA_NODISK;

	case DEV_MMC :
		return RES_OK;

	case DEV_USB :
		return STA_NOINIT;
	}
	return STA_NOINIT;
}

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = STA_NOINIT;;
	int result;

	switch (pdrv) {
	case DEV_RAM :
		break;

	case DEV_MMC :
		result = init_sd();
		if(result !=0 )
		{
			stat =  0;
		}
		else
		{
			stat = STA_NOINIT;
		}
		break;

	case DEV_USB :
		break;
	}
	return stat;
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA */
	UINT count		/* Number of sectors to read */
)
{
	DRESULT res = RES_PARERR;;
	int result;
	sd_error_enum SD_stat = SD_OK;
	
	if(!count){
			return RES_PARERR;
	}

	switch (pdrv) {
	case DEV_RAM :
		break;

	case DEV_MMC :
		
		result = sd_readdisk((uint8_t*)buff, sector, count);
        
		while (result != SD_OK)    
		{
				//printf("sd rd error:%d\r\n", result);
				init_sd(); 
				result = sd_readdisk((uint8_t*)buff, sector, count);
		}
						
		if (result == 0x00 || result == SD_OK)
		{
				res = RES_OK;
		}
		else
		{
				res = RES_ERROR; 
		}
		
		break;

	case DEV_USB :
		break;
	}

	return res;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if FF_FS_READONLY == 0

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
	DRESULT res = RES_PARERR;
	int result;
	
	if(!count){
			return RES_PARERR;
	}

	switch (pdrv) {
	case DEV_RAM :
		break;

	case DEV_MMC :
		result = sd_writedisk((uint8_t *)buff, sector, count);
        
		while (result != SD_OK)      
		{
				//printf("sd wr error:%d\r\n", result);
				init_sd(); 
				result = sd_writedisk((uint8_t *)buff, sector, count);
		}				
		if (result == 0x00 || result == SD_OK)
		{
				res = RES_OK;
		}
		else
		{
				res = RES_ERROR; 
		}
		break;

	case DEV_USB :
		break;
	}

	return res;
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res = RES_PARERR;;
	int result;

	switch (pdrv) {
	case DEV_RAM :
		return res;

	case DEV_MMC :

		switch(cmd) 
		{

		case GET_SECTOR_COUNT:   
				*(DWORD *)buff = sd_card_capacity_get() /512;
				res = RES_OK;
				break;

		case GET_SECTOR_SIZE:
				*(WORD *)buff = 512;
				res = RES_OK;
				break;

		case GET_BLOCK_SIZE:
				*(DWORD *)buff = 512;
        res = RES_OK;
				break;
		}
		res = RES_OK;
		return res;

	case DEV_USB :

		return res;
	}

	return RES_PARERR;
}

DWORD get_fattime (void)
{
    return 0;
}

 

2.4、ffconf.h

FatFs配置在此文件下修改

/*---------------------------------------------------------------------------/
/  Configurations of FatFs Module
/---------------------------------------------------------------------------*/

#define FFCONF_DEF	80286	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_READONLY	0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/  Read-only configuration removes writing API functions, f_write(), f_sync(),
/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/  and optional writing functions as well. */


#define FF_FS_MINIMIZE	0
/* This option defines minimization level to remove some basic API functions.
/
/   0: Basic functions are fully enabled.
/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/      are removed.
/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/   3: f_lseek() function is removed in addition to 2. */


#define FF_USE_FIND		0
/* This option switches filtered directory read functions, f_findfirst() and
/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */


#define FF_USE_MKFS		1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */


#define FF_USE_FASTSEEK	0
/* This option switches fast seek function. (0:Disable or 1:Enable) */


#define FF_USE_EXPAND	0
/* This option switches f_expand function. (0:Disable or 1:Enable) */


#define FF_USE_CHMOD	0
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */


#define FF_USE_LABEL	0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/  (0:Disable or 1:Enable) */


#define FF_USE_FORWARD	0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */


#define FF_USE_STRFUNC	0
#define FF_PRINT_LLI	1
#define FF_PRINT_FLOAT	1
#define FF_STRF_ENCODE	3
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/  f_printf().
/
/   0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/   1: Enable without LF-CRLF conversion.
/   2: Enable with LF-CRLF conversion.
/
/  FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/  makes f_printf() support floating point argument. These features want C99 or later.
/  When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/  encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/  to be read/written via those functions.
/
/   0: ANSI/OEM in current CP
/   1: Unicode in UTF-16LE
/   2: Unicode in UTF-16BE
/   3: Unicode in UTF-8
*/


/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define FF_CODE_PAGE	936
/* This option specifies the OEM code page to be used on the target system.
/  Incorrect code page setting can cause a file open failure.
/
/   437 - U.S.
/   720 - Arabic
/   737 - Greek
/   771 - KBL
/   775 - Baltic
/   850 - Latin 1
/   852 - Latin 2
/   855 - Cyrillic
/   857 - Turkish
/   860 - Portuguese
/   861 - Icelandic
/   862 - Hebrew
/   863 - Canadian French
/   864 - Arabic
/   865 - Nordic
/   866 - Russian
/   869 - Greek 2
/   932 - Japanese (DBCS)
/   936 - Simplified Chinese (DBCS)
/   949 - Korean (DBCS)
/   950 - Traditional Chinese (DBCS)
/     0 - Include all code pages above and configured by f_setcp()
*/


#define FF_USE_LFN		3
#define FF_MAX_LFN		255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */


#define FF_LFN_UNICODE	0
/* This option switches the character encoding on the API when LFN is enabled.
/
/   0: ANSI/OEM in current CP (TCHAR = char)
/   1: Unicode in UTF-16 (TCHAR = WCHAR)
/   2: Unicode in UTF-8 (TCHAR = char)
/   3: Unicode in UTF-32 (TCHAR = DWORD)
/
/  Also behavior of string I/O functions will be affected by this option.
/  When LFN is not enabled, this option has no effect. */


#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
/* This set of options defines size of file name members in the FILINFO structure
/  which is used to read out directory items. These values should be suffcient for
/  the file names to read. The maximum possible length of the read file name depends
/  on character encoding. When LFN is not enabled, these options have no effect. */


#define FF_FS_RPATH		0
/* This option configures support for relative path.
/
/   0: Disable relative path and remove related functions.
/   1: Enable relative path. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
*/


/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/

#define FF_VOLUMES		3
/* Number of volumes (logical drives) to be used. (1-10) */


#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/  logical drives. Number of items must not be less than FF_VOLUMES. Valid
/  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/  not defined, a user defined volume string table is needed as:
/
/  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/


#define FF_MULTI_PARTITION	0
/* This option switches support for multiple volumes on the physical drive.
/  By default (0), each logical drive number is bound to the same physical drive
/  number and only an FAT volume found on the physical drive will be mounted.
/  When this function is enabled (1), each logical drive number can be bound to
/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/  function will be available. */


#define FF_MIN_SS		512
#define FF_MAX_SS		512
/* This set of options configures the range of sector size to be supported. (512,
/  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/  harddisk, but a larger value may be required for on-board flash memory and some
/  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/  for variable sector size mode and disk_ioctl() function needs to implement
/  GET_SECTOR_SIZE command. */


#define FF_LBA64		0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */


#define FF_MIN_GPT		0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
/  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */


#define FF_USE_TRIM		0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/  To enable Trim function, also CTRL_TRIM command should be implemented to the
/  disk_ioctl() function. */



/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_TINY		0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/  Instead of private sector buffer eliminated from the file object, common sector
/  buffer in the filesystem object (FATFS) is used for the file data transfer. */


#define FF_FS_EXFAT		0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/  Note that enabling exFAT discards ANSI C (C89) compatibility. */


#define FF_FS_NORTC		0
#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2022
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/  an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/  timestamp feature. Every object modified by FatFs will have a fixed timestamp
/  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/  added to the project to read current time form real-time clock. FF_NORTC_MON,
/  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */


#define FF_FS_NOFSINFO	0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/  option, and f_getfree() function at the first time after volume mount will force
/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/  bit0=0: Use free cluster count in the FSINFO if available.
/  bit0=1: Do not trust free cluster count in the FSINFO.
/  bit1=0: Use last allocated cluster number in the FSINFO if available.
/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/


#define FF_FS_LOCK		0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/  is 1.
/
/  0:  Disable file lock function. To avoid volume corruption, application program
/      should avoid illegal open, remove and rename to the open objects.
/  >0: Enable file lock function. The value defines how many files/sub-directories
/      can be opened simultaneously under file lock control. Note that the file
/      lock control is independent of re-entrancy. */


#define FF_FS_REENTRANT	0
#define FF_FS_TIMEOUT	1000
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/  module itself. Note that regardless of this option, file access to different
/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/  and f_fdisk() function, are always not re-entrant. Only file/directory access
/  to the same volume is under control of this featuer.
/
/   0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/   1: Enable re-entrancy. Also user provided synchronization handlers,
/      ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
/      function, must be added to the project. Samples are available in ffsystem.c.
/
/  The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*/



/*--- End of configuration options ---*/

 

 

2.5、sdcard.c

这是官网SD卡驱动程序

/*!
    \file    sdcard.c
    \brief   SD card driver 
    
    \version 2024-01-05, V1.2.0, demo for GD32H7xx
*/

/*
    Copyright (c) 2024, GigaDevice Semiconductor Inc.

    Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice,
       this list of conditions and the following disclaimer in the documentation
       and/or other materials provided with the distribution.
    3. Neither the name of the copyright holder nor the names of its contributors
       may be used to endorse or promote products derived from this software without
       specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

#include "sdcard.h"
#include "gd32h7xx_sdio.h"
#include <stddef.h>


/* card status of R1 definitions */
#define SD_R1_OUT_OF_RANGE                  BIT(31)                   /* command's argument was out of the allowed range */
#define SD_R1_ADDRESS_ERROR                 BIT(30)                   /* misaligned address which did not match the block length */
#define SD_R1_BLOCK_LEN_ERROR               BIT(29)                   /* transferred block length is not allowed */
#define SD_R1_ERASE_SEQ_ERROR               BIT(28)                   /* an error in the sequence of erase commands occurred */
#define SD_R1_ERASE_PARAM                   BIT(27)                   /* an invalid selection of write-blocks for erase occurred */
#define SD_R1_WP_VIOLATION                  BIT(26)                   /* the host attempts to write to a protected block or to the temporary or permanent write protected card */
#define SD_R1_CARD_IS_LOCKED                BIT(25)                   /* the card is locked by the host */
#define SD_R1_LOCK_UNLOCK_FAILED            BIT(24)                   /* a sequence or password error has been detected in lock/unlock card command */
#define SD_R1_COM_CRC_ERROR                 BIT(23)                   /* CRC check of the previous command failed */
#define SD_R1_ILLEGAL_COMMAND               BIT(22)                   /* command not legal for the card state */
#define SD_R1_CARD_ECC_FAILED               BIT(21)                   /* card internal ECC was applied but failed to correct the data */
#define SD_R1_CC_ERROR                      BIT(20)                   /* internal card controller error */
#define SD_R1_GENERAL_UNKNOWN_ERROR         BIT(19)                   /* a general or an unknown error occurred during the operation */
#define SD_R1_CSD_OVERWRITE                 BIT(16)                   /* read only section of the CSD does not match or attempt to reverse the copy or permanent WP bits */
#define SD_R1_WP_ERASE_SKIP                 BIT(15)                   /* partial address space was erased */
#define SD_R1_CARD_ECC_DISABLED             BIT(14)                   /* command has been executed without using the internal ECC */
#define SD_R1_ERASE_RESET                   BIT(13)                   /* an erase sequence was cleared before executing */
#define SD_R1_READY_FOR_DATA                BIT(8)                    /* correspond to buffer empty signaling on the bus */
#define SD_R1_APP_CMD                       BIT(5)                    /* card will expect ACMD */
#define SD_R1_AKE_SEQ_ERROR                 BIT(3)                    /* error in the sequence of the authentication process */
#define SD_R1_ERROR_BITS                    ((uint32_t)0xFDF9E008U)   /* all the R1 error bits */

/* card status of R6 definitions */
#define SD_R6_COM_CRC_ERROR                 BIT(15)                   /* CRC check of the previous command failed */
#define SD_R6_ILLEGAL_COMMAND               BIT(14)                   /* command not legal for the card state */
#define SD_R6_GENERAL_UNKNOWN_ERROR         BIT(13)                   /* a general or an unknown error occurred during the operation */

/* card state */
#define SD_CARDSTATE_IDLE                   ((uint8_t)0x00)           /* card is in idle state */
#define SD_CARDSTATE_READY                  ((uint8_t)0x01)           /* card is in ready state */
#define SD_CARDSTATE_IDENTIFICAT            ((uint8_t)0x02)           /* card is in identificat state */
#define SD_CARDSTATE_STANDBY                ((uint8_t)0x03)           /* card is in standby state */
#define SD_CARDSTATE_TRANSFER               ((uint8_t)0x04)           /* card is in transfer state */
#define SD_CARDSTATE_DATA                   ((uint8_t)0x05)           /* card is in data sending state */
#define SD_CARDSTATE_RECEIVING              ((uint8_t)0x06)           /* card is in receiving state */
#define SD_CARDSTATE_PROGRAMMING            ((uint8_t)0x07)           /* card is in programming state */
#define SD_CARDSTATE_DISCONNECT             ((uint8_t)0x08)           /* card is in disconnect state */
#define SD_CARDSTATE_LOCKED                 ((uint32_t)0x02000000U)   /* card is in locked state */

#define SD_CHECK_PATTERN                    ((uint32_t)0x000001AAU)    /* check pattern for CMD8 */
#define SD_VOLTAGE_WINDOW                   ((uint32_t)0x80100000U)    /* host 3.3V request in ACMD41 */
#define SD_VOLTAGE_18V                      ((uint32_t)0x01000000U)    /* host 1.8V request in ACMD41 */

/* parameters for ACMD41(voltage validation) */
#define SD_HIGH_CAPACITY                    ((uint32_t)0x40000000U)    /* high capacity SD memory card */
#define SD_STD_CAPACITY                     ((uint32_t)0x00000000U)    /* standard capacity SD memory card */

/* SD bus width, check SCR register */
#define SD_BUS_WIDTH_4BIT                   ((uint32_t)0x00040000U)    /* 4-bit width bus mode */
#define SD_BUS_WIDTH_1BIT                   ((uint32_t)0x00010000U)    /* 1-bit width bus mode */

/* masks for SCR register */
#define SD_MASK_0_7BITS                     ((uint32_t)0x000000FFU)    /* mask [7:0] bits */
#define SD_MASK_8_15BITS                    ((uint32_t)0x0000FF00U)    /* mask [15:8] bits */
#define SD_MASK_16_23BITS                   ((uint32_t)0x00FF0000U)    /* mask [23:16] bits */
#define SD_MASK_24_31BITS                   ((uint32_t)0xFF000000U)    /* mask [31:24] bits */

#define SDIO_FIFO_ADDR                      SDIO_FIFO(SDIO)            /* address of SDIO_FIFO */
#define SD_FIFOHALF_WORDS                   ((uint32_t)0x00000008U)    /* words of FIFO half full/empty */
#define SD_FIFOHALF_BYTES                   ((uint32_t)0x00000020U)    /* bytes of FIFO half full/empty */

#define SD_DATATIMEOUT                      ((uint32_t)0xFFFF0000U)    /* DSM data timeout */
#define SD_MAX_VOLT_VALIDATION              ((uint32_t)0x0000FFFFU)    /* the maximum times of voltage validation */
#define SD_MAX_DATA_LENGTH                  ((uint32_t)0x01FFFFFFU)    /* the maximum length of data */
#define SD_ALLZERO                          ((uint32_t)0x00000000U)    /* all zero */
#define SD_RCA_SHIFT                        ((uint8_t)0x10U)           /* RCA shift bits */

/* user can according to need to change the macro values */
#define SD_CLK_DIV_INIT                     ((uint32_t)0x01F4)        /* SD clock division in initilization phase */
#define SD_CLK_DIV_TRANS_DSPEED             ((uint32_t)0x0008)        /* SD clock division in default speed transmission phase */
#define SD_CLK_DIV_TRANS_HSPEED             ((uint32_t)0x0004)        /* SD clock division in high speed transmission phase */
#define SD_CLK_DIV_TRANS_SDR25SPEED         ((uint32_t)0x0004)        /* SD clock division in SDR25 high speed transmission phase */
#define SD_CLK_DIV_TRANS_SDR50SPEED         ((uint32_t)0x0002)        /* SD clock division in SDR50 high speed transmission phase */
#define SD_CLK_DIV_TRANS_SDR104SPEED        ((uint32_t)0x0001)        /* SD clock division in SDR104 high speed transmission phase */
#define SD_CLK_DIV_TRANS_DDR50SPEED         ((uint32_t)0x0004)        /* SD clock division in DDR50 high speed transmission phase */

#define SDIO_MASK_INTC_FLAGS                ((uint32_t)0x1FE00FFF)    /* mask flags of SDIO_INTC */
#define SDIO_MASK_CMD_FLAGS                 ((uint32_t)0x002000C5)    /* mask flags of CMD FLAGS */
#define SDIO_MASK_DATA_FLAGS                ((uint32_t)0x18000F3A)    /* mask flags of DATA FLAGS */

uint32_t sd_scr[2] = {0, 0};                                          /* content of SCR register */

static sdio_card_type_enum cardtype = SDIO_STD_CAPACITY_SD_CARD_V1_1; /* SD card type */
static uint32_t sdcardtype = SD_STD_CAPACITY;                         /* SD card capacity level, for ACMD41 parameter */
uint32_t cardcapacity = SD_SDSC;                                      /* SD card capacity type */

static uint32_t sd_csd[4] = {0, 0, 0, 0};                             /* content of CSD register */
static uint32_t sd_cid[4] = {0, 0, 0, 0};                             /* content of CID register */
static uint16_t sd_rca = 0U;                                          /* RCA of SD card */
static uint32_t transmode = SD_POLLING_MODE;
static uint32_t totalnumber_bytes = 0U, stopcondition = 0U;

static __IO sd_error_enum transerror = SD_OK;
static __IO uint32_t transend = 0U, number_bytes = 0U;

/* check if the command sent error occurs */
static sd_error_enum cmdsent_error_check(void);
/* check if error occurs for R1 response */
static sd_error_enum r1_error_check(uint8_t cmdindex);
/* check if error type for R1 response */
static sd_error_enum r1_error_type_check(uint32_t resp);
/* check if error occurs for R2 response */
static sd_error_enum r2_error_check(void);
/* check if error occurs for R3 response */
static sd_error_enum r3_error_check(void);
/* check if error occurs for R6 response */
static sd_error_enum r6_error_check(uint8_t cmdindex, uint16_t *prca);
/* check if error occurs for R7 response */
static sd_error_enum r7_error_check(void);

/* get the state which the card is in */
static sd_error_enum sd_card_state_get(uint8_t *pcardstate);
/* configure the bus width mode */
static sd_error_enum sd_bus_width_config(uint32_t buswidth);
/* get the SCR of corresponding card */
static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr);
/* get the data block size */
static uint32_t sd_datablocksize_get(uint16_t bytesnumber);

/* configure the GPIO of SDIO interface */
static void gpio_config(void);
/* configure the RCU of SDIO */
static void rcu_config(void);
/* configure the DMA for SDIO request */
static void dma_config(uint32_t *srcbuf, uint32_t bufsize);

/*!
    \brief      initialize the SD card and make it in standby state
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_init(void)
{
    sd_error_enum status = SD_OK;
    /* configure the RCU and GPIO, deinitialize the SDIO */
    rcu_config();
    gpio_config();
    sdio_deinit(SDIO);

    /* configure the clock and work voltage */
    status = sd_power_on();
    if(SD_OK != status) {
        return status;
    }

    /* initialize the card and get CID and CSD of the card */
    status = sd_card_init();
    if(SD_OK != status) {
        return status;
    }

    /* configure the SDIO peripheral */
    sdio_clock_config(SDIO, SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_INIT);
    sdio_bus_mode_set(SDIO, SDIO_BUSMODE_1BIT);
    sdio_hardware_clock_disable(SDIO);

    return status;
}

/*!
    \brief      initialize the card and get CID and CSD of the card
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_card_init(void)
{
    sd_error_enum status = SD_OK;
    uint16_t temp_rca = 0x01U;

    if(SDIO_POWER_OFF == sdio_power_state_get(SDIO)) {
        status = SD_OPERATION_IMPROPER;
        return status;
    }

    /* the card is not I/O only card */
    if(SDIO_SECURE_DIGITAL_IO_CARD != cardtype) {
        /* send CMD2(SD_CMD_ALL_SEND_CID) to get the CID numbers */
        sdio_command_response_config(SDIO, SD_CMD_ALL_SEND_CID, (uint32_t)0x0, SDIO_RESPONSETYPE_LONG);
        sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
        sdio_csm_enable(SDIO);
        /* check if some error occurs */
        status = r2_error_check();
        if(SD_OK != status) {
            return status;
        }

        /* store the CID numbers */
        sd_cid[0] = sdio_response_get(SDIO, SDIO_RESPONSE0);
        sd_cid[1] = sdio_response_get(SDIO, SDIO_RESPONSE1);
        sd_cid[2] = sdio_response_get(SDIO, SDIO_RESPONSE2);
        sd_cid[3] = sdio_response_get(SDIO, SDIO_RESPONSE3);
    }

    /* the card is SD memory card or the I/O card has the memory portion */
    if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V3_0 == cardtype) ||
            (SDIO_HIGH_CAPACITY_SD_CARD == cardtype) || (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == cardtype)) {
        /* send CMD3(SEND_RELATIVE_ADDR) to ask the card to publish a new relative address (RCA) */
        sdio_command_response_config(SDIO, SD_CMD_SEND_RELATIVE_ADDR, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
        sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
        sdio_csm_enable(SDIO);
        /* check if some error occurs */
        status = r6_error_check(SD_CMD_SEND_RELATIVE_ADDR, &temp_rca);
        if(SD_OK != status) {
            return status;
        }
    }

    if(SDIO_SECURE_DIGITAL_IO_CARD != cardtype) {
        /* the card is not I/O only card */
        sd_rca = temp_rca;

        /* send CMD9(SEND_CSD) to get the addressed card's card-specific data (CSD) */
        sdio_command_response_config(SDIO, (uint32_t)SD_CMD_SEND_CSD, ((uint32_t)temp_rca) << SD_RCA_SHIFT, SDIO_RESPONSETYPE_LONG);
        sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
        sdio_csm_enable(SDIO);
        /* check if some error occurs */
        status = r2_error_check();
        if(SD_OK != status) {
            return status;
        }

        /* store the card-specific data (CSD) */
        sd_csd[0] = sdio_response_get(SDIO, SDIO_RESPONSE0);
        sd_csd[1] = sdio_response_get(SDIO, SDIO_RESPONSE1);
        sd_csd[2] = sdio_response_get(SDIO, SDIO_RESPONSE2);
        sd_csd[3] = sdio_response_get(SDIO, SDIO_RESPONSE3);
    }
    return status;
}

/*!
    \brief      configure the clock and the work voltage, and get the card type
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_power_on(void)
{
    sd_error_enum status = SD_OK;
    uint32_t response = 0U, count = 0U;
    uint8_t busyflag = 0U;
    uint32_t timedelay = 0U;

    /* configure the SDIO peripheral */
    sdio_clock_config(SDIO, SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_INIT);
    sdio_bus_mode_set(SDIO, SDIO_BUSMODE_1BIT);
    sdio_hardware_clock_disable(SDIO);
    sdio_power_state_set(SDIO, SDIO_POWER_ON);

    /* time delay for power up*/
    timedelay = 500U;
    while(timedelay > 0U) {
        timedelay--;
    }

    /* send CMD0(GO_IDLE_STATE) to reset the card */
    sdio_command_response_config(SDIO, SD_CMD_GO_IDLE_STATE, (uint32_t)0x0, SDIO_RESPONSETYPE_NO);
    sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
    /* enable the CSM */
    sdio_csm_enable(SDIO);

    /* check if command sent error occurs */
    status = cmdsent_error_check();
    if(SD_OK != status) {
        return status;
    }

    /* send CMD8(SEND_IF_COND) to get SD memory card interface condition */
    sdio_command_response_config(SDIO, SD_CMD_SEND_IF_COND, SD_CHECK_PATTERN, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
    sdio_csm_enable(SDIO);

    if(SD_OK == r7_error_check()) {
        /* SD Card 2.0 */
        cardtype = SDIO_STD_CAPACITY_SD_CARD_V2_0;
        sdcardtype = SD_HIGH_CAPACITY;
    }

    /* send CMD55(APP_CMD) to indicate next command is application specific command */
    sdio_command_response_config(SDIO, SD_CMD_APP_CMD, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
    sdio_csm_enable(SDIO);

    if(SD_OK == r1_error_check(SD_CMD_APP_CMD)) {
        /* SD memory card */
        while((!busyflag) && (count < SD_MAX_VOLT_VALIDATION)) {
            /* send CMD55(APP_CMD) to indicate next command is application specific command */
            sdio_command_response_config(SDIO, SD_CMD_APP_CMD, (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_APP_CMD);
            if(SD_OK != status) {
                return status;
            }

            /* send ACMD41(SD_SEND_OP_COND) to get host capacity support information (HCS) and OCR content */
            sdio_command_response_config(SDIO, SD_APPCMD_SD_SEND_OP_COND, (SD_VOLTAGE_WINDOW | SD_VOLTAGE_18V | sdcardtype), SDIO_RESPONSETYPE_SHORT);
            sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
            sdio_csm_enable(SDIO);
            /* check if some error occurs */
            status = r3_error_check();
            if(SD_OK != status) {
                return status;
            }
            /* get the response and check card power up status bit(busy) */
            response = sdio_response_get(SDIO, SDIO_RESPONSE0);
            busyflag = (uint8_t)((response >> 31U) & (uint32_t)0x01U);
            ++count;
        }
        if(count >= SD_MAX_VOLT_VALIDATION) {
            status = SD_VOLTRANGE_INVALID;
            return status;
        }
        if(response & SD_HIGH_CAPACITY) {
            /* SDHC card */
            cardcapacity = SD_SDHC_SDXC;
            cardtype = SDIO_HIGH_CAPACITY_SD_CARD;
        }
    }
    return status;
}

/*!
    \brief      close the power of SDIO
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_power_off(void)
{
    sd_error_enum status = SD_OK;
    sdio_power_state_set(SDIO, SDIO_POWER_OFF);
    return status;
}

/*!
    \brief      configure the bus mode
    \param[in]  busmode: the bus mode
      \arg        SDIO_BUSMODE_1BIT: 1-bit SDIO card bus mode
      \arg        SDIO_BUSMODE_4BIT: 4-bit SDIO card bus mode
      \arg        SDIO_BUSMODE_8BIT: 8-bit SDIO card bus mode (MMC only)
    \param[in]  speed: the bus speed mode
      \arg        SD_SPEED_DEFAULT: default bus speed
      \arg        SD_SPEED_HIGH: high bus speed
      \arg        SD_SPEED_SDR25: SDR25 bus speed
      \arg        SD_SPEED_SDR50: SDR50 bus speed
      \arg        SD_SPEED_SDR104: SDR104 bus speed
      \arg        SD_SPEED_DDR50: DDR50 bus speed
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_bus_mode_config(uint32_t busmode, uint32_t speed)
{
    sd_error_enum status = SD_OK;
    uint32_t count, clk_div;

    if(SDIO_MULTIMEDIA_CARD == cardtype) {
        /* MMC card doesn't support this function */
        status = SD_FUNCTION_UNSUPPORTED;
        return status;
    } else if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype) ||
              (SDIO_HIGH_CAPACITY_SD_CARD == cardtype)) {
        if(SDIO_BUSMODE_8BIT == busmode) {
            /* 8 bit bus mode doesn't support */
            status = SD_FUNCTION_UNSUPPORTED;
            return status;
        } else if(SDIO_BUSMODE_4BIT == busmode) {
            /* configure SD bus width and the SDIO */
            status = sd_bus_width_config(SD_BUS_WIDTH_4BIT);
            if(SD_OK == status) {
                sdio_bus_mode_set(SDIO, busmode);
            }
        } else if(SDIO_BUSMODE_1BIT == busmode) {
            /* configure SD bus width and the SDIO */
            status = sd_bus_width_config(SD_BUS_WIDTH_1BIT);
            if(SD_OK == status) {
                sdio_bus_mode_set(SDIO, busmode);
            }
        } else {
            status = SD_PARAMETER_INVALID;
        }
    }

    if((speed != SD_SPEED_DEFAULT) && (speed != SD_SPEED_HIGH)) {
        /* switch UHS-I speed mode */
        switch(speed) {
        case SD_SPEED_SDR25:
            clk_div = SD_CLK_DIV_TRANS_SDR25SPEED;
            break;
        case SD_SPEED_SDR50:
            clk_div = SD_CLK_DIV_TRANS_SDR50SPEED;
            break;
        case SD_SPEED_SDR104:
            clk_div = SD_CLK_DIV_TRANS_SDR104SPEED;
            break;
        case SD_SPEED_DDR50:
            clk_div = SD_CLK_DIV_TRANS_DDR50SPEED;
            break;
        default:
            clk_div = SD_CLK_DIV_TRANS_DSPEED;
            break;
        }

        /* send CMD16(SET_BLOCKLEN) to set the block length */
        sdio_command_response_config(SDIO, SD_CMD_SET_BLOCKLEN, (uint32_t)64, 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;
        }

        sdio_data_config(SDIO, SD_DATATIMEOUT, 64, SDIO_DATABLOCKSIZE_64BYTES);
        sdio_data_transfer_config(SDIO, SDIO_TRANSDIRECTION_TOSDIO, SDIO_TRANSMODE_BLOCKCOUNT);
        sdio_dsm_enable(SDIO);

        /* SDR25 0x80FFFF01U  SDR50 0x80FF1F02U  SDR104 0x80FF1F03U  DDR50 0x80FF1F04U */
        sdio_command_response_config(SDIO, SD_APPCMD_SET_BUS_WIDTH, speed, 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_APPCMD_SET_BUS_WIDTH);
        if(SD_OK != status) {
            return status;
        }

        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 = 0; count < SD_FIFOHALF_WORDS; count++) {
                    sdio_data_read(SDIO);
                }
            }
        }
        /* clear the SDIO_INTC flags */
        sdio_flag_clear(SDIO, SDIO_MASK_INTC_FLAGS);
        /* change the clock to UHS-I speed , user according to the speed configuration */
        sdio_clock_config(SDIO, SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKPWRSAVE_DISABLE, clk_div);
        if(speed == SD_SPEED_DDR50) {
            /* set SDIO data rate */
            sdio_data_rate_set(SDIO, SDIO_DATA_RATE_DDR);
        }
        sdio_hardware_clock_enable(SDIO);
    } else if(speed == SD_SPEED_HIGH) {
        /* change the clock to high speed , user according to the speed configuration */
        sdio_clock_config(SDIO, SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_TRANS_HSPEED);
        sdio_hardware_clock_enable(SDIO);
    } else if(speed == SD_SPEED_DEFAULT) {
        /* change the clock to default speed , user according to the speed configuration */
        sdio_clock_config(SDIO, SDIO_SDIOCLKEDGE_RISING, SDIO_CLOCKPWRSAVE_DISABLE, SD_CLK_DIV_TRANS_DSPEED);
        sdio_hardware_clock_enable(SDIO);
    }
    return status;
}

/*!
    \brief      switch 1.8V power level of SD card
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_card_voltage_switch(void)
{
    sd_error_enum status = SD_OK;
    /* send CMD11(SD_CMD_VOLATAGE_SWITCH) switch to 1.8V bus signaling level */
    sdio_command_response_config(SDIO, SD_CMD_VOLATAGE_SWITCH, (uint32_t)0x0, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
    sdio_csm_enable(SDIO);

    status = r1_error_check(SD_CMD_VOLATAGE_SWITCH);
    return status;
}

/*!
    \brief      configure the mode of transmission
    \param[in]  txmode: transfer mode
      \arg        SD_DMA_MODE: DMA mode
      \arg        SD_POLLING_MODE: polling mode
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_transfer_mode_config(uint32_t txmode)
{
    sd_error_enum status = SD_OK;
    /* set the transfer mode */
    if((SD_DMA_MODE == txmode) || (SD_POLLING_MODE == txmode)) {
        transmode = txmode;
    } else {
        status = SD_PARAMETER_INVALID;
    }
    return status;
}

/*!
    \brief      read a block data into a buffer from the specified address of a card
    \param[out] preadbuffer: a pointer that store a block read data
    \param[in]  readaddr: the read data address
    \param[in]  blocksize: the data block size
    \retval     sd_error_enum
*/
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;
}

/*!
    \brief      read multiple blocks data into a buffer from the specified address of a card
    \param[out] preadbuffer: a pointer that store multiple blocks read data
    \param[in]  readaddr: the read data address
    \param[in]  blocksize: the data block size
    \param[in]  blocksnumber: number of blocks that will be read
    \retval     sd_error_enum
*/
sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t blocksize, uint32_t blocksnumber)
{
    /* 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;
    }

    if(blocksnumber > 1U) {
        if(blocksnumber * blocksize > SD_MAX_DATA_LENGTH) {
            /* exceeds the maximum length */
            status = SD_PARAMETER_INVALID;
            return status;
        }

        stopcondition = 1U;
        totalnumber_bytes = blocksnumber * 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 CMD18(READ_MULTIPLE_BLOCK) to read multiple blocks */
            sdio_command_response_config(SDIO, SD_CMD_READ_MULTIPLE_BLOCK, 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_MULTIPLE_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;
                }
            }

            /* 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;
            }

            sdio_trans_start_disable(SDIO);

            if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTEND)) {
                if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype) ||
                        (SDIO_HIGH_CAPACITY_SD_CARD == cardtype)) {
                    /* send CMD12(STOP_TRANSMISSION) to stop transmission */
                    sdio_command_response_config(SDIO, SD_CMD_STOP_TRANSMISSION, (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_STOP_TRANSMISSION);
                    if(SD_OK != status) {
                        return status;
                    }
                }
            }
            /* 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 CMD18(READ_MULTIPLE_BLOCK) to read multiple blocks */
            sdio_command_response_config(SDIO, SD_CMD_READ_MULTIPLE_BLOCK, 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_MULTIPLE_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;
}

/*!
    \brief      write a block data to the specified address of a card
    \param[in]  pwritebuffer: a pointer that store a block data to be transferred
    \param[in]  writeaddr: the read data address
    \param[in]  blocksize: the data block size
    \param[out] none
    \retval     sd_error_enum
*/
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;
}

/*!
    \brief      write multiple blocks data to the specified address of a card
    \param[in]  pwritebuffer: a pointer that store multiple blocks data to be transferred
    \param[in]  writeaddr: the read data address
    \param[in]  blocksize: the data block size
    \param[in]  blocksnumber: number of blocks that will be written
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_t blocksize, uint32_t blocksnumber)
{
    /* 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;
    }

    if(blocksnumber > 1U) {
        if(blocksnumber * blocksize > SD_MAX_DATA_LENGTH) {
            status = SD_PARAMETER_INVALID;
            return status;
        }

        if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype) ||
                (SDIO_HIGH_CAPACITY_SD_CARD == cardtype)) {
            /* send CMD55(APP_CMD) to indicate next command is application specific command */
            sdio_command_response_config(SDIO, SD_CMD_APP_CMD, (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_APP_CMD);
            if(SD_OK != status) {
                return status;
            }

            /* send ACMD23(SET_WR_BLK_ERASE_COUNT) to set the number of write blocks to be preerased before writing */
            sdio_command_response_config(SDIO, SD_APPCMD_SET_WR_BLK_ERASE_COUNT, blocksnumber, 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_APPCMD_SET_WR_BLK_ERASE_COUNT);
            if(SD_OK != status) {
                return status;
            }
        }

        stopcondition = 1U;
        totalnumber_bytes = blocksnumber * blocksize;

        if(SD_POLLING_MODE == transmode) {
            /* 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);

            /* send CMD25(WRITE_MULTIPLE_BLOCK) to continuously write blocks of data */
            sdio_command_response_config(SDIO, SD_CMD_WRITE_MULTIPLE_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_MULTIPLE_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 */
            }

            if(RESET != sdio_flag_get(SDIO, SDIO_FLAG_DTEND)) {
                if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype) ||
                        (SDIO_HIGH_CAPACITY_SD_CARD == cardtype)) {
                    /* send CMD12(STOP_TRANSMISSION) to stop transmission */
                    sdio_command_response_config(SDIO, SD_CMD_STOP_TRANSMISSION, (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_STOP_TRANSMISSION);
                    if(SD_OK != status) {
                        return status;
                    }
                }
            }
        } 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);

            /* 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);

            /* send CMD25(WRITE_MULTIPLE_BLOCK) to continuously write blocks of data */
            sdio_command_response_config(SDIO, SD_CMD_WRITE_MULTIPLE_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_MULTIPLE_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;
}

/*!
    \brief      erase a continuous area of a card
    \param[in]  startaddr: the start address
    \param[in]  endaddr: the end address
    \param[out] none
    \retval     sd_error_enum
*/
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;
}



/*!
    \brief      process all the interrupts which the corresponding flags are set
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_interrupts_process(void)
{
    transerror = SD_OK;
    if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_DTEND)) {
        /* clear DTEND flag */
        sdio_interrupt_flag_clear(SDIO, SDIO_INT_FLAG_DTEND);
        /* disable idma for idma transfer */
        sdio_idma_disable(SDIO);

        /* disable all the interrupts */
        sdio_interrupt_disable(SDIO, SDIO_INT_DTCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_DTEND |
                               SDIO_INT_TFH | SDIO_INT_RFH | SDIO_INT_TXURE | SDIO_INT_RXORE);
        sdio_trans_start_disable(SDIO);
        /* send CMD12 to stop data transfer in multipule blocks operation */
        if(1 == stopcondition) {
            transerror = sd_transfer_stop();
        } else {
            transerror = SD_OK;
        }
        transend = 1;
        number_bytes = 0;
        /* clear data flags */
        sdio_flag_clear(SDIO, SDIO_MASK_DATA_FLAGS);
        return transerror;
    }

    if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_DTCRCERR | SDIO_INT_FLAG_DTTMOUT | SDIO_INT_FLAG_TXURE | SDIO_INT_FLAG_RXORE)) {
        /* set different errors */
        if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_DTCRCERR)) {
            transerror = SD_DATA_CRC_ERROR;
        }
        if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_DTTMOUT)) {
            transerror = SD_DATA_TIMEOUT;
        }
        if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_TXURE)) {
            transerror = SD_TX_UNDERRUN_ERROR;
        }
        if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_RXORE)) {
            transerror = SD_RX_OVERRUN_ERROR;
        }
        /* clear data flags */
        sdio_flag_clear(SDIO, SDIO_MASK_DATA_FLAGS);
        /* disable all the interrupts */
        sdio_interrupt_disable(SDIO, SDIO_INT_DTCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_DTEND |
                               SDIO_INT_TXURE | SDIO_INT_RXORE);

        sdio_trans_start_disable(SDIO);
        sdio_fifo_reset_enable(SDIO);
        sdio_fifo_reset_disable(SDIO);
        /* send CMD12 to stop data transfer in multipule blocks operation */
        transerror = sd_transfer_stop();
        sdio_flag_clear(SDIO, SDIO_FLAG_DTABORT);
        number_bytes = 0;

        if(transmode == SD_DMA_MODE) {
            sdio_idma_disable(SDIO);
        }
        return transerror;
    }

    if(RESET != sdio_interrupt_flag_get(SDIO, SDIO_INT_FLAG_IDMAERR)) {
        sdio_interrupt_flag_clear(SDIO, SDIO_INT_FLAG_IDMAERR);
        transerror = SD_DMA_ERROR;
        /* disable all the interrupts */
        sdio_interrupt_disable(SDIO, SDIO_INT_DTCRCERR | SDIO_INT_DTTMOUT | SDIO_INT_DTEND |
                               SDIO_INT_TFH | SDIO_INT_RFH | SDIO_INT_TXURE | SDIO_INT_RXORE);
        sdio_trans_start_disable(SDIO);
        sdio_fifo_reset_enable(SDIO);
        sdio_fifo_reset_disable(SDIO);
        /* send CMD12 to stop data transfer in multipule blocks operation */
        transerror = sd_transfer_stop();
        sdio_flag_clear(SDIO, SDIO_FLAG_DTABORT);
        number_bytes = 0;

        sdio_idma_disable(SDIO);
        return transerror;
    }
    return transerror;
}

/*!
    \brief      select or deselect a card
    \param[in]  cardrca: the RCA of a card
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_card_select_deselect(uint16_t cardrca)
{
    sd_error_enum status = SD_OK;
    /* send CMD7(SELECT/DESELECT_CARD) to select or deselect the card */
    sdio_command_response_config(SDIO, SD_CMD_SELECT_DESELECT_CARD, (uint32_t)cardrca << SD_RCA_SHIFT, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
    sdio_csm_enable(SDIO);

    status = r1_error_check(SD_CMD_SELECT_DESELECT_CARD);
    return status;
}

/*!
    \brief      get the card status whose response format R1 contains a 32-bit field
    \param[in]  none
    \param[out] pcardstatus: a pointer that store card status
    \retval     sd_error_enum
*/
sd_error_enum sd_cardstatus_get(uint32_t *pcardstatus)
{
    sd_error_enum status = SD_OK;
    if(NULL == pcardstatus) {
        status = SD_PARAMETER_INVALID;
        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;
    }

    *pcardstatus = sdio_response_get(SDIO, SDIO_RESPONSE0);
    return status;
}

/*!
    \brief      get SD card capacity
    \param[in]  none
    \param[out] none
    \retval     capacity of the card(KB)
*/
uint32_t sd_card_capacity_get(void)
{
    uint8_t tempbyte = 0U, devicesize_mult = 0U, readblklen = 0U;
    uint32_t capacity = 0U, devicesize = 0U;
    if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype)) {
        /* calculate the c_size(device size) */
        tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_8_15BITS) >> 8U);
        devicesize |= ((uint32_t)tempbyte & 0x03U) << 10U;
        tempbyte = (uint8_t)(sd_csd[1] & SD_MASK_0_7BITS);
        devicesize |= (uint32_t)((uint32_t)tempbyte << 2U);
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_24_31BITS) >> 24U);
        devicesize |= ((uint32_t)tempbyte & 0xC0U) >> 6U;

        /* calculate the c_size_mult(device size multiplier) */
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_16_23BITS) >> 16U);
        devicesize_mult = (tempbyte & 0x03U) << 1U;
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_8_15BITS) >> 8U);
        devicesize_mult |= (tempbyte & 0x80U) >> 7U;

        /* calculate the read_bl_len */
        tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_16_23BITS) >> 16U);
        readblklen = tempbyte & 0x0FU;

        /* capacity = BLOCKNR*BLOCK_LEN, BLOCKNR = (C_SIZE+1)*MULT, MULT = 2^(C_SIZE_MULT+2), BLOCK_LEN = 2^READ_BL_LEN */
        capacity = (devicesize + 1U) * (1U << (devicesize_mult + 2U));
        capacity *= (1U << readblklen);

        /* change the unit of capacity to KByte */
        capacity /= 1024U;
    } else if(SDIO_HIGH_CAPACITY_SD_CARD == cardtype) {
        /* calculate the c_size */
        tempbyte = (uint8_t)(sd_csd[1] & SD_MASK_0_7BITS);
        devicesize = ((uint32_t)tempbyte & 0x3FU) << 16U;
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_24_31BITS) >> 24U);
        devicesize |= (uint32_t)((uint32_t)tempbyte << 8U);
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_16_23BITS) >> 16U);
        devicesize |= (uint32_t)tempbyte;

        /* capacity = (c_size+1)*512KByte */
        capacity = (devicesize + 1U) * 512U;
    } else {
        /* if else end */
    }
    return capacity;
}

/*!
    \brief      get the detailed information of the SD card based on received CID and CSD
    \param[in]  none
    \param[out] pcardinfo: a pointer that store the detailed card information
    \retval     sd_error_enum
*/
sd_error_enum sd_card_information_get(sd_card_info_struct *pcardinfo)
{
    sd_error_enum status = SD_OK;
    uint8_t tempbyte = 0U;

    if(NULL == pcardinfo) {
        status = SD_PARAMETER_INVALID;
        return status;
    }

    /* store the card type and RCA */
    pcardinfo->card_type = cardtype;
    pcardinfo->card_rca = sd_rca;

    /* CID byte 0 */
    tempbyte = (uint8_t)((sd_cid[0] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_cid.mid = tempbyte;

    /* CID byte 1 */
    tempbyte = (uint8_t)((sd_cid[0] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_cid.oid = (uint16_t)((uint16_t)tempbyte << 8U);

    /* CID byte 2 */
    tempbyte = (uint8_t)((sd_cid[0] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_cid.oid |= (uint16_t)tempbyte;

    /* CID byte 3 */
    tempbyte = (uint8_t)(sd_cid[0] & SD_MASK_0_7BITS);
    pcardinfo->card_cid.pnm0 = (uint32_t)((uint32_t)tempbyte << 24U);

    /* CID byte 4 */
    tempbyte = (uint8_t)((sd_cid[1] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_cid.pnm0 |= (uint32_t)((uint32_t)tempbyte << 16U);

    /* CID byte 5 */
    tempbyte = (uint8_t)((sd_cid[1] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_cid.pnm0 |= (uint32_t)((uint32_t)tempbyte << 8U);

    /* CID byte 6 */
    tempbyte = (uint8_t)((sd_cid[1] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_cid.pnm0 |= (uint32_t)(tempbyte);

    /* CID byte 7 */
    tempbyte = (uint8_t)(sd_cid[1] & SD_MASK_0_7BITS);
    pcardinfo->card_cid.pnm1 = tempbyte;

    /* CID byte 8 */
    tempbyte = (uint8_t)((sd_cid[2] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_cid.prv = tempbyte;

    /* CID byte 9 */
    tempbyte = (uint8_t)((sd_cid[2] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_cid.psn = (uint32_t)((uint32_t)tempbyte << 24U);

    /* CID byte 10 */
    tempbyte = (uint8_t)((sd_cid[2] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_cid.psn |= (uint32_t)((uint32_t)tempbyte << 16U);

    /* CID byte 11 */
    tempbyte = (uint8_t)(sd_cid[2] & SD_MASK_0_7BITS);
    pcardinfo->card_cid.psn |= (uint32_t)tempbyte;

    /* CID byte 12 */
    tempbyte = (uint8_t)((sd_cid[3] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_cid.psn |= (uint32_t)tempbyte;

    /* CID byte 13 */
    tempbyte = (uint8_t)((sd_cid[3] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_cid.mdt = ((uint16_t)tempbyte & 0x0FU) << 8U;

    /* CID byte 14 */
    tempbyte = (uint8_t)((sd_cid[3] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_cid.mdt |= (uint16_t)tempbyte;

    /* CID byte 15 */
    tempbyte = (uint8_t)(sd_cid[3] & SD_MASK_0_7BITS);
    pcardinfo->card_cid.cid_crc = (tempbyte & 0xFEU) >> 1U;

    /* CSD byte 0 */
    tempbyte = (uint8_t)((sd_csd[0] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_csd.csd_struct = (tempbyte & 0xC0U) >> 6U;

    /* CSD byte 1 */
    tempbyte = (uint8_t)((sd_csd[0] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_csd.taac = tempbyte;

    /* CSD byte 2 */
    tempbyte = (uint8_t)((sd_csd[0] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_csd.nsac = tempbyte;

    /* CSD byte 3 */
    tempbyte = (uint8_t)(sd_csd[0] & SD_MASK_0_7BITS);
    pcardinfo->card_csd.tran_speed = tempbyte;

    /* CSD byte 4 */
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_csd.ccc = (uint16_t)((uint16_t)tempbyte << 4U);

    /* CSD byte 5 */
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_csd.ccc |= ((uint16_t)tempbyte & 0xF0U) >> 4U;
    pcardinfo->card_csd.read_bl_len = tempbyte & 0x0FU;

    /* CSD byte 6 */
    tempbyte = (uint8_t)((sd_csd[1] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_csd.read_bl_partial = (tempbyte & 0x80U) >> 7U;
    pcardinfo->card_csd.write_blk_misalign = (tempbyte & 0x40U) >> 6U;
    pcardinfo->card_csd.read_blk_misalign = (tempbyte & 0x20U) >> 5U;
    pcardinfo->card_csd.dsp_imp = (tempbyte & 0x10U) >> 4U;

    if((SDIO_STD_CAPACITY_SD_CARD_V1_1 == cardtype) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == cardtype)) {
        /* card is SDSC card, CSD version 1.0 */
        pcardinfo->card_csd.c_size = ((uint32_t)tempbyte & 0x03U) << 10U;

        /* CSD byte 7 */
        tempbyte = (uint8_t)(sd_csd[1] & SD_MASK_0_7BITS);
        pcardinfo->card_csd.c_size |= (uint32_t)((uint32_t)tempbyte << 2U);

        /* CSD byte 8 */
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_24_31BITS) >> 24U);
        pcardinfo->card_csd.c_size |= ((uint32_t)tempbyte & 0xC0U) >> 6U;
        pcardinfo->card_csd.vdd_r_curr_min = (tempbyte & 0x38U) >> 3U;
        pcardinfo->card_csd.vdd_r_curr_max = tempbyte & 0x07U;

        /* CSD byte 9 */
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_16_23BITS) >> 16U);
        pcardinfo->card_csd.vdd_w_curr_min = (tempbyte & 0xE0U) >> 5U;
        pcardinfo->card_csd.vdd_w_curr_max = (tempbyte & 0x1CU) >> 2U;
        pcardinfo->card_csd.c_size_mult = (tempbyte & 0x03U) << 1U;

        /* CSD byte 10 */
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_8_15BITS) >> 8U);
        pcardinfo->card_csd.c_size_mult |= (tempbyte & 0x80U) >> 7U;

        /* calculate the card block size and capacity */
        pcardinfo->card_blocksize = 1U << (pcardinfo->card_csd.read_bl_len);
        pcardinfo->card_capacity = pcardinfo->card_csd.c_size + 1U;
        pcardinfo->card_capacity *= (1U << (pcardinfo->card_csd.c_size_mult + 2U));
        pcardinfo->card_capacity *= pcardinfo->card_blocksize;
    } else if(SDIO_HIGH_CAPACITY_SD_CARD == cardtype) {
        /* card is SDHC card, CSD version 2.0 */
        /* CSD byte 7 */
        tempbyte = (uint8_t)(sd_csd[1] & SD_MASK_0_7BITS);
        pcardinfo->card_csd.c_size = ((uint32_t)tempbyte & 0x3FU) << 16U;

        /* CSD byte 8 */
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_24_31BITS) >> 24U);
        pcardinfo->card_csd.c_size |= (uint32_t)((uint32_t)tempbyte << 8U);

        /* CSD byte 9 */
        tempbyte = (uint8_t)((sd_csd[2] & SD_MASK_16_23BITS) >> 16U);
        pcardinfo->card_csd.c_size |= (uint32_t)tempbyte;

        /* calculate the card block size and capacity */
        pcardinfo->card_blocksize = 512U;
        pcardinfo->card_capacity = (pcardinfo->card_csd.c_size + 1U) * 512U * 1024U;
    } else {
        /* if else end */
    }

    pcardinfo->card_csd.erase_blk_en = (tempbyte & 0x40U) >> 6U;
    pcardinfo->card_csd.sector_size = (tempbyte & 0x3FU) << 1U;

    /* CSD byte 11 */
    tempbyte = (uint8_t)(sd_csd[2] & SD_MASK_0_7BITS);
    pcardinfo->card_csd.sector_size |= (tempbyte & 0x80U) >> 7U;
    pcardinfo->card_csd.wp_grp_size = (tempbyte & 0x7FU);

    /* CSD byte 12 */
    tempbyte = (uint8_t)((sd_csd[3] & SD_MASK_24_31BITS) >> 24U);
    pcardinfo->card_csd.wp_grp_enable = (tempbyte & 0x80U) >> 7U;
    pcardinfo->card_csd.r2w_factor = (tempbyte & 0x1CU) >> 2U;
    pcardinfo->card_csd.write_bl_len = (tempbyte & 0x03U) << 2U;

    /* CSD byte 13 */
    tempbyte = (uint8_t)((sd_csd[3] & SD_MASK_16_23BITS) >> 16U);
    pcardinfo->card_csd.write_bl_len |= (tempbyte & 0xC0U) >> 6U;
    pcardinfo->card_csd.write_bl_partial = (tempbyte & 0x20U) >> 5U;

    /* CSD byte 14 */
    tempbyte = (uint8_t)((sd_csd[3] & SD_MASK_8_15BITS) >> 8U);
    pcardinfo->card_csd.file_format_grp = (tempbyte & 0x80U) >> 7U;
    pcardinfo->card_csd.copy_flag = (tempbyte & 0x40U) >> 6U;
    pcardinfo->card_csd.perm_write_protect = (tempbyte & 0x20U) >> 5U;
    pcardinfo->card_csd.tmp_write_protect = (tempbyte & 0x10U) >> 4U;
    pcardinfo->card_csd.file_format = (tempbyte & 0x0CU) >> 2U;

    /* CSD byte 15 */
    tempbyte = (uint8_t)(sd_csd[3] & SD_MASK_0_7BITS);
    pcardinfo->card_csd.csd_crc = (tempbyte & 0xFEU) >> 1U;

    return status;
}

/*!
    \brief      check if the command sent error occurs
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum cmdsent_error_check(void)
{
    sd_error_enum status = SD_OK;
    uint32_t timeout = 100000U;
    /* check command sent flag */
    while((RESET == sdio_flag_get(SDIO, SDIO_FLAG_CMDSEND)) && (timeout > 0U)) {
        --timeout;
    }
    /* command response is timeout */
    if(0U == timeout) {
        status = SD_CMD_RESP_TIMEOUT;
        return status;
    }
    /* if the command is sent, clear the CMD_FLAGS flags */
    sdio_flag_clear(SDIO, SDIO_MASK_CMD_FLAGS);
    return status;
}

/*!
    \brief      check if error type for R1 response
    \param[in]  resp: content of response
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum r1_error_type_check(uint32_t resp)
{
    sd_error_enum status = SD_ERROR;
    /* check which error occurs */
    if(resp & SD_R1_OUT_OF_RANGE) {
        status = SD_OUT_OF_RANGE;
    } else if(resp & SD_R1_ADDRESS_ERROR) {
        status = SD_ADDRESS_ERROR;
    } else if(resp & SD_R1_BLOCK_LEN_ERROR) {
        status = SD_BLOCK_LEN_ERROR;
    } else if(resp & SD_R1_ERASE_SEQ_ERROR) {
        status = SD_ERASE_SEQ_ERROR;
    } else if(resp & SD_R1_ERASE_PARAM) {
        status = SD_ERASE_PARAM;
    } else if(resp & SD_R1_WP_VIOLATION) {
        status = SD_WP_VIOLATION;
    } else if(resp & SD_R1_LOCK_UNLOCK_FAILED) {
        status = SD_LOCK_UNLOCK_FAILED;
    } else if(resp & SD_R1_COM_CRC_ERROR) {
        status = SD_COM_CRC_ERROR;
    } else if(resp & SD_R1_ILLEGAL_COMMAND) {
        status = SD_ILLEGAL_COMMAND;
    } else if(resp & SD_R1_CARD_ECC_FAILED) {
        status = SD_CARD_ECC_FAILED;
    } else if(resp & SD_R1_CC_ERROR) {
        status = SD_CC_ERROR;
    } else if(resp & SD_R1_GENERAL_UNKNOWN_ERROR) {
        status = SD_GENERAL_UNKNOWN_ERROR;
    } else if(resp & SD_R1_CSD_OVERWRITE) {
        status = SD_CSD_OVERWRITE;
    } else if(resp & SD_R1_WP_ERASE_SKIP) {
        status = SD_WP_ERASE_SKIP;
    } else if(resp & SD_R1_CARD_ECC_DISABLED) {
        status = SD_CARD_ECC_DISABLED;
    } else if(resp & SD_R1_ERASE_RESET) {
        status = SD_ERASE_RESET;
    } else if(resp & SD_R1_AKE_SEQ_ERROR) {
        status = SD_AKE_SEQ_ERROR;
    } else {
        /*no todo,  if else end */
    }
    return status;
}

/*!
    \brief      check if error occurs for R1 response
    \param[in]  cmdindex: the index of command
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum r1_error_check(uint8_t cmdindex)
{
    sd_error_enum status = SD_OK;
    uint32_t reg_status = 0U, resp_r1 = 0U;

    /* store the content of SDIO_STAT */
    reg_status = SDIO_STAT(SDIO);
    while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))) {
        reg_status = SDIO_STAT(SDIO);
    }
    /* check whether an error or timeout occurs or command response received */
    if(reg_status & SDIO_FLAG_CCRCERR) {
        status = SD_CMD_CRC_ERROR;
        sdio_flag_clear(SDIO, SDIO_FLAG_CCRCERR);
        return status;
    } else if(reg_status & SDIO_FLAG_CMDTMOUT) {
        status = SD_CMD_RESP_TIMEOUT;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDTMOUT);
        return status;
    } else {
        /* if else end */
    }

    /* check whether the last response command index is the desired one */
    if(sdio_command_index_get(SDIO) != cmdindex) {
        status = SD_ILLEGAL_COMMAND;
        return status;
    }
    /* clear all the CMD_FLAGS flags */
    sdio_flag_clear(SDIO, SDIO_MASK_CMD_FLAGS);
    /* get the SDIO response register 0 for checking */
    resp_r1 = sdio_response_get(SDIO, SDIO_RESPONSE0);
    if(SD_ALLZERO == (resp_r1 & SD_R1_ERROR_BITS)) {
        /* no error occurs, return SD_OK */
        status = SD_OK;
        return status;
    }

    /* if some error occurs, return the error type */
    status = r1_error_type_check(resp_r1);
    return status;
}

/*!
    \brief      check if error occurs for R2 response
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum r2_error_check(void)
{
    sd_error_enum status = SD_OK;
    uint32_t reg_status = 0U;

    /* store the content of SDIO_STAT */
    reg_status = SDIO_STAT(SDIO);
    while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))) {
        reg_status = SDIO_STAT(SDIO);
    }
    /* check whether an error or timeout occurs or command response received */
    if(reg_status & SDIO_FLAG_CCRCERR) {
        status = SD_CMD_CRC_ERROR;
        sdio_flag_clear(SDIO, SDIO_FLAG_CCRCERR);
        return status;
    } else if(reg_status & SDIO_FLAG_CMDTMOUT) {
        status = SD_CMD_RESP_TIMEOUT;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDTMOUT);
        return status;
    } else {
        /* if else end */
    }
    /* clear all the CMD_FLAGS flags */
    sdio_flag_clear(SDIO, SDIO_MASK_CMD_FLAGS);

    return status;
}


/*!
    \brief      check if error occurs for R3 response
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum r3_error_check(void)
{
    sd_error_enum status = SD_OK;
    uint32_t reg_status = 0U;

    /* store the content of SDIO_STAT */
    reg_status = SDIO_STAT(SDIO);
    while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))) {
        reg_status = SDIO_STAT(SDIO);
    }
    if(reg_status & SDIO_FLAG_CMDTMOUT) {
        status = SD_CMD_RESP_TIMEOUT;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDTMOUT);
        return status;
    }
    /* clear all the CMD_FLAGS flags */
    sdio_flag_clear(SDIO, SDIO_MASK_CMD_FLAGS);
    return status;
}

/*!
    \brief      check if error occurs for R6 response
    \param[in]  cmdindex: the index of command
    \param[out] prca: a pointer that store the RCA of card
    \retval     sd_error_enum
*/
static sd_error_enum r6_error_check(uint8_t cmdindex, uint16_t *prca)
{
    sd_error_enum status = SD_OK;
    uint32_t reg_status = 0U, response = 0U;

    /* store the content of SDIO_STAT */
    reg_status = SDIO_STAT(SDIO);
    while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))) {
        reg_status = SDIO_STAT(SDIO);
    }
    /* check whether an error or timeout occurs or command response received */
    if(reg_status & SDIO_FLAG_CCRCERR) {
        status = SD_CMD_CRC_ERROR;
        sdio_flag_clear(SDIO, SDIO_FLAG_CCRCERR);
        return status;
    } else if(reg_status & SDIO_FLAG_CMDTMOUT) {
        status = SD_CMD_RESP_TIMEOUT;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDTMOUT);
        return status;
    } else {
        /* if else end */
    }

    /* check whether the last response command index is the desired one */
    if(sdio_command_index_get(SDIO) != cmdindex) {
        status = SD_ILLEGAL_COMMAND;
        return status;
    }
    /* clear all the CMD_FLAGS flags */
    sdio_flag_clear(SDIO, SDIO_MASK_CMD_FLAGS);
    /* get the SDIO response register 0 for checking */
    response = sdio_response_get(SDIO, SDIO_RESPONSE0);

    if(SD_ALLZERO == (response & (SD_R6_COM_CRC_ERROR | SD_R6_ILLEGAL_COMMAND | SD_R6_GENERAL_UNKNOWN_ERROR))) {
        *prca = (uint16_t)(response >> 16U);
        return status;
    }
    /* if some error occurs, return the error type */
    if(response & SD_R6_COM_CRC_ERROR) {
        status = SD_COM_CRC_ERROR;
    } else if(response & SD_R6_ILLEGAL_COMMAND) {
        status = SD_ILLEGAL_COMMAND;
    } else if(response & SD_R6_GENERAL_UNKNOWN_ERROR) {
        status = SD_GENERAL_UNKNOWN_ERROR;
    } else {
        /* if else end */
    }
    return status;
}


/*!
    \brief      check if error occurs for R7 response
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum r7_error_check(void)
{
    sd_error_enum status = SD_ERROR;
    uint32_t reg_status = 0U, timeout = 100000U;

    /* store the content of SDIO_STAT */
    reg_status = SDIO_STAT(SDIO);
    while(((reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV)) == 0U) && (timeout > 0U)) {
        reg_status = SDIO_STAT(SDIO);
        --timeout;
    }

    /* check the flags */
    if((reg_status & SDIO_FLAG_CMDTMOUT) || (0U == timeout)) {
        status = SD_CMD_RESP_TIMEOUT;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDTMOUT);
        return status;
    }
    if(reg_status & SDIO_FLAG_CMDRECV) {
        status = SD_OK;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDRECV);
        return status;
    }
    return status;
}

/*!
    \brief      get the state which the card is in
    \param[in]  none
    \param[out] pcardstate: a pointer that store the card state
      \arg        SD_CARDSTATE_IDLE: card is in idle state
      \arg        SD_CARDSTATE_READY: card is in ready state
      \arg        SD_CARDSTATE_IDENTIFICAT: card is in identificat state
      \arg        SD_CARDSTATE_STANDBY: card is in standby state
      \arg        SD_CARDSTATE_TRANSFER: card is in transfer state
      \arg        SD_CARDSTATE_DATA: card is in data state
      \arg        SD_CARDSTATE_RECEIVING: card is in receiving state
      \arg        SD_CARDSTATE_PROGRAMMING: card is in programming state
      \arg        SD_CARDSTATE_DISCONNECT: card is in disconnect state
      \arg        SD_CARDSTATE_LOCKED: card is in locked state
    \retval     sd_error_enum
*/
static sd_error_enum sd_card_state_get(uint8_t *pcardstate)
{
    sd_error_enum status = SD_OK;
    __IO uint32_t reg_status = 0U, response = 0U;

    /* 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);

    /* store the content of SDIO_STAT */
    reg_status = SDIO_STAT(SDIO);
    while(!(reg_status & (SDIO_FLAG_CCRCERR | SDIO_FLAG_CMDTMOUT | SDIO_FLAG_CMDRECV))) {
        reg_status = SDIO_STAT(SDIO);
    }
    /* check whether an error or timeout occurs or command response received */
    if(reg_status & SDIO_FLAG_CCRCERR) {
        status = SD_CMD_CRC_ERROR;
        sdio_flag_clear(SDIO, SDIO_FLAG_CCRCERR);
        return status;
    } else if(reg_status & SDIO_FLAG_CMDTMOUT) {
        status = SD_CMD_RESP_TIMEOUT;
        sdio_flag_clear(SDIO, SDIO_FLAG_CMDTMOUT);
        return status;
    } else {
        /* if else end */
    }

    /* command response received, store the response command index */
    reg_status = (uint32_t)sdio_command_index_get(SDIO);
    if(reg_status != (uint32_t)SD_CMD_SEND_STATUS) {
        status = SD_ILLEGAL_COMMAND;
        return status;
    }
    /* clear all the SDIO_INTC flags */
    sdio_flag_clear(SDIO, SDIO_MASK_INTC_FLAGS);
    /* get the SDIO response register 0 for checking */
    response = sdio_response_get(SDIO, SDIO_RESPONSE0);
    *pcardstate = (uint8_t)((response >> 9U) & 0x0000000FU);

    if(SD_ALLZERO == (response & SD_R1_ERROR_BITS)) {
        /* no error occurs, return SD_OK */
        status = SD_OK;
        return status;
    }

    /* if some error occurs, return the error type */
    status = r1_error_type_check(response);
    return status;
}

/*!
    \brief      stop an ongoing data transfer
    \param[in]  none
    \param[out] none
    \retval     sd_error_enum
*/
sd_error_enum sd_transfer_stop(void)
{
    sd_error_enum status = SD_OK;
    /* send CMD12(STOP_TRANSMISSION) to stop transmission */
    sdio_command_response_config(SDIO, SD_CMD_STOP_TRANSMISSION, (uint32_t)0x0U, SDIO_RESPONSETYPE_SHORT);
    sdio_wait_type_set(SDIO, SDIO_WAITTYPE_NO);
    sdio_trans_stop_enable(SDIO);
    sdio_csm_enable(SDIO);
    /* check if some error occurs */
    status = r1_error_check(SD_CMD_STOP_TRANSMISSION);
    sdio_trans_stop_disable(SDIO);
    return status;
}

/*!
    \brief      lock or unlock a card
    \param[in]  lockstate: the lock state
      \arg        SD_LOCK: lock the SD card
      \arg        SD_UNLOCK: unlock the SD card
    \param[out] none
    \retval     sd_error_enum
*/
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;
}

/*!
    \brief      configure the bus width mode
    \param[in]  buswidth: the bus width
      \arg        SD_BUS_WIDTH_1BIT: 1-bit bus width
      \arg        SD_BUS_WIDTH_4BIT: 4-bit bus width
    \param[out] none
    \retval     sd_error_enum
*/
static sd_error_enum sd_bus_width_config(uint32_t buswidth)
{
    sd_error_enum status = SD_OK;
    /* check whether the card is locked */
    if(sdio_response_get(SDIO, SDIO_RESPONSE0) & SD_CARDSTATE_LOCKED) {
        status = SD_LOCK_UNLOCK_FAILED;
        return status;
    }
    /* get the SCR register */
    status = sd_scr_get(sd_rca, sd_scr);
    if(SD_OK != status) {
        return status;
    }

    if(SD_BUS_WIDTH_1BIT == buswidth) {
        if(SD_ALLZERO != (sd_scr[1] & buswidth)) {
            /* send CMD55(APP_CMD) to indicate next command is application specific command */
            sdio_command_response_config(SDIO, SD_CMD_APP_CMD, (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_APP_CMD);
            if(SD_OK != status) {
                return status;
            }

            /* send ACMD6(SET_BUS_WIDTH) to define the data bus width */
            sdio_command_response_config(SDIO, SD_APPCMD_SET_BUS_WIDTH, (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_APPCMD_SET_BUS_WIDTH);
            if(SD_OK != status) {
                return status;
            }
        } else {
            status = SD_OPERATION_IMPROPER;
        }
        return status;
    } else if(SD_BUS_WIDTH_4BIT == buswidth) {
        if(SD_ALLZERO != (sd_scr[1] & buswidth)) {
            /* send CMD55(APP_CMD) to indicate next command is application specific command */
            sdio_command_response_config(SDIO, SD_CMD_APP_CMD, (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_APP_CMD);
            if(SD_OK != status) {
                return status;
            }

            /* send ACMD6(SET_BUS_WIDTH) to define the data bus width */
            sdio_command_response_config(SDIO, SD_APPCMD_SET_BUS_WIDTH, (uint32_t)0x2U, 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_APPCMD_SET_BUS_WIDTH);
            if(SD_OK != status) {
                return status;
            }
        } else {
            status = SD_OPERATION_IMPROPER;
        }
        return status;
    } else {
        status = SD_PARAMETER_INVALID;
        return status;
    }
}

/*!
    \brief      get the SCR of corresponding card
    \param[in]  rca: RCA of a card
    \param[out] pscr: a pointer that store the SCR content
    \retval     sd_error_enum
*/
static sd_error_enum sd_scr_get(uint16_t rca, uint32_t *pscr)
{
    uint32_t temp_scr[2] = {0, 0}, idx_scr = 0U;
    sd_error_enum status = SD_OK;
    /* send CMD16(SET_BLOCKLEN) to set 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 CMD55(APP_CMD) to indicate next command is application specific command */
    sdio_command_response_config(SDIO, SD_CMD_APP_CMD, (uint32_t)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_APP_CMD);
    if(SD_OK != status) {
        return status;
    }

    /* configure SDIO data */
    sdio_data_config(SDIO, SD_DATATIMEOUT, (uint32_t)8, SDIO_DATABLOCKSIZE_8BYTES);
    sdio_data_transfer_config(SDIO, SDIO_TRANSMODE_BLOCKCOUNT, SDIO_TRANSDIRECTION_TOSDIO);
    sdio_dsm_enable(SDIO);

    /* send ACMD51(SEND_SCR) to read the SD configuration register */
    sdio_command_response_config(SDIO, SD_APPCMD_SEND_SCR, (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_APPCMD_SEND_SCR);
    if(SD_OK != status) {
        return status;
    }

    /* store the received SCR */
    while(!sdio_flag_get(SDIO, SDIO_FLAG_DTCRCERR | SDIO_FLAG_DTTMOUT | SDIO_FLAG_RXORE | SDIO_FLAG_DTBLKEND | SDIO_FLAG_DTEND)) {
        if((SET != sdio_flag_get(SDIO, SDIO_FLAG_RFE)) && (SET == sdio_flag_get(SDIO, SDIO_FLAG_DATSTA))) {
            *(temp_scr + idx_scr) = sdio_data_read(SDIO);
            ++idx_scr;
        }
    }

    /* check whether some error occurs */
    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 */
    }

    /* clear all the SDIO_INTC flags */
    sdio_flag_clear(SDIO, SDIO_MASK_INTC_FLAGS);
    /* readjust the temp SCR value */
    *(pscr) = ((temp_scr[1] & SD_MASK_0_7BITS) << 24U) | ((temp_scr[1] & SD_MASK_8_15BITS) << 8U) |
              ((temp_scr[1] & SD_MASK_16_23BITS) >> 8U) | ((temp_scr[1] & SD_MASK_24_31BITS) >> 24U);
    *(pscr + 1U) = ((temp_scr[0] & SD_MASK_0_7BITS) << 24U) | ((temp_scr[0] & SD_MASK_8_15BITS) << 8U) |
                   ((temp_scr[0] & SD_MASK_16_23BITS) >> 8U) | ((temp_scr[0] & SD_MASK_24_31BITS) >> 24U);
    return status;
}

/*!
    \brief      get the data block size
    \param[in]  bytesnumber: the number of bytes
    \param[out] none
    \retval     data block size
      \arg        SDIO_DATABLOCKSIZE_1BYTE: block size = 1 byte
      \arg        SDIO_DATABLOCKSIZE_2BYTES: block size = 2 bytes
      \arg        SDIO_DATABLOCKSIZE_4BYTES: block size = 4 bytes
      \arg        SDIO_DATABLOCKSIZE_8BYTES: block size = 8 bytes
      \arg        SDIO_DATABLOCKSIZE_16BYTES: block size = 16 bytes
      \arg        SDIO_DATABLOCKSIZE_32BYTES: block size = 32 bytes
      \arg        SDIO_DATABLOCKSIZE_64BYTES: block size = 64 bytes
      \arg        SDIO_DATABLOCKSIZE_128BYTES: block size = 128 bytes
      \arg        SDIO_DATABLOCKSIZE_256BYTES: block size = 256 bytes
      \arg        SDIO_DATABLOCKSIZE_512BYTES: block size = 512 bytes
      \arg        SDIO_DATABLOCKSIZE_1024BYTES: block size = 1024 bytes
      \arg        SDIO_DATABLOCKSIZE_2048BYTES: block size = 2048 bytes
      \arg        SDIO_DATABLOCKSIZE_4096BYTES: block size = 4096 bytes
      \arg        SDIO_DATABLOCKSIZE_8192BYTES: block size = 8192 bytes
      \arg        SDIO_DATABLOCKSIZE_16384BYTES: block size = 16384 bytes
*/
static uint32_t sd_datablocksize_get(uint16_t bytesnumber)
{
    uint8_t exp_val = 0U;
    /* calculate the exponent of 2 */
    while(1U != bytesnumber) {
        bytesnumber >>= 1U;
        ++exp_val;
    }
    return DATACTL_BLKSZ(exp_val);
}

/*!
    \brief      configure the GPIO of SDIO interface
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void gpio_config(void)
{
    /* configure the SDIO_DAT0(PB13), SDIO_DAT1(PC9), SDIO_DAT2(PC10), SDIO_DAT3(PC11), SDIO_CLK(PC12) and SDIO_CMD(PD2) */
    gpio_af_set(GPIOB, GPIO_AF_12, GPIO_PIN_13);
    gpio_af_set(GPIOC, GPIO_AF_12, GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
    gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_2);

    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_13);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11);

    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11);

    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);

    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
}

/*!
    \brief      configure the RCU of SDIO
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void rcu_config(void)
{
    /* SDIO clock */
    /* configure the pll1 input and output clock range */
    rcu_pll_input_output_clock_range_config(IDX_PLL1, RCU_PLL1RNG_4M_8M, RCU_PLL1VCO_192M_836M);
    /* configure the PLL1 clock: CK_PLL1P/CK_PLL1Q/CK_PLL1R = HXTAL_VALUE / 5 * 40 / 1 */
    /* SDIO clock 400M */
    rcu_pll1_config(5, 160, 1, 2, 2);
    /* enable PLL1R clock output */
    rcu_pll_clock_output_enable(RCU_PLL1R);
    /* enable PLL1 clock */
    rcu_osci_on(RCU_PLL1_CK);

    if(ERROR == rcu_osci_stab_wait(RCU_PLL1_CK)) {
        while(1) {
        }
    }
    
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOD);
    rcu_sdio_clock_config(IDX_SDIO0, RCU_SDIO0SRC_PLL1R);
    rcu_periph_clock_enable(RCU_SDIO0);
}

/*!
    \brief      configure the IDMA
    \param[in]  srcbuf: a pointer point to a buffer which will be transferred
    \param[in]  bufsize: the size of buffer(not used in flow controller is peripheral)
    \param[out] none
    \retval     none
*/
static void dma_config(uint32_t *srcbuf, uint32_t bufsize)
{
    sdio_idma_set(SDIO, SDIO_IDMA_SINGLE_BUFFER, bufsize);
    sdio_idma_buffer0_address_set(SDIO, (uint32_t)srcbuf);
}

 

2.6、sdcard.h

/*!
    \file    sdcard.h
    \brief   the header file of SD card driver
    
    \version 2024-01-05, V1.2.0, demo for GD32H7xx
*/

/*
    Copyright (c) 2024, GigaDevice Semiconductor Inc.

    Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice,
       this list of conditions and the following disclaimer in the documentation
       and/or other materials provided with the distribution.
    3. Neither the name of the copyright holder nor the names of its contributors
       may be used to endorse or promote products derived from this software without
       specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

#ifndef SDCARD_H
#define SDCARD_H

#define SDIO SDIO0

#include "gd32h7xx.h"

/* SD memory card bus commands index */
#define SD_CMD_GO_IDLE_STATE                  ((uint8_t)0)   /* CMD0, GO_IDLE_STATE */
#define SD_CMD_ALL_SEND_CID                   ((uint8_t)2)   /* CMD2, ALL_SEND_CID */
#define SD_CMD_SEND_RELATIVE_ADDR             ((uint8_t)3)   /* CMD3, SEND_RELATIVE_ADDR */
#define SD_CMD_SET_DSR                        ((uint8_t)4)   /* CMD4, SET_DSR */
#define SD_CMD_SWITCH_FUNC                    ((uint8_t)6)   /* CMD6, SWITCH_FUNC */
#define SD_CMD_SELECT_DESELECT_CARD           ((uint8_t)7)   /* CMD7, SELECT_DESELECT_CARD */
#define SD_CMD_SEND_IF_COND                   ((uint8_t)8)   /* CMD8, SEND_IF_COND */
#define SD_CMD_SEND_CSD                       ((uint8_t)9)   /* CMD9, SEND_CSD */
#define SD_CMD_SEND_CID                       ((uint8_t)10)  /* CMD10, SEND_CID */
#define SD_CMD_VOLATAGE_SWITCH                ((uint8_t)11)  /* CMD11, SD_CMD_VOLATAGE_SWITCH */
#define SD_CMD_STOP_TRANSMISSION              ((uint8_t)12)  /* CMD12, STOP_TRANSMISSION */
#define SD_CMD_SEND_STATUS                    ((uint8_t)13)  /* CMD13, SEND_STATUS */
#define SD_CMD_GO_INACTIVE_STATE              ((uint8_t)15)  /* CMD15, GO_INACTIVE_STATE */
#define SD_CMD_SET_BLOCKLEN                   ((uint8_t)16)  /* CMD16, SET_BLOCKLEN */
#define SD_CMD_READ_SINGLE_BLOCK              ((uint8_t)17)  /* CMD17, READ_SINGLE_BLOCK */
#define SD_CMD_READ_MULTIPLE_BLOCK            ((uint8_t)18)  /* CMD18, READ_MULTIPLE_BLOCK */
#define SD_SEND_TUNING_PATTERN                ((uint8_t)19)  /* CMD19, SEND_TUNING_PATTERN */
#define SD_CMD_WRITE_BLOCK                    ((uint8_t)24)  /* CMD24, WRITE_BLOCK */
#define SD_CMD_WRITE_MULTIPLE_BLOCK           ((uint8_t)25)  /* CMD25, WRITE_MULTIPLE_BLOCK */
#define SD_CMD_PROG_CSD                       ((uint8_t)27)  /* CMD27, PROG_CSD */
#define SD_CMD_SET_WRITE_PROT                 ((uint8_t)28)  /* CMD28, SET_WRITE_PROT */
#define SD_CMD_CLR_WRITE_PROT                 ((uint8_t)29)  /* CMD29, CLR_WRITE_PROT */
#define SD_CMD_SEND_WRITE_PROT                ((uint8_t)30)  /* CMD30, SEND_WRITE_PROT */
#define SD_CMD_ERASE_WR_BLK_START             ((uint8_t)32)  /* CMD32, ERASE_WR_BLK_START */
#define SD_CMD_ERASE_WR_BLK_END               ((uint8_t)33)  /* CMD33, ERASE_WR_BLK_END */
#define SD_CMD_ERASE                          ((uint8_t)38)  /* CMD38, ERASE */
#define SD_CMD_LOCK_UNLOCK                    ((uint8_t)42)  /* CMD42, LOCK_UNLOCK */
#define SD_CMD_APP_CMD                        ((uint8_t)55)  /* CMD55, APP_CMD */
#define SD_CMD_GEN_CMD                        ((uint8_t)56)  /* CMD56, GEN_CMD */

/* SD memory card application specific commands index */
#define SD_APPCMD_SET_BUS_WIDTH               ((uint8_t)6)   /* ACMD6, SET_BUS_WIDTH */
#define SD_APPCMD_SD_STATUS                   ((uint8_t)13)  /* ACMD13, SD_STATUS */
#define SD_APPCMD_SEND_NUM_WR_BLOCKS          ((uint8_t)22)  /* ACMD22, SEND_NUM_WR_BLOCKS */
#define SD_APPCMD_SET_WR_BLK_ERASE_COUNT      ((uint8_t)23)  /* ACMD23, SET_WR_BLK_ERASE_COUNT */
#define SD_APPCMD_SD_SEND_OP_COND             ((uint8_t)41)  /* ACMD41, SD_SEND_OP_COND */
#define SD_APPCMD_SET_CLR_CARD_DETECT         ((uint8_t)42)  /* ACMD42, SET_CLR_CARD_DETECT */
#define SD_APPCMD_SEND_SCR                    ((uint8_t)51)  /* ACMD51, SEND_SCR */

/* card command class */
#define SD_CCC_SWITCH                          BIT(10)       /* class 10 */
#define SD_CCC_IO_MODE                         BIT(9)        /* class 9 */
#define SD_CCC_APPLICATION_SPECIFIC            BIT(8)        /* class 8 */
#define SD_CCC_LOCK_CARD                       BIT(7)        /* class 7 */
#define SD_CCC_WRITE_PROTECTION                BIT(6)        /* class 6 */
#define SD_CCC_ERASE                           BIT(5)        /* class 5 */
#define SD_CCC_BLOCK_WRITE                     BIT(4)        /* class 4 */
#define SD_CCC_BLOCK_READ                      BIT(2)        /* class 2 */
#define SD_CCC_BASIC                           BIT(0)        /* class 0 */

/* SD card data transmission mode */
#define SD_DMA_MODE                           ((uint32_t)0x00000000) /* DMA mode */
#define SD_POLLING_MODE                       ((uint32_t)0x00000001) /* polling mode */

/* lock unlock status */
#define SD_LOCK                               ((uint8_t)0x05)        /* lock the SD card */
#define SD_UNLOCK                             ((uint8_t)0x02)        /* unlock the SD card */

#define SD_SDSC                               ((uint32_t)0x00000000U)/*!< SD Standard Capacity <2G*/
#define SD_SDHC_SDXC                          ((uint32_t)0x00000001U)/*!< SD High Capacity <32G, SD Extended Capacity <2T */

#define SD_SPEED_DEFAULT                      ((uint32_t)0x00000000U)/*!< maintain current speed , clock frequency max value 25M*/
#define SD_SPEED_HIGH                         ((uint32_t)0x00000001U)/*!< switch high speed, clock frequency max value 50M */
#define SD_SPEED_SDR25                        ((uint32_t)0x80FFFF01U)/*!< switch UHS-I SDR25 speed, clock frequency max value 50M */
#define SD_SPEED_SDR50                        ((uint32_t)0x80FF1F02U)/*!< switch UHS-I SDR50 speed, clock frequency max value 104M */
#define SD_SPEED_SDR104                       ((uint32_t)0x80FF1F03U)/*!< switch UHS-I SDR104 speed, clock frequency max value 208M */
#define SD_SPEED_DDR50                        ((uint32_t)0x80FF1F04U)/*!< switch UHS-I DDR speed , clock frequency max value 50M */

/* supported memory cards types */
typedef enum {
    SDIO_STD_CAPACITY_SD_CARD_V1_1 = 0,   /* standard capacity SD card version 1.1 */
    SDIO_STD_CAPACITY_SD_CARD_V2_0,       /* standard capacity SD card version 2.0 */
    SDIO_STD_CAPACITY_SD_CARD_V3_0,       /* standard capacity SD card version 3.0 */
    SDIO_HIGH_CAPACITY_SD_CARD,           /* high capacity SD card */
    SDIO_SECURE_DIGITAL_IO_CARD,          /* secure digital IO card */
    SDIO_SECURE_DIGITAL_IO_COMBO_CARD,    /* secure digital IO combo card */
    SDIO_MULTIMEDIA_CARD,                 /* multimedia card */
    SDIO_HIGH_CAPACITY_MULTIMEDIA_CARD,   /* high capacity multimedia card */
    SDIO_HIGH_SPEED_MULTIMEDIA_CARD       /* high speed multimedia card */
} sdio_card_type_enum;

/* card identification (CID) register */
typedef struct {
    __IO uint8_t mid;                     /* manufacturer ID */
    __IO uint16_t oid;                    /* OEM/application ID */
    __IO uint32_t pnm0;                   /* product name */
    __IO uint8_t pnm1;                    /* product name */
    __IO uint8_t prv;                     /* product revision */
    __IO uint32_t psn;                    /* product serial number */
    __IO uint16_t mdt;                    /* manufacturing date */
    __IO uint8_t cid_crc;                 /* CID CRC7 checksum */
} sd_cid_struct;

/* CSD register (CSD version 1.0 and 2.0) */
typedef struct {
    __IO uint8_t csd_struct;              /* CSD struct */
    __IO uint8_t taac;                    /* data read access-time */
    __IO uint8_t nsac;                    /* data read access-time in CLK cycles */
    __IO uint8_t tran_speed;              /* max. data transfer rate */
    __IO uint16_t ccc;                    /* card command classes */
    __IO uint8_t read_bl_len;             /* max. read data block length */
    __IO uint8_t read_bl_partial;         /* partial blocks for read allowed */
    __IO uint8_t write_blk_misalign;      /* write block misalignment */
    __IO uint8_t read_blk_misalign;       /* read block misalignment */
    __IO uint8_t dsp_imp;                 /* DSR implemented */
    __IO uint32_t c_size;                 /* device size, 12 bits in CSD version 1.0, 22 bits in CSD version 2.0 */
    __IO uint8_t vdd_r_curr_min;          /* max. read current [url=home.php?mod=space&uid=201251]@vdd[/url] min, CSD version 1.0 */
    __IO uint8_t vdd_r_curr_max;          /* max. read current @VDD max, CSD version 1.0 */
    __IO uint8_t vdd_w_curr_min;          /* max. write current @VDD min, CSD version 1.0 */
    __IO uint8_t vdd_w_curr_max;          /* max. write current @VDD max, CSD version 1.0 */
    __IO uint8_t c_size_mult;             /* device size multiplier, CSD version 1.0 */
    __IO uint8_t erase_blk_en;            /* erase single block enable */
    __IO uint8_t sector_size;             /* erase sector size */
    __IO uint8_t wp_grp_size;             /* write protect group size */
    __IO uint8_t wp_grp_enable;           /* write protect group enable */
    __IO uint8_t r2w_factor;              /* write speed factor */
    __IO uint8_t write_bl_len;            /* max. write data block length */
    __IO uint8_t write_bl_partial;        /* partial blocks for write allowed */
    __IO uint8_t file_format_grp;         /* file format group */
    __IO uint8_t copy_flag;               /* copy flag (OTP) */
    __IO uint8_t perm_write_protect;      /* permanent write protection */
    __IO uint8_t tmp_write_protect;       /* temporary write protection */
    __IO uint8_t file_format;             /* file format */
    __IO uint8_t csd_crc;                 /* CSD CRC checksum */
} sd_csd_struct;


/* information of card */
typedef struct {
    sd_cid_struct card_cid;               /* CID register */
    sd_csd_struct card_csd;               /* CSD register */
    sdio_card_type_enum card_type;        /* card tpye */
    uint32_t card_capacity;               /* card capacity */
    uint32_t card_blocksize;              /* card block size */
    uint16_t card_rca;                    /* card relative card address */
} sd_card_info_struct;

/* SD error flags */
typedef enum {
    SD_OUT_OF_RANGE = 0,                  /* command's argument was out of range */
    SD_ADDRESS_ERROR,                     /* misaligned address which did not match the block length */
    SD_BLOCK_LEN_ERROR,                   /* transferred block length is not allowed for the card or the number of transferred bytes does not match the block length */
    SD_ERASE_SEQ_ERROR,                   /* an error in the sequence of erase command occurs */
    SD_ERASE_PARAM,                       /* an invalid selection of write-blocks for erase occurred */
    SD_WP_VIOLATION,                      /* attempt to program a write protect block or permanent write protected card */
    SD_LOCK_UNLOCK_FAILED,                /* sequence or password error has been detected in lock/unlock card command */
    SD_COM_CRC_ERROR,                     /* CRC check of the previous command failed */
    SD_ILLEGAL_COMMAND,                   /* command not legal for the card state */
    SD_CARD_ECC_FAILED,                   /* card internal ECC was applied but failed to correct the data */
    SD_CC_ERROR,                          /* internal card controller error */
    SD_GENERAL_UNKNOWN_ERROR,             /* general or unknown error occurred during the operation */
    SD_CSD_OVERWRITE,                     /* read only section of the CSD does not match the card content or an attempt to reverse the copy or permanent WP bits was made */
    SD_WP_ERASE_SKIP,                     /* only partial address space was erased or the temporary or permanent write protected card was erased */
    SD_CARD_ECC_DISABLED,                 /* command has been executed without using internal ECC */
    SD_ERASE_RESET,                       /* erase sequence was cleared before executing because an out of erase sequence command was received */
    SD_AKE_SEQ_ERROR,                     /* error in the sequence of the authentication process */

    SD_CMD_CRC_ERROR,                     /* command response received (CRC check failed) */
    SD_DATA_CRC_ERROR,                    /* data block sent/received (CRC check failed) */
    SD_CMD_RESP_TIMEOUT,                  /* command response timeout */
    SD_DATA_TIMEOUT,                      /* data timeout */
    SD_TX_UNDERRUN_ERROR,                 /* transmit FIFO underrun error occurs */
    SD_RX_OVERRUN_ERROR,                  /* received FIFO overrun error occurs */
    SD_START_BIT_ERROR,                   /* start bit error in the bus */

    SD_VOLTRANGE_INVALID,                 /* the voltage range is invalid */
    SD_PARAMETER_INVALID,                 /* the parameter is invalid */
    SD_OPERATION_IMPROPER,                /* the operation is improper */
    SD_FUNCTION_UNSUPPORTED,              /* the function is unsupported */
    SD_ERROR,                             /* an error occurred */
    SD_DMA_ERROR,                         /* an error when idma transfer */
    SD_OK                                 /* no error occurred */
} sd_error_enum;

extern uint32_t sd_scr[2];                /* SD card SCR */
extern uint32_t cardcapacity;             /* SD card capacity type */

/* function declarations */
/* initialize the SD card and make it in standby state */
sd_error_enum sd_init(void);
/* initialize the card and get CID and CSD of the card */
sd_error_enum sd_card_init(void);
/* configure the clock and the work voltage, and get the card type */
sd_error_enum sd_power_on(void);
/* close the power of SDIO */
sd_error_enum sd_power_off(void);
/* configure the bus mode */
sd_error_enum sd_bus_mode_config(uint32_t busmode, uint32_t speed);
/* configure the mode of transmission */
sd_error_enum sd_transfer_mode_config(uint32_t txmode);

/* read a block data into a buffer from the specified address of a card */
sd_error_enum sd_block_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t blocksize);
/* read multiple blocks data into a buffer from the specified address of a card */
sd_error_enum sd_multiblocks_read(uint32_t *preadbuffer, uint32_t readaddr, uint16_t blocksize, uint32_t blocksnumber);
/* write a block data to the specified address of a card */
sd_error_enum sd_block_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_t blocksize);
/* write multiple blocks data to the specified address of a card */
sd_error_enum sd_multiblocks_write(uint32_t *pwritebuffer, uint32_t writeaddr, uint16_t blocksize, uint32_t blocksnumber);
/* erase a continuous area of a card */
sd_error_enum sd_erase(uint32_t startaddr, uint32_t endaddr);
/* process all the interrupts which the corresponding flags are set */
sd_error_enum sd_interrupts_process(void);

/* select or deselect a card */
sd_error_enum sd_card_select_deselect(uint16_t cardrca);
/* get the card status whose response format R1 contains a 32-bit field */
sd_error_enum sd_cardstatus_get(uint32_t *pcardstatus);

/* stop an ongoing data transfer */
sd_error_enum sd_transfer_stop(void);
/* lock or unlock a card */
sd_error_enum sd_lock_unlock(uint8_t lockstate);

/* get SD card capacity(KB) */
uint32_t sd_card_capacity_get(void);
/* get the detailed information of the SD card based on received CID and CSD */
sd_error_enum sd_card_information_get(sd_card_info_struct *pcardinfo);

/* switch 1.8V power level of SD card */
sd_error_enum sd_card_voltage_switch(void);

#endif /* SDCARD_H */

 

2.7、sd.c

#include "sdcard/sdcard.h"
#include "sdcard/sd.h"
#include <stdio.h>

sd_card_info_struct sd_cardinfo;                    

void nvic_config(void)
{
    nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
    nvic_irq_enable(SDIO0_IRQn, 2U, 0);
}


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!");
        status = sd_lock_unlock(SD_UNLOCK);
        if(status != SD_OK) {
            return SD_LOCK_UNLOCK_FAILED;
        } else {
            printf("\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
    }

    if(SD_OK == status) {
        /* set data transfer mode */
        status = sd_transfer_mode_config(SDIO_DTMODE);
    }
    return status;
}




uint8_t sd_readdisk(uint8_t* buf,uint32_t sector,uint32_t cnt)
{
	uint8_t sta=SD_OK;
	long long lsector=sector;
	lsector <<= 9;

	if (cnt == 1)
	{
			sta = sd_block_read((uint32_t*)buf, lsector, 512);           
	}
	else 
	{
			sta = sd_multiblocks_read((uint32_t*)buf, lsector, 512, cnt); 		
	}
	return sta;
}


uint8_t sd_writedisk(uint8_t *buf,uint32_t sector,uint32_t cnt)
{   
	uint8_t sta=SD_OK;
	long long lsector=sector;

	lsector <<= 9;
	sta = sd_multiblocks_write((uint32_t*)buf, lsector, 512, cnt);

  return sta;
}


//0-fail  1-ok
uint8_t init_sd(void)
{
	sd_error_enum sd_error;
	uint16_t i = 5;
	do {
        sd_error = sd_io_init();
    } while((SD_OK != sd_error) && (--i));

    if(i) 
		{
        printf("\r\n Card init success!\r\n");
				return i;
    } 
		else 
		{
      printf("\r\n Card init failed!\r\n");
			return 0;
    }
}

 

2.8、sd.h

#ifndef SD_H
#define SD_H

#include "stdint.h"

#define BUSMODE_1BIT    1
#define BUSMODE_4BIT    0

/* SDIO bus switch */
/* config SDIO bus mode, select: BUSMODE_1BIT/BUSMODE_4BIT */
#define SDIO_BUSMODE        BUSMODE_1BIT
/* config SDIO speed mode, select: SD_SPEED_DEFAULT/SD_SPEED_HIGH */
#define SDIO_SPEEDMODE      SD_SPEED_HIGH
/* config data transfer mode, select: SD_POLLING_MODE/SD_DMA_MODE */
#define SDIO_DTMODE         SD_POLLING_MODE

extern  sd_card_info_struct sd_cardinfo; 

void nvic_config(void);
uint8_t init_sd(void);

uint8_t sd_readdisk(uint8_t* buf,uint32_t sector,uint32_t cnt);
uint8_t sd_writedisk(uint8_t *buf,uint32_t sector,uint32_t cnt);


#endif 

 

2.9、main.c

#include "main.h"

void cache_enable(void);			   

BYTE writeBuffer[] = "\r\n https://bbs.eeworld.com.cn/GD32H759I-EVAL FatFs Test!\r\n";
BYTE readBuffer[2048] = {0};

static FATFS fs[3];
FIL fp;
UINT fnum;	
FRESULT res; 


int main(void)
{
	cache_enable();
	nvic_config();
	systick_config();
	init_usart(115200);
	init_led();
	
	while(init_sd()==0)
	{
		led2_on();
		delay_1ms(500);					
		led2_off();
		delay_1ms(500);
	}

	res = f_mount(&fs[1], "1:", 1);	
	if(res == FR_NO_FILESYSTEM)
	{
		printf("Flash Disk FR_NO_FILESYSTEM \r\n");
		while(1);
	}
	else if(res == FR_OK)
	{
		printf("\r\n文件系统挂载成功!\r\n");
		
		//写文件
		res = f_open(&fp, "1:dat1.txt", FA_OPEN_ALWAYS | FA_WRITE);
		if(res == FR_OK)
		{
			printf("\r\n文件打开成功,准备写入数据!\r\n");	
			res = f_write(&fp,writeBuffer,sizeof(writeBuffer),&fnum);
			if (res==FR_OK) 					
			{
					printf("文件写入成功,写入字节数据: %d\r\n",fnum);		
					printf("写入的数据为: %s\r\n",writeBuffer);
			} 
			else 
			{
					printf("!!文件写入失败: (%d)\n",res);
			}			
			f_close(&fp);	 
		}
	  else
	  {
			printf("\r\n文件打开失败,失败代码 = %d\r\n",res);				 
	  }
		 
		 //读文件
		 res = f_open(&fp, "1:dat1.txt", FA_OPEN_EXISTING | FA_READ);
		 if(res==FR_OK)
		 {
			 printf("\r\n打开文件成功!\r\n");	
			 res = f_read(&fp, readBuffer,sizeof(readBuffer),&fnum );
			 if (res==FR_OK) 
			 {
				 printf("\r\n 读取内容: %s \r\n", readBuffer);
			 }
			 else
			 {
					printf("!!文件读取失败: (%d)\n",res);
			 }
		 }
		 else
		 {
			 printf("\r\n文件打开失败,失败代码 = %d\r\n",res);	
		 }
		 f_close(&fp);
	}
	else
	{
		printf("\r\n设备挂载失败,失败代码:error = %d\r\n",res);
		while(1);
	}
}

void cache_enable(void)
{
    /* enable i-cache */
    SCB_EnableICache();

    /* enable d-cache */
    SCB_EnableDCache();
}

 

2.10、main.h

#ifndef __MAIN_H__
#define __MAIN_H__

#include "gd32h7xx.h"
#include "gd32h759i_eval.h"
#include "gd32h759i_lcd_eval.h"
#include "systick.h"
#include <stdio.h>
#include "stdlib.h"
#include "string.h"

#include "led/led.h"
#include "usart/usart.h"
#include "sdcard/sdcard.h"
#include "sdcard/sd.h"
#include "ff.h"


typedef signed char         i8;
typedef signed short        i16;
typedef signed int          i32;
typedef unsigned char       u8;
typedef unsigned short      u16;
typedef unsigned int        u32;
typedef unsigned long long  u64;

#endif 

 

三、运行

 

3.1、串口输出

插入格式化FAT32格式的TF卡,下载程序后,复位开发板,串口输出:

 

创建文件写入的内容和从文件中读取的内容。

 

3.2、在TF卡上生成的文件和内容

 

最新回复

谢谢   详情 回复 发表于 2024-7-16 13:33
点赞 关注
 
 

回复
举报

6992

帖子

11

TA的资源

版主

沙发
 

看楼主,文件系统移植非常熟练呀!感谢分享。

点评

移植花费了些时间  详情 回复 发表于 2024-6-6 10:21
 
 
 

回复

441

帖子

3

TA的资源

纯净的硅(高级)

板凳
 
lugl4313820 发表于 2024-6-6 10:15 看楼主,文件系统移植非常熟练呀!感谢分享。

移植花费了些时间

 
 
 

回复

194

帖子

4

TA的资源

纯净的硅(初级)

4
 

项目能不能发一下,我自己移植的时候移植绑定不了System。 然后把我的代码全部替换成你的代码之后。 在读sector的时候 错误代码返回并不是Res_ok. 返回了12. 我想找一下这个问题到底在哪里

点评

我测试的源代码:  详情 回复 发表于 2024-7-15 22:18
 
 
 

回复

441

帖子

3

TA的资源

纯净的硅(高级)

5
 
御坂10032号 发表于 2024-6-30 02:34 项目能不能发一下,我自己移植的时候移植绑定不了System。 然后把我的代码全部替换成你的代码之后。 在读se ...
我测试的源代码: gd32h759_prj_eeworld_fatfs_015.rar (2.93 MB, 下载次数: 12)

点评

谢谢  详情 回复 发表于 2024-7-16 13:33
 
 
 

回复

194

帖子

4

TA的资源

纯净的硅(初级)

6
 
TL-LED 发表于 2024-7-15 22:18 我测试的源代码:

谢谢

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表