SD卡(Secure Digital Memory Card)具有体积小、容量大、数据传输快、可插拔、安全性好等优点,被广泛应用于便携式设备上。例如作为数码相机的存储卡,作为手机、平板多媒体扩展卡用的TF卡(micro sd)等等。
1. SD卡概述
SD卡技术是在MMC卡的基础上发展起来的,其尺寸与MMC卡一样,只是比MMC卡厚了0.7mm,因此SD主机可以识别并存取MMC卡。SD卡接口除了保留MMC卡的7针外,还在两边加了2针,作为数据线,目的是通过把传输方式由串行变成并行,以提高传输速率。此时的规范为SD1.0版本,最高容量只能到4GB。为了跟进产品的更新换代,SD联合协会在06年发布了容量更大、存储更快的下一代SD卡规范SD2.0。该规范重新定义了SD卡的速度等级,分为三档:Class 2、4、6,分别对应写入速度2MB/S、4MB/S、6MB/S。根据卡容量又分为标准卡(小于2GB)和高容量卡(2GB32GB),目前市面上应用的SD卡绝大部分都是SD2.0版本的卡。为了让储存卡更加迷你,通过SD卡规范标准,又衍生了MiniSD卡和Micro SD卡,这些卡均比标准SD卡尺寸小,通过SD转接套可以当作一般的SD卡使用。尤其是Micro SD卡,可以算是最小的存储卡了,超小的体积可以极大的节省消费电子产品内部设计的空间,基本目前的Android手机均是选用Micro SD卡作为多媒体扩展储存卡。随着科技的进步,SD2.0规范SD卡也渐渐无法满足应用的需求,在10年SD联合协会又发布了新的SD3.0规范,该规范定义了sdxc和uhs,并增加了Class10,容量范围为32GB2TB。在sdxc卡仍需进一步坐等其价格下降的情况下,SD4.0规范已经开始在紧张的制订中,这已超出本文的讨论范围内了。
2. SD卡驱动
SD卡共支持三种传输模式:SPI模式、1位SD模式、4位SD模式。所有的SD卡都必须支持较老的SPI/MMC模式,这个模式支持慢速的四线SPI接口,使很多微控制器都可以通过SPI或模拟SPI接口来读写SD卡。万利的LPC5411x开发板通过SPI接口扩展了一个TF卡槽,可以用SPI接口读写TF卡。
SD2.0标准定义了物理层相关规范以及主机控制器规范,SD卡驱动的编写必须参考这两个规范,遵循标准的SD卡均可以采用统一的软件驱动实现数据访问。NXP对于其全系列的芯片提供了sdmmc库中间件用于支持SD/MMC卡的读写,可以在官网下载相应的BSP,里面包含sdmmc库,fsl_sdspi.h/fsl_sdspi.c即为SPI模式访问SD卡的标准驱动,可以直接应用到LPC5411x开发环境中。SD卡驱动最主要实现三个接口,分别是SD卡的识别初始化、SD卡的块读、SD卡的块写,具体实现可以参考fsl_sdspi.h/fsl_sdspi.c驱动文件。
3. Fatfs
数据往往以文件的形式保存在储存设备中,对于SD卡,一般采用的是Fat32文件系统,Fatfs由于其开源免费,支持Fat32,受到了广泛的应用。
Fatfs是由日本工程师ChaN所编写的Fat文件系统模块,从06年发布第一个Fatfs版本开始,作者就从未停止维护和更新。Fatfs的编写遵循ANSI C,并且完全与磁盘I/O层分开。它不依赖于硬件架构,代码和工作区占用空间小,使之可以嵌入到各个低成本的微控制器中,如AVR、8051、PIC、ARM、Z80、68K等。由于SD卡一般使用Fat32文件系统,在使用到SD卡的系统中移植Fatfs,将很好地实现对SD卡文件的管理。
Fatfs模块完全独立于磁盘I/O层,因此底层磁盘I/O访问并不属于Fatfs的模块部分,用户必须自己实现这部分用来访问存储设备。通常在diskio.c中实现这几个函数disk_initialize()、disk_status()、disk_read()、disk_wirte()、disk_ioctl()即可,如果使能了OS相关的特性,则还需额外实现进程/内存函数。其中disk_initialize()对应SD卡驱动中的卡识别初始化接口,disk_read()对应SD卡的块读接口,disk_wirte()对应SD卡的块写接口。NXP对于其全系列的芯片提供了Fatfs中间件的支持,Fatfs对应SD卡驱动接口的具体实现可以参考BSP中已移植好的Fatfs中间件。
4. 读写测试
移植好SD卡驱动以及Fatfs底层对应接口后,就可以用Fatfs应用编程接口读写SD卡里面的文件。应用以2KB大小为读写单位,测试读写10MB大小文件的平均读写速度。
uint8_t TestBuffer[2048];
int main()
{
uint32_t i;
FATFS fs;
FIL file;
FRESULT Res;
uint32_t TimeCount;
uint32_t ByteWrite, ByteRead;
/* Board pin, clock, debug console init */
/*attach 12 MHz clock to FLEXCOMM0 (debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
/* enable clock for GPIO*/
CLOCK_EnableClock(kCLOCK_Gpio0);
CLOCK_EnableClock(kCLOCK_Gpio1);
BOARD_InitPins();
BOARD_BootClockFROHF96M();
BOARD_InitDebugConsole();
Gpio_Init();
f_mount(&fs, "4:" , 0);
/*
Res = f_mkfs("", 0, 4096);
if (Res != RES_OK) {
PRINTF("f_mkfs error %d\r\n",Res);
while(1);
}
*/
PRINTF("Writing test.bin, file sise10MB\r\n");
Res= f_open(&file, "4:test.bin", FA_WRITE | FA_CREATE_ALWAYS);
if (Res != RES_OK) {
PRINTF("Createfile failed\r\n");
while(1){
GPIO_TogglePinsOutput(GPIO,0, 1u << 15);
Delay_ms(300);
}
}
for (i=0; i<sizeof(TestBuffer); i++){
TestBuffer[i]= i;
}
TimeCount =timer_get_current_milliseconds();
for (i=0;i<10*1024*1024/sizeof(TestBuffer); i++) {
Res= f_write(&file, &TestBuffer, sizeof(TestBuffer), &ByteWrite);
if(Res != RES_OK) {
f_close(&file);
PRINTF("Writefile error\r\n");
while(1){
GPIO_TogglePinsOutput(GPIO,0, 1u << 15);
Delay_ms(300);
}
}
}
f_close(&file);
PRINTF("Sd write speed %dKB/s\r\n", 10*1024*1024/(timer_get_current_milliseconds()-TimeCount));
PRINTF("Reading test.bin, file sise10MB\r\n");
Res = f_open(&file, "4:test.bin", FA_READ | FA_OPEN_EXISTING);
if (Res != RES_OK) {
PRINTF("Openfile failed\r\n");
while(1){
GPIO_TogglePinsOutput(GPIO,0, 1u << 15);
Delay_ms(300);
}
}
TimeCount =timer_get_current_milliseconds();
for (i=0;i<10*1024*1024/sizeof(TestBuffer); i++) {
Res= f_read(&file, (unsigned char *)&TestBuffer, sizeof(TestBuffer),&ByteRead);
if(Res != RES_OK) {
f_close(&file);
PRINTF("Readfile error\r\n");
while(1){
GPIO_TogglePinsOutput(GPIO,0, 1u << 15);
Delay_ms(300);
}
}
}
f_close(&file);
PRINTF("Sd read speed %dKB/s\r\n", 10*1024*1024/(timer_get_current_milliseconds()-TimeCount));
while(1) {
GPIO_TogglePinsOutput(GPIO,0, 1u << 15);
Delay_ms(1000);
}
}
读写速度测试结果如下:
SD卡写速度为872 KB/S,读速度为1169 KB/S,SD卡通过SPI接口读写,SPI时钟采用FRO 12M时钟,因此这个读写速度是合适的。SD卡读写速度跟卡速度等级、数据传输速率有关,读写多块要比一块一块分多次读写快,此处测试一次性读取8块(512字节/块),2048字节,读写速度主要受限于SPI的传输速率,LPC5411x的SPI接口最高支持48M的时钟,SPI可以通过采用PLL时钟、内部高速时钟,进一步提高SPI的传输速率,从而进一步提高SD卡的读写速度。
5. 附录
MDK工程,包含SPI模式的SD驱动,Fatfs文件系统模块,SD卡读写速度测试应用例程。
源码:https://pan.baidu.com/s/1cMz1G6