本文主要介绍在zynq中通过xilffs库读写SD卡的一个例子,并给出在使用中遇到的问题
在Xilinx SDK的standalone已移植好了FatFs库(SDK中叫做xilffs),所以在SDK中添加xilffs库后就可以在程序中使用FatFs中各API来操作SD卡,该库支持FAT12, FAT16 and FAT32文件系统(本例将SD卡格式化为FAT32)。FatFs API详细说明可查看http://elm-chan.org/fsw/ff/00index_e.html。
zynq中PS部分已经集成了SD卡控制器,在PS部分需要打开SD卡控制器,并配置对应管脚(取决于开发板),本人用的开发板PS部分配置如下图
综合实现生成.bit后导出,lautch SDK进入SDK新建工程和BSP,由于用了xilffs库所以在BSP中需要选择打开xilffs,如下图
对于xilffs的设置如下图
enable_mmc选择false;fs_interface选择1;read_only选择false。可以参考后面Description描述来设置。本例使用如上默认配置即可。
c程序如下:
/*
* main.c
*
* Created on: 2016年8月20日
* Author: hsp
* 本文件实现SD写入一段字符串,然后从其中读出并打印到串口。
*
*/
#include
#include "platform.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "ff.h"
#include "xdevcfg.h"
static FATFS fatfs;
int SD_Init()
{
FRESULT rc;
rc = f_mount(&fatfs,"",0);
if(rc)
{
xil_printf("ERROR : f_mount returned %d\r\n",rc);
return XST_FAILURE;
}
return XST_SUCCESS;
}
int SD_Transfer_read(char *FileName,u32 DestinationAddress,u32 ByteLength)
{
FIL fil;
FRESULT rc;
UINT br;
rc = f_open(&fil,FileName,FA_READ);
if(rc)
{
xil_printf("ERROR : f_open returned %d\r\n",rc);
return XST_FAILURE;
}
rc = f_lseek(&fil, 0);
if(rc)
{
xil_printf("ERROR : f_lseek returned %d\r\n",rc);
return XST_FAILURE;
}
rc = f_read(&fil, (void*)DestinationAddress,ByteLength,&br);
if(rc)
{
xil_printf("ERROR : f_read returned %d\r\n",rc);
return XST_FAILURE;
}
rc = f_close(&fil);
if(rc)
{
xil_printf(" ERROR : f_close returned %d\r\n", rc);
return XST_FAILURE;
}
return XST_SUCCESS;
}
int SD_Transfer_write(char *FileName,u32 SourceAddress,u32 ByteLength)
{
FIL fil;
FRESULT rc;
UINT bw;
rc = f_open(&fil,FileName,FA_CREATE_ALWAYS | FA_WRITE);
if(rc)
{
xil_printf("ERROR : f_open returned %d\r\n",rc);
return XST_FAILURE;
}
rc = f_lseek(&fil, 0);
if(rc)
{
xil_printf("ERROR : f_lseek returned %d\r\n",rc);
return XST_FAILURE;
}
rc = f_write(&fil,(void*) SourceAddress,ByteLength,&bw);
if(rc)
{
xil_printf("ERROR : f_write returned %d\r\n", rc);
return XST_FAILURE;
}
rc = f_close(&fil);
if(rc){
xil_printf("ERROR : f_close returned %d\r\n",rc);
return XST_FAILURE;
}
return XST_SUCCESS;
}
#define FILE "test.txt"
int main()
{
init_platform();
const char src_str[] = "hsp test sd card write and read!";
u32 len = strlen(src_str);
SD_Init();
SD_Transfer_write(FILE,(u32)src_str,(len+1000));//当直接指定len时没有写出,需要指定较大的长度才会写出,原因未知
char dest_str[33];//len<=33
SD_Init();
SD_Transfer_read(FILE,(u32)dest_str,(len+1));
xil_printf("%s\r\n",dest_str);
print("SD write and read over!\r\n");
cleanup_platform();
return 0;
}
SD_Init
函数主要用f_mount
函数注册工作区域SD_Transfer_read
主要用f_open
打开文件,后用f_read
将从文件中读取数据到缓冲区,最后用f_close
关闭文件SD_Transfer_write
主要用f_open
打开文件,后用f_write
将缓冲区的内容写入到文件,最后用f_close
关闭文件FatFs API详细说明请参考上面FatFs链接中的说明。
注意:由于使用了strlen
函数需要#include
;使用了FatFs API所以要#include "ff.h"
;使用了XST_SUCCESS
之类的宏定义作为返回值所以要#include "xdevcfg.h"
。
关于文件名
刚开始定义#define FILE "husipeng_test.txt"
,每次运行在打开文件时返回6即FR_INVALID_NAME
,也就是说文件名无效,查询API库Path Names页面,再看文件ffconf.h中有定义#define _FS_RPATH 0U
;即文件名不能用子目录而且为8.3 format file name (SFN)
格式,也就说文件名只能为:<8个字符+.+<3字符方式,所以这里定义的文件名太长才引起的错误,所以改为#define FILE "test.txt"
这样的短文件名即可解决这个问题。
关于写保护
解决了以上文件名错误后,发现本次打开文件返回10即FR_WRITE_PROTECTED
,也就是SD卡打开了写保护,本人开发板上使用的小卡且也没有写保护的设置管脚。最后发现直接在PS部分SD卡设置里将WP取消选择即可(改正后设置如上面PS部分设置图中所示),出错原因应该是开发板上没有写保护管脚相关的设置,而开始在PS中又设置了WP管脚并设定到了一个MIO管脚,这将导致对写保护的误判;WP管脚需要拉低才能取消写保护,才能写SD卡,所以也可以将SD控制器的WP管脚连到EMIO,然后在PL部分将此EMIO拉低即可;而这里选择使用前面不连接WP的方法。
关于文件长度
解决以上两个问题后,写入文件是成功了,但是从电脑中打开查看,test.txt文件是存在了,但是内容为空,产看写入字节数bw的值也非0的正确值,理应写入了才对,但实际上却没有写进去,后来改变写入数据的大小发现每次写入的字节数比f_write(&fil,(void*) SourceAddress,ByteLength,&bw);
传递的参数ByteLength小几十到一百多B不等;也就是说最终真的写入文件的字节数比ByteLength小(而返回写入的字节数bw却与ByteLength相等);后来怀疑是内部缓存没有更新到文件中,但检查f_close
中也调用了f_sync(fp);
即Flush cached data。最后直接将传递入的ByteLength加大1000(如上程序中SD_Transfer_write(FILE,(u32)src_str,(len+1000));
),这样需要写入的数据是写入了,而通常会多写入一些其他数据,这也是没有办法,只有在读取的时候只读取有效部分。所以最终没有完全解决这个问题,希望有知道的朋友留言指教。