一、SDMMC简介
经过资料搜集,发现没有SDMMC卡或者是SDMMC接口,资料上出现SDMMC可能要表达的意思是SD/MMC,或者SDMMC是数字安全记忆卡(SecureDigital Memory Card)的简称,即SDMMC就是SD卡。因此介绍一下SD与MMC。
a、SD卡
SD卡,数字安全记忆卡(Secure Digital Memory Card),是用于移动设备的标准记忆卡。SD卡数据传送和物理规范由MMC发展而来,大小和下文提到的
MMC差不多。长宽和MMC一样,比MMC稍微厚了一点。兼容性方面SD卡向下兼容多媒体卡(Multi Media Card)。
SD有9pin:

SD卡也有SD和SPI两种工作模式,在各个工作模式下引脚定义如下:
引脚号 | 名称 | 功能(SD模式) | 功能(SPI模式) |
1 | DAT3/CS | 数据线3 | 片选 |
2 | CMD/DI | 命令线 | 片选/从选(SS) |
3 | VSS1 | 电源地 | 主出从入(MOSI) |
4 | VDD | 电源 | 电源地 |
5 | CLK | 时钟 | 时钟(SCK) |
6 | VSS2 | 电源地 | 电源地 |
7 | DAT0/DO | 数据线0 | 主入从出(MISO) |
8 | DATI/IRQ | 数据线 1 | 保留 |
9 | DAT2/NC | 数据线2 | 保留 |
b、MMC
MMC的全称是”MultiMediaCard”――所以也通常被叫做”多媒体卡”,是一种小巧大容量的快闪存储卡,特别应用于移动电话和数字影像及其他移动终端中。
MMC存贮卡只有7pin,可以支持MMC和SPI两种工作模式,或者换句话说:MMC是一种通信协议,支持两种模式SPI和MMC。MMC模式是标准的默认模式,具有MMC的全部特性。而SPI模式则是MMC存贮卡可选的第二种模式,这个模式是MMC协议的一个子集。
如下图为MMC在各个工作模式下的引脚定义:

二、SDMMC驱动(基于FMQL开发环境,ARM开发可参考)
2.1、SD/MMC初始化
首先配置好DMA(应该是可进行可不进行):
首先各个结构体初始化在主函数外进行
FDmaPs_T g_DMA_dmac;
FDmaPs_Param_T g_DMA_param;
FDmaPs_Instance_T g_DMA_instance;
在初始化函数内配置指针指向句柄
FDmaPs_T *pDmac = &g_DMA_dmac;
FDmaPs_Instance_T *pInstance = &g_DMA_instance;
FDmaPs_Param_T *pParam = &g_DMA_param;
FDmaPs_Config *pDmaCfg;
2、初始化中断控制器,配置好中断:
FGicPs_SetupInterruptSystem(&IntcInstance);
FMSH_ExceptionRegisterHandler(FMSH_EXCEPTION_ID_IRQ_INT,
(FMSH_ExceptionHandler)FGicPs_InterruptHandler_IRQ,&IntcInstance);
接下来就是查找设备ID,配置DMA等一系列操作最后设置SD/MMC工作模式为DMA。
3、为每个分区都挂载一下FAT文件系统
FAT是文件分配表 (FileAllocation table)的缩写,FAT32指的是 文件分配表 是采用32位二进制数记录管理的磁盘文件管理方式,因FAT类文件系统的核心是文件分配表。
接着进行物理驱动分区:
Res = f_fdisk(ulPhyDriveNo, plist, work);
里面的参数分别是物理磁盘号、指向每个分区的大小表的指针、指向工作缓冲区的指针(null:使用堆内存)。
为每个分区创建FAT32文件:
FRESULT f_mkfs (
const TCHAR* path, BYTE opt, DWORD au, void*work, UINT len)
里面参数分别是:逻辑盘号、格式选择、分配单元大小(集群)[byte]、指向工作缓冲区的指针(null:使用堆内存)、工作缓冲区的大小[字节]
挂载(或是卸载)逻辑盘驱动
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt)
里面的参数分别是:指向文件系统对象(NULL: unmount)的指针、要挂载/卸载的逻辑驱动器号、模式选项(0:不挂载(延迟挂载),1:立即挂载)。
最后可以通过(void)show_partition_usage(ulPhyDriveNo,ulPartitionNum)
该函数可以查看各个磁盘的分区内存容量信息,前者是磁盘号后者是分区号。
2.2、文件扫描
1、创建并且打开目录的对象:
FRESULTf_opendir (DIR* dp, const TCHAR* path)
第一个参数是,指向目录的指针,第二个是目录路径的指针。
2、读取目录项:
FRESULTf_readdir ( DIR* dp, FILINFO* fno)
参数分别为指向打开目录对象的指针、返回指向文件信息的指针。
接下来判定读取到的内容。if (fno.fattrib & AM_DIR)。目录文件的属性位与文件属性位与操作(结果大于等于1时,为文件;否则为目录)。

读取到的是目录时以字符串的形式输出路径末尾文件名,读取到的是文件时输出文件参数信息。最后检索不到目录或者文件则关闭目录。
2.3、目录扫描
1、创建并且打开目录的对象:
FRESULT f_opendir (DIR* dp, const TCHAR* path)
第一个参数是,指向目录的指针,第二个是目录路径的指针。
2、读取目录项:
FRESULT f_readdir ( DIR* dp, FILINFO* fno)
参数分别为指向打开目录对象的指针、返回指向文件信息的指针。
接下来判定读取到的内容:if (fno.fattrib & AM_DIR)。目录文件的属性位与文件属性位与操作(结果大于等于1时,为目录;否则为文件)。

读取到的是目录时输出文件参数信息,以字符串的形式输出路径末尾文件名并且通过递归重新读取,读取到的是文件时不进行操作,最后关闭目录。
2.4、物理驱动创建分区表
使用FRESULT f_fdisk (BYTE pdrv,const DWORD* szt,void* work)划分物理磁盘。
参数:物理磁盘号、指向每个分区的表的容量的指针、指向工作缓冲区的指针(null:使用堆内存)
2.5、删除单个文件
使用FRESULT f_unlink(const TCHAR* path )删除指定文件。
参数:指向文件或目录路径的指针

2.6、删除某个目录及其子目录下的所有文件
1、首先将文件或目录路径写进设定好的缓冲区charpath_buf[]
strcpy(path_buf,path);
2、打开/生成目录文件,判断是否开启成功。
3、读取目录/文件。判断是否读取成功。
4、判断读取到的是文件还是目录,如果读取到的是目录将当前文件名输出到path_buf[]里
例如:从xx/dd/cc开始检索在cc下面检索到了mm那么便通过
i = strlen(path_buf);
sprintf(&path_buf[i], "/%s",fno.fname);
将存储写入path_buf末尾,里面也就成了xx/dd/cc/mm了,接下来就是目录检索喜闻乐见的递归啦,重新来过,继续检索,一直到最后读取到最后一个文件,删除,重复操作,最后目录里面啥也没有循环也就退出了,检索不到东西,读取函数读取失败,最终目录就关闭了,结束运行。
2.7、删除目录及其子目录
删除path指针指定的空目录及其子目录,确保目录下没有文件,否则会删除失败。
1、与2.6操作相同
TCHAR* pfname_buf = path_buf;
strcpy(pfname_buf,path);将路径写入字符串。
2、打开/生成目录文件,判断是否开启成功。
3、读取目录/文件。判断是否读取成功。
4、判定目录下为空:if(fno.fname[0]== 0)
关闭目录并且将该空目录删除:rc =f_unlink(pfname_buf);
如果还能检测到子目录:
if(fno.fattrib & AM_DIR)
{
i = strlen(pfname_buf);
sprintf(&pfname_buf[i],"/%s", fno.fname);
rc = rm_all_empty_dir(pfname_buf);

也是与2.6操作一致将文件名加到缓冲区,使用递归继续检索一直到目录打开失败也就是完全删除,退出循环,最后关闭目录。
2.8、逻辑分区的容量信息及剩余容量信息
首先给两个磁盘,一共8个分区编号:
TCHAR *Path[] = {"0:","1:","2:","3:","4:","5:","6:","7:"};
使用函数(函数功能:获取空闲簇的数目)
RESULT f_getfree (
onst TCHAR* path, DWORD* nclst, FATFS** fatfs)
函数参数:
逻辑驱动编号、指向一个变量的指针(该变量返回空闲集群的数量)、回指向相应文件系统对象指针的指针。
用该函数获取每个分区剩余的容量信息后:
得到总扇区数 tot_sect= (fs->n_fatent - 2) * fs->csize;
得到空闲扇区数 fre_sect = fre_clust * fs->csize;
fs->n_fatent– 2:所有簇的数量
csize: 每簇中的扇区数
fre_clust:空闲簇的数量
2.9、通过IAR jlink上传主机上的bin文件到sdmmc中指定的目录
1、以只读的形式打开二进制文件(如果该文件不存在则打开失败)fptr = fopen(host_file,"rb");
2、判断文件是否为空(空则退出)
使用函数intfseek(FILE *stream, long int offset, int whence)操作文件指针。
函数参数:
stream :这是指向 FILE 对象的指针,该 FILE 对象标识了流。
Offset :这是相对 whence 的偏移量,以字节为单位。
Whence :这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
SEEK_SET 文件的开头
SEEK_CUR 文件指针的当前位置
SEEK_END 文件的末尾
使用fseek操作:
fseek(fptr,0,SEEK_END);
fileLen= ftell(fptr);(ftell:获取指针当前位置的函数。参数为文件对象指针。)
fseek(fptr,0,SEEK_SET);
这一步就是将指针移到末尾,使用ftell获取当前指针位置由于当前指针在文件末尾,返回值是相对于文件首地址的偏移量,这样就巧妙地获取了文件的大小,最后再通过fseek()函数把文件指针移回首地址。
3、照着host_file文件的大小申请一块内存空间,并且使用fread()函数将数据读到pbuf中:
pbuf= malloc(fileLen);
rdsize= fread(pbuf,1,fileLen,fptr);
size_t fread( void *buffer, size_tsize, size_t count, FILE *stream );
该函数从给定流stream 读取数据到buffer 所指向的数组中。
void*buffer 参数 : 将文件中的二进制数据读取到该缓冲区中 ;
size_tsize 参数 : 读取的 基本单元 字节大小 , 单位是字节 , 一般是buffer 缓冲的单位大小 ;
如果buffer 缓冲区是 char 数组, 则该参数的值是 sizeof(char) ;
如果buffer 缓冲区是 int 数组, 则该参数的值是 sizeof(int) ;
size_tcount 参数:读取的基本单元个数 ;
FILE*stream 参数:文件指针 ;
size_t返回值 : 实际从文件中读取的基本单元个数 ; 读取的字节数是基本单元数基本单元字节大小 ;

4、使用rc =f_open(fp, dst_path, FA_CREATE_ALWAYS|FA_WRITE)创建文件,其中的FA_CREATE_ALWAYS|FA_WRITE(f_open与fopen是有区别的:fopen是stdio下的文件io接口,f_open是fatFS的接口,主要用来操作FAT文件)
Rc不等于0则创建文件失败。
5、使用函数rc =f_write(fp,(void*)pbuf, fileLen, &ulbw);将数据写入fb
如果rc ==0 或者是数据长度不相等都是烧写失败。
