硬件平台:英蓓特 EM-STM3210E开发板
软件平台:mdk3.40;stm32V2.01库
FATfs版本:R0.07e。可在http://elm-chan.org/fsw/ff/00index_e.html下载
EM-STM3210E开发板上的sd卡例程只是简单的写和读sd卡的某个扇区,当然这样写过后卡上的文件系统格式就被破坏了,正因为这样,运行过板上的示例程序后我就不爽——要把卡重新格一遍。
在网上找了找,看到fatfs文件系统的移植也挺简单的,于是就搜集了fat文件系统的一些资料开始看,准备看完后做移植。
移植工作需要做的就是修改diskio.c和diskio.h这两个文件,给sd卡的底层和文件系统做个接口。
具体的工作就是完成如下几个函数:
disk_initialize ();//卡初始化
disk_status (); //返回卡的状态
disk_read (); //读扇区
disk_write (); //写扇区
disk_ioctl (); //支持几个命令
get_fattime (); //给fatfs提供时间
其中:
disk_initialize (); disk_read (); disk_read ();三个函数比较重要。
在进行上面函数的编写前,我们一定要保证我们已经写好了sd卡的底层,如卡的初始化,读一个扇区、读多个扇区、写一个扇区、写多个扇区等。我的开发板上这些功能函数比较全,sd卡的访问使用sd模式,可选择 查询、中断、DMA三种方式进行操作,当然DMA方式是最快的。
/**************************************************************************
功能 :磁盘初始化
***************************************************************************/
DSTATUS disk_initialize (BYTE drv )// Physical drive nmuber (0..)
{
SD_Error Status;
if(drv)
{
return STA_NOINIT; //仅支持磁盘0的操作
}
Status = SD_Init();
if(Status != SD_OK)
{
return STA_NOINIT; //其他错误:初始化失败
}
else
{
Status = SD_GetCardInfo(&SDCardInfo); //读sd卡信息
if (Status != SD_OK)
{
return STA_NOINIT;//RES_NOTRDY; //报NOT READY错误
}
// Select Card
Status = SD_SelectDeselect((u32) (SDCardInfo.RCA << 16));
if (Status != SD_OK)
{
return STA_NOINIT;//RES_NOTRDY; //报NOT READY错误
}
switch(mda_or_interrupt)
{
case 1: //dma方式
Status = SD_EnableWideBusOperation(SDIO_BusWide_4b);
if (Status != SD_OK)
{
return RES_NOTRDY; //报NOT READY错误
}
Status = SD_SetDeviceMode(SD_DMA_MODE);
if (Status != SD_OK)
{
return RES_NOTRDY; //报NOT READY错误
}
break;
case 0: //中断方式
Status = SD_EnableWideBusOperation(SDIO_BusWide_1b);
if (Status != SD_OK)
{
return RES_NOTRDY; //报NOT READY错误
}
Status = SD_SetDeviceMode(SD_INTERRUPT_MODE);
if (Status != SD_OK)
{
return RES_NOTRDY; //报NOT READY错误
}
break;
default :
break;
}
return 0; //初始化成功
}
}
下面的函数一般可以直接返回0,也可用 sd_ncd 脚检测有无sd卡
/**************************************************************************
功能:Return Disk Status 可用 sd_ncd 脚检测有无sd卡 有: 拉低 ; 无: 浮空
***************************************************************************/
DSTATUS disk_status (BYTE drv) // Physical drive nmuber (0..)
{
if(drv)
{
return STA_NOINIT; //仅支持磁盘0的操作
}
return 0; //初始化成功
}
/**************************************************************************
功能: Read Sector(s)
***************************************************************************/
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
{
vu8 res1 = 0, res2 = 0;
SD_Error Status;
if (drv || !count)
{
return RES_PARERR; //仅支持单磁盘操作,count不能等于0,否则返回参数错误
}
switch(mda_or_interrupt)
{
case 1: //dma方式
if(count==1) //1个sector的读操作
{
Status = SD_ReadBlock(sector << 9,buff2,BlockSize);//sector<<9 扇区地址转为字节地址 一个扇区512字节
memcpy(buff,buff2,BlockSize);
res1=buff[510]; res2=buff[511]; //调试用
}
else //多个sector的读操作
{
Status = SD_ReadMultiBlocks(sector << 9,buff2,BlockSize,count);
memcpy(buff,buff2,BlockSize * count);
}
break;
case 0: //中断方式
if(count==1) //1个sector的读操作
{
Status = SD_ReadBlock(sector<<9,(u32 *)(&buff[0]),BlockSize);
res1=buff[510]; res2=buff[511]; //调试用
}
else //多个sector的读操作
{
Status = SD_ReadMultiBlocks(sector<<9 ,(u32 *)(&buff[0]),BlockSize,count);
}
break;
default :
break;
}
//处理返回值,将sdcard.c的返回值转成ff.c的返回值
if(Status == SD_OK)
return RES_OK;
else
return RES_ERROR;
}
/**************************************************************************
功能: Write Sector(s)
***************************************************************************/
#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
{
SD_Error Status;
if (drv || !count)
{
return RES_PARERR; //仅支持单磁盘操作,count不能等于0,否则返回参数错误
}
switch(mda_or_interrupt)
{
case 1: //dma方式
if(count==1) //1个sector的写操作
{
memcpy(buff2,buff,BlockSize);
Status = SD_WriteBlock(sector << 9,buff2,BlockSize);//sector<<9 扇区地址转为字节地址 一个扇区512字节
}
else //多个sector的写操作
{
memcpy(buff2,buff,BlockSize * count);
Status =SD_WriteMultiBlocks(sector << 9,buff2,BlockSize,count);
}
break;
case 0: //中断方式
if(count==1) //1个sector的写操作
{
Status = SD_WriteBlock(sector << 9 ,(u32 *)(&buff[0]),BlockSize);
}
else //多个sector的写操作
{
Status = SD_WriteMultiBlocks(sector << 9 ,(u32 *)(&buff[0]),BlockSize,count);
}
break;
default :
break;
}
//处理返回值,将sdcard.c的返回值转成ff.c的返回值
if(Status == SD_OK)
return RES_OK;
else
return RES_ERROR;
}
#endif /* _READONLY */
/**************************************************************************
功能: Miscellaneous s
***************************************************************************/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
u32 x, y, z;
DRESULT res;
if (drv)
{
return RES_PARERR; //仅支持单磁盘操作,否则返回参数错误
}
//FATFS目前版本仅需处理CTRL_SYNC,GET_SECTOR_COUNT,GET_BLOCK_SIZ三个命令
switch(ctrl)
{
case CTRL_SYNC:
if(SD_GetTransferState()==SD_NO_TRANSFER)
{
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_SECTOR_COUNT: //读卡容量
////formula of the capacity///////////////
//
// memory capacity = BLOCKNR * BLOCK_LEN
//
// BLOCKNR = (C_SIZE + 1)* MULT
//
// C_SIZE_MULT+2
// MULT = 2
//
// READ_BL_LEN
// BLOCK_LEN = 2
//////////////////////////////////////////
if (SD_GetCardInfo(&SDCardInfo)==SD_OK)//读sd卡信息
{
x=SDCardInfo.SD_csd.DeviceSize+1; //C_SIZE + 1
y=SDCardInfo.SD_csd.DeviceSizeMul+2; //C_SIZE_MULT+2
z=SDCardInfo.SD_csd.RdBlockLen+y;
*(DWORD*)buff =x<
}
else
{
res = RES_ERROR ;
}
break;
default:
res = RES_PARERR;
break;
}
return res;
}
/*-----------------------------------------------------------------------*/
/* User defined to give a current time to fatfs module */
/* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
/* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
/*-----------------------------------------------------------------------*/
DWORD get_fattime (void)
{
struct tm t;
DWORD date;
t = Time_GetCalendarTime();
t.tm_year -= 1980; //年份改为1980年起
t.tm_mon++; //0-11月改为1-12月
t.tm_sec /= 2; //将秒数改为0-29
date = 0;
date = (t.tm_year << 25) | (t.tm_mon<<21) | (t.tm_mday<<16)|\
(t.tm_hour<<11) | (t.tm_min<<5) | (t.tm_sec);
return date;
} //此函数参考网友 九九的 程序
当这些函数写好后,移植工作就完成了。
我们在使用时在main.c中加入#i nclude “ccsbcs.c” ,并在 ffconf.h里做如下修改
#define _USE_LFN 1
#define_LFN_UNICODE 0//不使用UNICODE
移植完后我进行了读写速度测试:10M文件连续写入、读出。 1s中断计时
DMA 方式 : 读 49k-50K byte/s 写 1M byte/s
中断 方式 : 读 47k byte/s 写 0.83M byte/s
wav文件的播放,我主要是先遍历music文件夹下所有wav格式的文件,然后把文件名写入play_list.txt,播放时,从play_list.txt读出歌曲名,根据歌曲名读出数据送DA。
/***************************************************************
功能: 从music 文件夹里读出所有wav格式的歌曲名写入 play_list.txt
返回 name_num
****************************************************************/
////////////////////////////////////////////////////////////////
//当一个文件名的长度小于 8+1+3时长文件名区是空的;当长度大于12时
//长文件名区才会是 这个文件的真实文件名,而这是短文件名区是经过计算后
//的文件名,并不是真实的文件名
////////////////////////////////////////////////////////////////
FRESULT scan_files (char* path)
{
FRESULT res;
FILINFO fno;
DIR dir;
int i,j;
char *fn;
char namebuf_1[fn_buf_len]={0};
static u8 fs_type_copy;
#if _USE_LFN //为 长文件名区指针赋初值
static char lfn[_MAX_LFN * (_DF1S ? 2 : 1) + 1];
fno.lfname = lfn;
fno.lfsize = sizeof(lfn);
#endif
name_num=0;
res =f_mount(0, &fs); //每次 open一个对象时都先要 注册一个 缓冲区
res = f_unlink ("music/play_list.txt");//每次重建文件名时,先删掉原来的。
res = f_opendir(&dir, path); // 打开指定的目录
if (res == FR_OK) {
for (;;)
{
repet: //第一次执行读一个文件,第二次执行读第二个文件………………
res = f_readdir(&dir, &fno);//读出目录里的 文件信息 存入 fno
fs_type_copy=dir.fs->fs_type;
if (res != FR_OK || fno.fname[0] == 0) break;
#if _USE_LFN
{
fn = *fno.lfname ? fno.lfname : fno.fname;
j = *fno.lfname ? (fno.lfsize-1): (8+1+3 -1);
}
#else
{
fn = fno.fname; j=8+1+3 -1;
}
#endif
for(i=j;i>=3;i--) //文件名 从右到左判断 当文件扩展名为 wav时成立
{
if ((*(fn+i-2) == 'w')&&(*(fn+i-1) == 'a')&&(*(fn+i) == 'v'))
{
*(fn+i+1)='\0';//为文件名添加字符串结束符
/////////////// 成功检测到相符的文件了
//对文件名进行存储 存入"music/play_list.txt"
/////////////////////////////////////////////
//f_open 函数的型参 指的是 从根目录开始的文件名称
//故muxic文件夹下的goon.wav 文件的名字要写做 "music/goon.wav"
/////////////////////////////////////////////
for(j=0;j
namebuf[0]='m';namebuf[1]='u';namebuf[2]='s';namebuf[3]='i';namebuf[4]='c';namebuf[5]='/';
memcpy(&namebuf[6],fn,(i+2));
for(j=0;j
//文件名是不是写完一遍了
if(!strcmp(namebuf,namebuf_1))
{
res = f_close (&F);
res = f_mount(0, NULL);
return FR_OK; //相等 返回0
}
if(updata_list==1){updata_list=0;memcpy(namebuf_1,namebuf,fn_buf_len);}
// res =f_mount(0, &fs);
res = f_open (&F,"music/play_list.txt", FA_CREATE_ALWAYS |FA_OPEN_EXISTING | FA_WRITE|FA_READ);
res = f_lseek (&F, name_num*fn_buf_len );
res = f_write (&F, namebuf, fn_buf_len, &rw_num);
res = f_sync (&F);
name_num++;
dir.fs->fs_type=fs_type_copy; //dir 不知怎么被改动了,导致无法正确读下一个文件
// 改动的原因是: f_mount(0,&fs)时 fs->fs_type=0;而下次f_readdir时 就用了 fs这个缓冲
// 只 f_mount(0,&fs1)而不f_mount(0,&fs)?
goto repet;
}
}
goto repet;
}
}
return res;
}
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』