代码见:https://github.com/qinyunti/py32f403-nes.git
一.前言
前面我们实现了FLASH的读写,现在开始就可以移植文件系统了,这里移植比较常用的FATFS。
二.移植过程
2.1添加文件
下载源码
从以下地址http://elm-chan.org/fsw/ff/00index_e.html下载最新源码
保留以下文件
工程中创建fatfs目录,添加上述文件
并添加头文件包含路径
2.2适配读写接口
diskio.h
修改diskio.c
先只实现一个设备0,路径以0:开头
#include "ff.h"
#include "diskio.h"
#include "flash_itf.h"
#define DEV_FLASH 0
disk_status
获取状态,直接返回OK
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
switch (pdrv) {
case DEV_FLASH :
return RES_OK;
}
return STA_NOINIT;
}
disk_initialize
驱动在其他地方已经初始化,这里直接返回OK
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
switch (pdrv) {
case DEV_FLASH :
return RES_OK;
}
return STA_NOINIT;
}
disk_read
这里是按照sector为单位的,转化为字节为单位
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
switch (pdrv) {
case DEV_FLASH :
flash_itf_read(buff, sector*512, count*512);
return RES_OK;
}
return RES_PARERR;
}
disk_write
这里是按照sector为单位的,转化为字节为单位
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
switch (pdrv) {
case DEV_FLASH :
flash_itf_write((uint8_t*)buff, sector*512, count*512);
return RES_OK;
}
return RES_PARERR;
}
#endif
disk_ioctl
获取容量等,先固定,以后再优化为通过flash接口自动获取
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
switch (pdrv) {
case DEV_FLASH :
// Process of the command for the RAM drive
switch(cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(WORD*)buff = 4096;
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = 131072;
res = RES_OK;
break;
case CTRL_TRIM:
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
}
return RES_PARERR;
}
2.3时间接口
默认需要实现
get_fattime()
我们修改ffconf.h中
#define FF_FS_NORTC 0
为
#define FF_FS_NORTC 1
不使能时间戳功能。
2.4长文件名支持
ffconf.h中
#define FF_USE_LFN 0改为
#define FF_USE_LFN 3
如果配置为1则使用静态变量,线程不安全。
如果配置为2则使用栈,会占用较大栈容易栈溢出。
如果配置为3则使用动态内存分配,需要实现接口ff_memalloc()和ff_memfree()
我这里配置为3
ffsystem.c中
#if FF_USE_LFN == 3 /* Use dynamic memory allocation */
#include "FreeRTOS.h"
/*------------------------------------------------------------------------*/
/* Allocate/Free a Memory Block */
/*------------------------------------------------------------------------*/
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
UINT msize /* Number of bytes to allocate */
)
{
return pvPortMalloc(msize); /* Allocate a new memory block */
}
void ff_memfree (
void* mblock /* Pointer to the memory block to free (no effect if null) */
)
{
vPortFree(mblock); /* Free the memory block */
}
#endif
2.5线程安全
ffconf.h中
以下宏配置为1
#define FF_FS_REENTRANT 1
ffsystem.c中
以下宏配置为3使用FreeRTOS,其他不支持的系统需要移植对应互斥量的接口。
#define OS_TYPE 3
2.6其他配置
配置
#define FF_USE_MKFS 1支持格式化。
三.测试
Main.c中初始化
#include “ff.h”
static FATFS fs; /* Filesystem object */
static BYTE work[FF_MAX_SS]; /* Work area (larger is better for processing time) */
static void fs_init(void)
{
FRESULT res; /* API result code */
if(FR_OK != (res = f_mount (&fs, "0:", 1)))
{
xprintf("mount err %d, mkfs\r\n",res);
res = f_mkfs("0:",0,work,sizeof(work));
if(res == 0)
{
xprintf("mkfs ok\r\n");
if(FR_OK == f_mount (&fs, "0:", 1))
{
xprintf("mount ok\r\n");
}
else
{
xprintf("mount err\r\n");
}
}
else
{
xprintf("mkfs err %d\r\n",res);
}
}
else
{
xprintf("mount ok\r\n");
}
}
在shell 任务中调用fs_init初始化,因为spi接口使用了互斥量等系统api。
在shell_func.c中
#include "ff.h"
添加函数申明
static void printfilefunc(uint8_t* param);
static void writefilefunc(uint8_t* param);
g_shell_cmd_list_ast中添加两行
{ (uint8_t*)"printfile", printfilefunc, (uint8_t*)"printfile path addr size"},
{ (uint8_t*)"writefile", writefilefunc, (uint8_t*)"writefile path addr[hex] hexstr"},
实现如下
void printfilefunc(uint8_t* param)
{
char path[128];
uint8_t tmp[16];
uint32_t addr;
uint32_t size;
FRESULT res;
FIL fil;
UINT br;
uint32_t offset = 0;
if(3 == sscanf((const char*)param, "%*s %s %x %d", path, &addr, &size))
{
xprintf("hexdump %s 0x%x %d\r\n",path,addr,size);
if(FR_OK == (res=f_open(&fil,(const char*)path, FA_READ)))
{
xprintf("\r\n");
do
{
br = 0;
if(FR_OK == (res = f_read(&fil,tmp,(size>16)?16:size,&br)))
{
xprintf("%08x ",offset);
offset+=br;
for(uint32_t i=0;i<br;i++)
{
xprintf("%02x",(uint32_t)tmp);
}
xprintf(":");
for(uint32_t i=0;i<br;i++)
{
xprintf("%c",((tmp>0x1F)&&(tmp<0x7F))?(char)tmp:'.');
}
xprintf("\r\n");
size -= br;
}
else
{
break;
}
}while(br > 0);
if(FR_OK != (res = f_close(&fil)))
{
xprintf("close %s err %d\r\n",path,res);
}
}
else
{
xprintf("open %s err %d",path,res);
}
}
else
{
xprintf("param err");
}
}
void writefilefunc(uint8_t* param)
{
char path[128];
uint8_t hexstr[32+1];
uint8_t tmp[16];
uint32_t hexnum = 0;
uint32_t addr;
FRESULT res;
FIL fil;
UINT bw;
if(3 == sscanf((const char*)param, "%*s %s %x %s", path, &addr, hexstr))
{
xprintf("hexwrite %s 0x%x %s\r\n",path,addr,hexstr);
if(FR_OK == (res=f_open(&fil,(const char*)path, FA_WRITE | FA_CREATE_ALWAYS)))
{
xprintf("\r\n");
hexnum = str2hex((const char*)hexstr,tmp,32);
if(hexnum > 0)
{
if(FR_OK == (res=(f_lseek(&fil,addr))))
{
if(FR_OK != (res = f_write(&fil,tmp,hexnum,&bw)))
{
xprintf("write err %d\r\n",res);
}
}
else
{
xprintf("seek %d err %d\r\n",addr,res);
}
}
if(FR_OK != (res = f_close(&fil)))
{
xprintf("close %s err %d\r\n",path,res);
}
}
else
{
xprintf("open %s err %d",path,res);
}
}
else
{
xprintf("param err");
}
}
初始格式化需要一点时间,
writefile 0:/test.bin 0 112233445566778899写入文件
printfile 0:/test.bin 0 16读出文件看到一致
四.总结
以上我们完成了FATFS的移植,并基于SHELL进行了文件读写测试,后面就可以继续实现文件的相关操作命令以及文件到导入导出。