原创 基于CH376的SD卡调试记录

2010-10-14 15:27 9540 1 5 分类: MCU/ 嵌入式

基于CH376的SD卡调试记录(基于NIOS II)


                                    


一.调试计划:


1.      创建文件。


2.      向文件中写入数据


3.      打开文件


4.      读取文件


5.      删除文件


6.      枚举文件


7.      向已有文件中追加数据


8.      查询SD卡总容量


9.      查询SD卡剩余容量


10.  查询文件大小


 


二.调试记录


8月19日:


上午调试记录


读取SD容量,调用CH376DiskCapacity()函数,在FILE_SYS.H中需宏定义#define EN_DISK_QUERY。返回的是磁盘总扇区数,转化成字节时需乘以512字节。


 


用FileCreatAndWrite()函数创建文件并向文件中写入数据,提示写入成功。


该FileCreatAndWrite()函数中创建文件使用函数CH376FileCreate(),既向当前目录下创建文件,文件命名格式:filename=”xxx.txt”。向文件中写入数据使用函数CH376ByteWrite( pfiledata, size, NULL )。


 


打开文件读取数据采用FileOpenAndRead(ref_u8 *pfilename,ref_u8 *pbuffer,ref_u8 size)函数,其中文件名格式filename=”xxx.txt”,即只能打开当前目录下的文件打开文件读取数据时出现问题:a.有时显示文件没找到,b.有时文件打开成功,但返回数据长度不正确,察看数据缓冲区发现数据已丢失。


 


下午调试记录


先前已将文件建立好,试着注释掉创建文件并向文件中写入数据那段函数之后,能成功打开文件并正确读取数据。(注:其实后来也不知道什么原因能读出来的,当时文件名是小写,所以本身操作是不合规定的。)


 


采用结构体数组的形式定义5组文件名,5组数据,创建5个文件,并向创建好的文件中写入定义好的数据,5组数据分别为10字节,15字节,20字节,25字节,30字节,包含字母与数字。先执行FileCreatAndWrite()函数,成功创建5个文件并分别向其中写入相应数据,注释掉该部分内容,执行FileOpenAndRead()函数,成功打开刚建的5个文件,正确返回相应的字节数。


 


将两部分内容连起来,既先调用FileCreatAndWrite()函数接着调用FileOpenAndRead()函数,第一部分能成功进行,提示“创建并数据写入成功”,但打开文件时提示“文件读取失败”,返回代码0x42(未找到文件提示错误),之间需要延时?延时2秒,还是那错误……


 


后来将创建文件的函数改成CH376FileCreatePath(),打开文件的函数改成 CH376FileOpenPath(),既支持多路径下的文件操作后,上述问题解决,注意此时字符串格式为”/xx/xx.xx”。(其实两个函数的区别无非就是文件名关键字的提取,CH376FileCreatePath()分析文件路径,打开一层层目录之后,打开文件,CH376FileCreate()只能打开当前目录下的文件,至于为什么出错还真查不出原因,先留个记号 。)注:在8月23日调试记录中有说明。


 


晚上调试记录


准备在早上程序的基础上添加向文件中写入大数据量的功能,手册上说建议单次写入字节数尽量是扇区大小512 的倍数,又最大字节数不超过 65535,综合考虑选择一次能写入的最大字节数为65024。(对于读写操作,SD卡只能进行字节操作,以字节为单位的文件读写子程序,占用RAM 相对较少,能够自动处理文件长度,使用较为方便。尽量缓冲和集中多个零碎数据,然后合并起来成批成块写入,减少擦写次数。建议单次写入字节数尽量是扇区大小512 的倍数,避免频繁地向U 盘中的文件写入零碎的数据,那样会缩短U盘或者SD 卡中闪存的使用寿命(因为闪存只能进行有限次擦写))


 


8月20日


上午调试记录


读取文件数据得到的字节数之前是未知的,可以函数通过返回值来判断是否已将文件数据全部读完,但写入的字节数如果也是未知的呢,(不高兴去统计要写入多少字节)有办法去判断吗?


 


当准备一10字节的字符串,但我要求写入20字节,最后返回多少字节呢?后来用读文件数据函数去执行,返回20字节数据,由调试输出可以看到从11字节开始都为0x00,看来你规定写多少字节他就写多少字节,而且CH376的读字节命令并不以结束符结束的。想到CH376ByteWrite( buf, ReqCount, RealCount )函数中形参RealCount在例程中都是NULL,只是一个空指针,但他有什么用呢,察看沁恒的源函数库发现他返回写入字节长度,它能主动识别出正确的字符串长度吗,经调试发现他返回的数据也为20,既仍是要求写入的字节数,并不是实际的字节数,暂且想到的方法是在执行向文件写入数据函数之前,用strlen() 测试一下需写入的字符串长度与实际要求写的字节数比较,如果小于实际要求的字节数,则将测得的字符串长度付给要求的长度变量,经测试此种方法可行。


 


下午调试记录


后来发现一种情况导致推翻了上午的方案,strlen()是不计”\0”的长度的,既遇到0x00就停止了,当那一串字符串中有0 时,strlen()会做出错误的判断。所以上午的方案不可行。


 


发现在沁恒的说明文档上对文件名的书写有一定的要求:路径名和文件名的格式与DOS 文件名格式相同,但是不含盘符和冒号,左斜杠与右斜杠等效,所有字符必须是大写,不能使用通配符,文件名长度不超过11 个字符,其中主文件名不超过8个字符,扩展名不超过3个字符,如果有扩展名,则用小数点与主文件名隔开。在SD卡的FAT表中,文件名都是以大写字母的ASCLL码来表示的,如果小写芯片可不会智能识别。在此我有些疑问了,之前没注意到大写这一点,我文件名都用的小写,并且都能正常读写,不知什么原因了,而且还发现相同的文件名大写和小写CH376会认为是两个文件,所以肯定一个文件名是非正常写入SD卡的FAT 表的,由于公司提供的SD卡是焊死在电路板上的,我也不能从电脑上看看文件名的效果。(对于CH376处理小写的文件名以大写的形式存入SD卡是有例程说明的)


 


8月23号


调试记录


       对于CH376写数据处理数据长度问题,咨询了沁恒公司,他们的回答是:CH376没有检测写入字符串长度的功能,所以CH376ByteWrite( buf, ReqCount, RealCount )中,ReqCount,即写入的长度,必须是明确的,对于要求写的长度大于实际字符串长度时,多于的长度会以’\0’填充,并且在调用CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount )时,’\0’是算作数据长度的。


       进行了文件枚举的操作,查找根目录下的JPG文件,在xWriteCH376Cmd( CMD0H_FILE_OPEN );后返回的数据不是USB_INT_DISK_READ(0x1D),而是0xa2,这个代码CH376命令集中并未宏定义,不能很明显的看出错误原因。


       因为采用的是SPI模式,所以在每次发送完写命令后需要调用xEndCH376Cmd( )函数,使片选无效,结束CH376命令,开始忘了调用该函数导致返回错误码0xa2的。


       在8月19日的调试中遇到这样的问题CH376FileCreatePath(),CH376FileOpenPath()能正常工作,CH376FileCreate(),CH376FileOpen()不能正常工作其实还是文件名大小写的问题,文件名必须大写,CH376FileOpen(),文件名写成”/XX.XXX”,CH376FileOpenPath()文件路径写成”/XX/XX.XX”,两者’/’都不能少。


 


8月24号


调试记录


       写个两个关于查询磁盘剩余容量大小,查询文件大小的函数,准备在创建文件之前,先查询一下剩余容量,若有足够的空间才创建文件,在打开文件之后,查询一下文件的大小,判断依据,剩余空间的大小是否能容纳写入文件数据量的10倍大小(具体倍数应按实际情况确定)。


 


       在查询剩余容量时调用函数CH376DiskQuery( DiskFre ),发现调用该函数,很费时间,查了一下手册,手册上说“查询磁盘剩余空间信息,该子程序在FAT32文件系统的磁盘中调用时最快,在FAT16 文件系统的磁盘中调用时最慢,磁盘容量越大,操作越慢”。当时测试芯片格式化的时候,选择的是格成FAT16文件系统,可以在格式化的时候选择格成FAT32试下


 


       在几个较小数据的文件创建好以后,扇区数一直没变,不知怎么回事歪?


       调了很长时间,发现其实我建好文件以后并没有删除,每次重新编译,下载,运行时 ,建立文件也只是覆盖原有文件,所以读出的剩余扇区数就一直不变了,将建立的文件删除后重新编译,下载 ,运行,可以看到剩余扇区数是有变化的,只是写入的实际数据大小与占用的扇区数是有差别的,这与SD卡的FAT表的结构有关。


 


8月26日


调试记录


测试了CH376对于SD卡大数据量数据的读写,向SD卡中写入10.1M的数据,发现创建文件写数据时,用先前的FileCreatAndWrite(ref_u8 *pfilename,ref_u8 *pfiledata,ref_u32 size)和FileOpenAndAddWrite(ref_u8 *pfilename,ref_u8 *pfiledata,ref_u32 size)效率非常低,每次执行FileOpenAndAddWrite()会重复调用FileOpen(),FileLocate()函数。于是修改了一下程序,按照第一次调用CreatFile();之后重复调用FileWrite()函数,最后关闭文件。经测试10M数据全部写向SD中的文件共花费了80秒左右。读函数已不能用FileOpenAndRead()因为没有足够的缓存来存储读到的数据,应先调用FileOpen()函数,之后反复调用FileRead()函数直至读完,在此期间可对缓存区的数据进行处理。经测试将10M的数据全部读完共花费60秒左右(不包括中间对数据处理的时间)


 


CH376调试中需注意的问题及一些说明:


1.    SD卡格式化时,试着格式化成FAT32格式的,在手册上说:“查询磁盘剩余空间信息,该子程序在FAT32文件系统的磁盘中调用时最快,在FAT16 文件系统的磁盘中调用时最慢,磁盘容量越大,操作越慢”。如果要查询磁盘剩余空间,使用FAT16会很慢(调试时,看到打印调试信息明显有停顿感,FAT32没有测试。)


 


2.    调用总容量函数和剩余容量函数时返回的是扇区数,整成字节时需乘以512.


单次写入字节数尽量是扇区大小512 的倍数,最大字节数不超过 65535。


 


3.    路径名和文件名的格式与DOS 文件名格式相同,但是不含盘符和冒号,左斜杠与右斜杠等效,所有字符必须是大写,不能使用通配符,文件名长度不超过11 个字符,其中主文件名不超过8个字符,扩展名不超过3个字符。CH376FileOpen(),文件名写成”/XX.XXX”,CH376FileOpenPath()文件路径写成”/XX/XX.XXX”,两者’/’都不能少。


 


4.    CH376没有检测写入字符串长度的功能,所以CH376ByteWrite( buf, ReqCount, RealCount )中,ReqCount,即写入的长度,必须是明确的,对于要求写的长度大于实际字符串长度时,多于的长度会以’\0’填充,并且在调用CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount )时,’\0’是算作数据长度的。(不能使用strlen()来测试要写入数据缓存区的数据长度)


 


5.对于向文件中写入或者读出大数据量的数据(10M数据),调试输出过程中会有一个较长时间的停顿,属正常情况。


 


6.对于向SD卡中写数据,一次最大到底应该写多少数据,应该根据实际情况而定,手册上说是最大不超过65535个字节,最好一次写入的字节数是512的倍数。开始我设定的是65024,即在那个允许的范围内,选择512整数倍的最大数。后来在调试过程中我发现为了能方便进行移位操作(程序中直接用除法,效率不高)还应满足这个数是2的幂,后来将65024改成了32768,这个数值只能作为一个参考,具体数值应根据能分配的数据缓存大小确定。


 


7.对于从文件读数据,一次能读出的最大字节数也应该根据能分配的数据缓存大小确定,我设定的一次最大能读出512字节的数据。


 


程序的说明:


1.      我主要在ref_main.cpp,ref_usb.cpp,ref_usb.h三个文件中添加的定义及函数,对于添加的函数及函数中的一些关键语句,都有注释。


2.读写缓存的大小,我写了两个宏定义(#define WRITE_MAX_BUF 32768


#define READ_MAX_BUF 512,在ref_usb.h中定义)修改容量时只需修改这两个数值。


3.写大量数据时,应先调用FileOpenPath(()函数,文件打开成功之后,反复调用FileWrite()函数,直至将所要写入的数据写完,最后调用FileClose(TRUE)函数,关闭文件并更新文件长度。


4.读文件中的数据时,应先调用CreatFile()函数,成功之后,反复调用FileRead()函数,直至将文件中的数据读完,最后调用FileClose(FALSE)函数,关闭文件不更新文件长度。


 


 

PARTNER CONTENT

文章评论4条评论)

登录后参与讨论

用户377235 2014-8-8 14:17

可以把调好的代码,传上来,让我们学习一下吗?

用户235506 2011-2-23 16:34

您好,请问可以把你的初始化部分代码给我(muhp3@163.com)发一份吗?我参考一下,谢谢!

用户303775 2010-11-1 10:51

建议参考沁恒官方针对CH376的说明文档及函数库,在此基础上再添加自己的功能函数,呵呵,如果没有参考官方的函数库的话,自己从头开始写会遇到很多困难(CH376的说明文档很多地方说的不清楚),之前我做ch376和U盘通讯的时候不知道有函数库,也走了很多弯路哈~~~

用户308612 2010-10-28 21:25

你好,看到了这篇文章,心里有一丝喜悦,因为我也正在学习用 CH376S 来控制 SD 卡。但进展很慢,哈哈!说出来真是不好意思! 不知道你是否愿意发给我一个用 CH376 控制 SD 卡的参考程序? 我的邮箱是 ES2000@126.com 。 QQ 是 1115311610 也希望能有机会和你交流一下。谢谢!
相关推荐阅读
用户303775 2011-03-03 16:25
配置TFTP服务
配置TFTP服务 TFTP一般用于向目标板下载镜像文件TFTP是用来下载远程文件的最简单网络协议,它其于UDP协议而实现。嵌入式linux的tftp开发环境包括两个方面:一是linux服务器端的tft...
用户303775 2011-02-14 09:32
GNU-ARM 汇编指令
第一部分 Linux下ARM汇编语法尽管在Linux下使用C或C++编写程序很方便,但汇编源程序用于系统最基本的初始化,如初始化堆栈指针、设置页表、操作 ARM的协处理器等。初始化完成后就可以跳转到C...
用户303775 2011-01-11 15:23
[转]U_boot 的 bootcmd 和bootargs参数详解
转自 :http://linux.chinaunix.net/bbs/archiver/tid-1111568.html     U-boot的环境变量值得注意的有两个: bootcmd 和boota...
用户303775 2011-01-11 15:20
解决无法挂载Linux文件系统的问题
要点如下: 1. 在正确的位置烧写正确格式的文件系统映象: 2. 内核支持这种文件系统格式 3. 文件系统的内容要完备上面说得简单,一个个介绍。 1. 在正确的位置烧写正确的文件系统映象: (a). ...
用户303775 2010-12-02 01:09
蓝牙鼠标之我见
今天偶然得到一个ADVENT的蓝牙鼠标,入手之后就仔仔细细的把玩了一番,玩的过程中觉得这鼠标挺有意思的,就忍不住写篇日志纪念下了O(∩_∩)O哈!    这鼠标挺奇怪的,需两节7号电供电,却找不到一个...
用户303775 2010-10-29 15:29
cdev结构体及其相关函数
(1)在Linux2.6内核中一个字符设备用cdev结构来描述,其定义如下: struct cdev {         struct kobject kobj;         struct mod...
EE直播间
更多
我要评论
4
1
关闭 站长推荐上一条 /3 下一条