8854|33

350

帖子

5

TA的资源

纯净的硅(初级)

楼主
 

SD卡测试程序教程 [复制链接]

 
本帖最后由 651076842 于 2014-2-27 16:52 编辑

一、实验目标:
该程序主要是通过串口调试助手来查看SD卡的信息,并对其中的文件进行打开与读取,并通过串口调试助手将内容以及一些必要的数据参数显示出来。
二、硬件设计
应用单片机读写SD卡有两点需要注意。首先,需要寻找一个实现单片机与SD卡通讯的解决方案;其次,SD卡所能接受的逻辑电平与单片机提供的逻辑电平不匹配,需要解决电平匹配问题。
1、通讯模式
SD卡有两个可选的通讯协议:SD模式和SPI模式。SD模式是SD卡标准的读写方式,但是在选用SD模式时,往往需要选择带有SD卡控制器接口的MCU,或者必须加入额外的SD卡控制单元以支持SD卡的读写。然而,单片机没有继承SD卡的控制器接口,若选用SD模式通讯就无形中增加了产品的硬件成本。在SD卡数据读写时间要求不是很严格的情况下,选用SPI模式可以说是一种最佳的解决方案。因为在SPI模式下,通过四条线就可以完成所有的数据交换,并且目前市场上很多MCU都集成有现成的SPI接口电路,采用SPI模式对SD卡进行读写操作可大大简化硬件电路的设计。而我们所用的STC12C5A60S2是有现成的SPI接口模块的。
2、电平匹配
SD卡的逻辑电平相当于3.3V TTL电平标准,而控制芯片STC12C5A60S2的逻辑电平为5V CMOS电平标准。因此,他们之间不能直接相连,否则会有烧毁SD卡的可能。处于对安全工作的考虑,有必要解决电平匹配问题。
要解决这一问题,最根本的就是解决逻辑器件接口的电平兼容问题,原则抓药有两条:一为输出电平器件输出高电平的最小电压值,应该大于接收电平器件识别为高电平的最低电压值;另一条为输出电平器件输出低电平的最大电压值,应该小于接收电平器件识别为低电平的最高电压值。
一般来收,通用的电平转换方案是采用类似SN74ALVC4245的专用电平转换新品,这类芯片不仅可以用作升压和降压,而且允许两边电源不同步。但是,这个方案代价相对昂贵,而且一般的专用电平转换芯片都是8路、16路或者更多路数的电平,相对本系统仅仅需要3路来说是一种资源浪费。
考虑到SD卡在SPI协议的工作模式下,通讯是单向的,于是在单片机向SD卡传输数据时采用上拉47K电阻的方案。而在SD卡向单片机传输数据时可以直接连接,因为他们之间的电平刚好满足上述的电平兼容原则,既经济又实用。

file:///C:\Users\larry\AppData\Local\Temp\ksohtml\wps_clip_image-26090.png
3.3V电源可以用AMS1117稳压管从5V电源稳压获取。
3、硬件接口设计
SD卡提供9Pin的引脚接口便于外围电路对其进行操作,9Pin的引脚随工作模式的不同有所差异。在SPI模式下,引脚1DAT3)作为SPI片选线CS用,引脚2CMD)用作SPI总线的数据输出线MOSI,而引脚7DAT0)为数据输入线MISO,引脚5用作时钟线(CLK)。除电源和地,保留引脚可悬空。
三、软件设计
1SPI工作模式与SD卡初始化
SD卡从上电到到对SD卡进行正确的读写操作,旺旺都需要一个上电初始化的过程。SD卡上电后,主机必须先向SD卡发送74个时钟周期,以完成SD卡的上电过程。通常SD卡上电后会自动进入SD总线模式,并在SD总线模式下向SD卡发送复位命令(CMD0)。因此,主机在对SD卡进行任何操作前,都必须先要拉低SD卡的片选信号CS,以使SD卡进入SPI总线模式,然后再由主机向SD卡发送命令。SD卡可以响应主机发来的各种应答信号,同事向主机发送一个特殊的数据响应标志。若主机读到的应答信号为01,即表明SD卡已进入SPI模式,此时,主机即可不断地向SD卡发送命令(CMD1)并读取SD卡的应答信号,直到应答信号为00,表明SD卡已完成初始化过程,并准备好接收下一命令。伺候,系统便可读取SD卡的各寄存器,以进行读写等操作。
本程序中调用下面的函数进行SD卡初始化
void znFAT_Device_Init()
{
SD_Reset();
SD_Init();
}
/******************************************************************
- 功能描述:复位SD卡,用到CMD0,使用SD卡切换到SPI模式
- 隶属模块:SD卡模块
- 函数属性:外部,供用户调用
- 参数说明:无
- 返回说明:调用成功,返回0x00,否则返回INIT_CMD0_ERROR (sd.h中有定义)
******************************************************************/
unsigned char SD_Reset()//SD卡复位,进入SPI模式,使用CMD0(命令0
{
unsigned char time,temp,i;
unsigned char pcmd[] = {0x40,0x00,0x00,0x00,0x00,0x95}; //命令0的字节序列
is_init=1; //is_init置为1
SD_CS=1;  //关闭片选
for(i=0;i<0x0f;i++) //初始时,首先要发送最少74个时钟信号,这是必须的!!!
{
  SD_spi_write(0xff); //120个时钟
}
SD_CS=0; //打开片选
time=0;
do
{
  temp=SD_Write_Cmd(pcmd);//写入CMD0
  time++;
  if(time==TRY_TIME)
  {
   SD_CS=1; //关闭片选
   return(INIT_CMD0_ERROR);//CMD0写入失败
  }
}while(temp!=0x01);
SD_CS=1; //关闭片选
SD_spi_write(0xff); //按照SD卡的操作时序在这里补8个时钟
return 0;//返回0,说明复位操作成功
}
/******************************************************************
- 功能描述:初始化SD卡,使用CMD1
- 隶属模块:SD卡模块
- 函数属性:外部,供用户调用
- 参数说明:无
- 返回说明:调用成功,返回0x00,否则返回INIT_CMD1_ERROR (sd.h中有定义)
******************************************************************/
unsigned char SD_Init()        //初始化,使用CMD1(命令1
{  
unsigned char time,temp;
unsigned char pcmd[] = {0x41,0x00,0x00,0x00,0x00,0xff}; //命令1的字节序列
SD_CS=0; //打开片选
time=0;
do
{
  temp=SD_Write_Cmd(pcmd);
  time++;
  if(time==TRY_TIME)
  {
   SD_CS=1; //关闭片选
   return(INIT_CMD1_ERROR);//CMD1写入失败
  }
}while(temp!=0);

is_init=0; //初始化完毕,将is_init设置为0,为了提高以后的数据传输速度

SD_CS=1;  //关闭片选
SD_spi_write(0xff); //按照SD卡的操作时序在这里补8个时钟
return(0); //返回0,说明初始化操作成功
}
2、文件系统初始化
1)什么是文件系统
操作系统中负责管理和存储文件信息的软件机构成为文件管理系统,简称文件系统。具体地说,它负责为用户建立文件,存入、独处、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
2)本系统采用的文件系统
本系统采用FAT32文件系统,该文件系统开源,免费,用途广泛,并且可以很好的和PC机兼容,该文件系统是个不错的选择。
3)FAT文件系统的使用
在使用FAT文件系统在SD卡上进行文件操作前,我们首先要做的就是将SD卡的扇区读写函数与FAT的存储设备进行挂接。
FAT中提供了两个用于标定存储设备的全局变量,Dev_NO是设备号,FAT通过它来选择存储设备驱动。pArg是一个指针,FAT通过pArg所指向的参数集合,来获取在标定的存储上操作文件所需要的重要参数。所以在进行SD卡与FAT进行挂接时,要进行定义,程序如下:
unsigned char Dev_No; //设备号
struct znFAT_Init_Arg *pArg; //用于指针文件系统参数集合的指针
znFAT_Init_Arg中有许多我们所需要的文件参数,比如说BPB所在扇区号,磁盘总容量等,所以我们需要我这个文件系统参数集合,定义一个结构体变量:
struct znFAT_Init_Arg Init_Arg_SDCARD; //文件系统参数集合,用于记录文件系统的重要参数
当然,我们还需要将pArg指向SD卡文件系统的参数集合,这样我们以后需要的参数就从这个参数集合中获取。程序如下:
pArg=&Init_Arg_SDCARD;        
而我们是要对SD卡进行读写操作,所以我们应当使得设备号Dev_NOSDCARD进行对接,当然,我们还可以使得设备号与UDISKCFCARD等进行对接,对接方法是:
Dev_No=SDCARD;        //设备号为SDCARDznFAT依照此设备号选择存储设备驱动
OK,对接之后,我们便可以对SD卡进行对接操作了。
3、串口、SD卡与文件系统的挂接
我们在将定义完成之后,我们便可以写主函数了。
我们需要串口调试助手来查看信息,所以我们要用到串口,那么我们就要对串口进行初始化,设备在进行使用的时候都需要进行初始化。同样的,SD卡存储设备也需要进行初始化,进入SPI通信模式。然后就是上面所说到的SD卡与FAT进行对接,最后进行文件系统的初始化。程序如下:
UART_Init(); //串口初始化        
znFAT_Device_Init(); //存储设备初始化
pArg=&Init_Arg_SDCARD;        //指针指向SD卡文件系统参数集合,znFAT将从这个集合中获取参数
Dev_No=SDCARD;        //设备号为SDCARDznFAT依照此设备号选择存储设备驱动        
znFAT_Init();        //文件系统初始化
在系统运行的时候,文件系统参数集合会写入一些参数,由于我们已经对串口进行了初始化,那么我们就可以通过串口调试助手来查看这些参数,程序如下:
UART_Put_Inf("总存储容量:(字节)",pArg->Total_Size);//从串口输出各种参数信息
UART_Put_Inf("每扇区字节:()",pArg->BytesPerSector);
UART_Put_Inf("每簇扇区:()",pArg->SectorsPerClust);
3、对SD卡进行读写操作
1)打开文件
FAT文件系统中已经编写了“打开文件”的函数,我们在使用中直接调用即可。我们来看看函数:
UINT8 znFAT_Open_File(struct FileInfoStruct *pfi,CONST INT8 *filepath,UINT32 item,UINT8 is_file)
pfiFileInfoStruct类型的结构体指针,用来装载文件的参数信息,比如文件的大小、文件的名称等等,以备后面使用。
filepath:文件的路径,支持任意层目录。
item:在文件中有通配符*或?的情况下,实际与之匹配的文件有多个,Item就是打开的文件的项数,比如符合通配条件的文件有6个,如果item = 3,那么此函数就会打开这6个文件中按顺序排号为3的那个文件(item文件编号从0开始)。
is_file:区别要打开的是文件不是目录。1:打开的是文件。0:打开的是目录。
打开文件函数将文件的相关参数信息装入到一个参数集合中,也就是FileInfoStruct类型的结构体变量,它们是操作文件的基础。
在我们使用znFAT_Open_File这个函数的时候,需要对文件参数集合进行定义。即:
struct FileInfoStruct FileInfo;
下面,我们来看看znFAT_Open_File这个函数是如何使用的:
znFAT_Open_File(&FileInfo,"\\test.txt",0,1)
&FileInfo即为指向文件参数集合;"\\test.txt"文件名,其中第一个‘\’为C语言的转义符,第二个‘\’才是真正的‘\’;0表示第1个文件,1表示是打开文件而不是打开目录。
当打开文件成功的时候,返回值为0。我们添加一个条件语句,返回值如果非0,表示打开失败,我们做一个失败处理;如果成功,那我们就从串口输出一些参数。程序如下:
if(!znFAT_Open_File(&FileInfo,"\\test.txt",0,1))//打开SD卡根目录下的test.txt文件
        {  
                UART_Send_Str("打开文件成功\n"); //从串口输出文件参数信息
                UART_Send_Str("文件名为:");
                UART_Send_Str(FileInfo.FileName);
                UART_Send_Enter();
                UART_Put_Inf("文件大小(字节)",FileInfo.FileSize);
                UART_Put_Inf("文件当前偏移量(字节)",FileInfo.FileCurOffset);

                UART_Send_Str("文件创建时间:\n");
                UART_Put_Num(FileInfo.FileCreateDate.year);UART_Send_Str("");
                UART_Put_Num(FileInfo.FileCreateDate.month);UART_Send_Str("");
                UART_Put_Num(FileInfo.FileCreateDate.day);UART_Send_Str("");
                UART_Put_Num(FileInfo.FileCreateTime.hour);UART_Send_Str("");
                UART_Put_Num(FileInfo.FileCreateTime.min);UART_Send_Str("");
                UART_Put_Num(FileInfo.FileCreateTime.sec);UART_Send_Str("");
                UART_Send_Enter();
else
        {
                UART_Send_Str("打开文件失败\n");
        }
当然我们也可以将"\\test.txt"改为多层目录,这个函数依然可以使用。但是我们建议,最好将要处理的文件放在根目录下,不要太多层,这样会损耗一些时间。
2)读取文件
FAT文件系统中,我们同样编写了“读取文件”的函数,使用中依然是直接调用,我们来看看函数:
UINT32 znFAT_Read_File(struct FileInfoStruct *pfi,UINT32 offset,UINT32 len,UINT8 *pbuf)
pfiFileInfoStruct类型的结构体指针,用来装载文件的参数信息,文件读取的过程中,此结构体中的相关参数会更新。比如文件的当前偏移量、文件的当前扇区等。
offset:要定位的偏移量,读取文件数据从这里开始,要小于文件的大小
len:要读取的数据长度,如果len+offset大于文件的大小,则实际读取的数据量是从offset开始到文件结束。
pbuf:数据缓冲区,读到的数据将放入其中。
“读取文件”函数将从文件的指定位置开始读取指定长度的数据,并将数据存入数据缓冲区,同时在读取数据的过程中,对文件信息参数集合进行更新。
使用“读取文件”函数的时候,用到数据缓冲区,那么我们需要在前面定义一个32为的数组,作为缓冲区,为:
unsigned char buf[32]; //数据缓冲区,用于存储读取的文件数据
下面我们来看看znFAT_Read_File这个函数是如何使用的:
znFAT_Read_File(&FileInfo,0,FileInfo.FileSize,buf)
&FileInfo即为指向文件参数集合;0为从0偏移量开始;FileInfo.FileSize为读取全部文件;buf为数据缓冲区。
同样,我们为函数添加一个条件语句,当然,该函数使用的前提是打开文件成功,所以需要嵌套进去。如果读取成功,则会返回读取到的数据实际的长度。否则进入错误处理语句。程序如下:
if(len=znFAT_Read_File(&FileInfo,0,FileInfo.FileSize,buf)) //读取文件数据到数据缓冲区
                {
                        UART_Send_Str("读取文件成功\n");        //串口输出读到的数据相关信息
                        UART_Put_Inf("共读出字节:",len);
                        UART_Put_Inf("文件当前偏移量(字节)",FileInfo.FileCurOffset);
                        UART_Send_Str("读到的内容是:\n");

                        for(i=0;i将读到的数据通过串口发出来
                        {
                                UART_Send_Byte(buf);
                        }     
                }
else
                {
                        UART_Send_Str("读取文件失败\n");
                }
我们让len的赋值为读取函数的返回量,所以我们需要在前面给len定义一下。
unsigned char i=0,len=0;
OK,将以上的程序进行整合,那么我们的程序便可以完成了。
四、结束语
该程序使用了模块化编写,有许多底层函数,我便不再一一说明,如果有什么问题,可以进入畅学电子网http//www.eeskill.com里的“模块化编写”小组进行提问以及讨论。
谢谢!程序在附件中,需要回复才能下载
游客,如果您要查看本帖隐藏内容请回复
游客,如果您要查看本帖隐藏内容请回复


此帖出自51单片机论坛

最新回复

学习一下   详情 回复 发表于 2023-9-28 08:56
点赞 关注(6)
个人签名成功者找方法,失败者找理由
 

回复
举报

350

帖子

5

TA的资源

纯净的硅(初级)

沙发
 
:
此帖出自51单片机论坛
 
个人签名成功者找方法,失败者找理由
 
 

回复

12

帖子

1

TA的资源

一粒金砂(初级)

板凳
 
好,看看学习学习
此帖出自51单片机论坛
 
 
 

回复

2

帖子

0

TA的资源

一粒金砂(初级)

4
 
看起来挺不错的  正好需要
此帖出自51单片机论坛
 
 
 

回复

14

帖子

0

TA的资源

一粒金砂(中级)

5
 
好,我回复,看看
此帖出自51单片机论坛
 
 
 

回复

7

帖子

0

TA的资源

一粒金砂(初级)

6
 

此帖出自51单片机论坛
 
 
 

回复

2

帖子

0

TA的资源

一粒金砂(初级)

7
 
正好需要,學習中!
此帖出自51单片机论坛
 
 
 

回复

11

帖子

0

TA的资源

一粒金砂(初级)

8
 
谢谢分享
此帖出自51单片机论坛
 
 
 

回复

86

帖子

1

TA的资源

一粒金砂(中级)

9
 
谢谢分享!!!!!!!!
此帖出自51单片机论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

10
 
很好,赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞赞
此帖出自51单片机论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

11
 
不错!感谢分享了!!!!!!!!!!!!
此帖出自51单片机论坛
 
 
 

回复

130

帖子

13

TA的资源

一粒金砂(高级)

12
 
51+ch375读写U盘超精简原程序(啊雨)
此帖出自51单片机论坛
 
个人签名good
 
 

回复

81

帖子

0

TA的资源

一粒金砂(初级)

13
 
学习学习~~
此帖出自51单片机论坛
 
 
 

回复

2

帖子

1

TA的资源

一粒金砂(初级)

14
 
希望可以学到东西
此帖出自51单片机论坛
 
 
 

回复

231

帖子

0

TA的资源

一粒金砂(中级)

15
 
跟FATFS有什么区别?

此帖出自51单片机论坛
 
 
 

回复

14

帖子

0

TA的资源

一粒金砂(初级)

16
 
感谢分享
此帖出自51单片机论坛
 
 
 

回复

8

帖子

0

TA的资源

一粒金砂(初级)

17
 
顶几个!!!!!!!!!!!!
此帖出自51单片机论坛
 
 
 

回复

15

帖子

0

TA的资源

一粒金砂(初级)

18
 
给个接线图呀,无图无真相
此帖出自51单片机论坛
 
 
 

回复

71

帖子

0

TA的资源

一粒金砂(高级)

19
 
不知道模拟spi能炒作不
此帖出自51单片机论坛
 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

20
 
谢谢,我需要这个
此帖出自51单片机论坛
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

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

 
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
快速回复 返回顶部 返回列表