最近使用SD卡,需要上传文件到手机,这个功能需要支持同一个文件同时打开一个写入和一个读取句柄,写入的同时能够读取文件内容上传,写入都是写入文件末尾,文件是一个以小时为单位建的,要想上传当前小时内的文件,就必须支持单写多读功能
STM32芯片使用的是STM32 F412系列,使用STM32CubeMX配置的Keil5工程,找了一下配置发现有个可重入的设置
但是默认为Disabled,且只有Disabled可选,看了一下ffconf.h的代码,想直接在代码里修改为enable
重试修改后编译报错,主要因为_SYNC_t配置为空了
接着配置了FreeRTOS,FS_REENTRANT被自动修改为enable了,一阵小兴奋,立刻创建了一个测试文件,写了测试代码,对test.txt文件同时打开一个写入句柄和一个读取句柄,都是返回一个打开成功,另一个就打开返回失败。
不知道是我理解错了还是需要额外的其他配置
直接配置不行,那就直接修改FatFs的源码
FatFs本身是默认支持同一个文件打开多个读取句柄的,但是打开一个写入后其他都会返回失败,或者打开读取后不关闭再打开写入也会返回失败,错误码为:FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
依照这个错误,查找对应的代码,修改为支持单写入多读取,需要修改两个函数
static
FRESULT chk_lock ( /* Check if the file can be accessed */
DIR* dp, /* Directory object pointing the file to be checked */
int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */
)
{
UINT i, be;
/* Search file semaphore table */
for (i = be = 0; i < _FS_LOCK; i++) {
if (Files[i].fs) { /* Existing entry */
if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */
Files[i].clu == dp->obj.sclust &&
Files[i].ofs == dp->dptr) break;
} else { /* Blank entry */
be = 1;
}
}
if (i == _FS_LOCK) { /* The object is not opened */
return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */
}
/* The object has been opened. Reject any open against writing file and all write mode open */
//return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
// 修改为支持多次打开,只支持一个写入,多个读取
if(acc == 0 || (Files[i].ctr & 0x100) == 0)
{ // 不是写入或者不是不存在写入锁,直接返回OK
return FR_OK;
}
if((Files[i].ctr & 0x100) && acc != 0)
{ // 已经存在写入锁,禁止新写入锁
return FR_LOCKED;
}
return FR_LOCKED;
}
static
UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
DIR* dp, /* Directory object pointing the file to register or increment */
int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
)
{
UINT i;
for (i = 0; i < _FS_LOCK; i++) { /* Find the object */
if (Files[i].fs == dp->obj.fs &&
Files[i].clu == dp->obj.sclust &&
Files[i].ofs == dp->dptr) break;
}
if (i == _FS_LOCK) { /* Not opened. Register it as new. */
for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */
Files[i].fs = dp->obj.fs;
Files[i].clu = dp->obj.sclust;
Files[i].ofs = dp->dptr;
Files[i].ctr = 0;
}
//if (acc && Files[i].ctr) return 0; /* Access violation (int err) */
//Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */
Files[i].ctr = acc ? (Files[i].ctr | 0x100) : Files[i].ctr + 1; /* Set semaphore value *//*标志修改为支持一写多读标志*/
return i + 1;
}
修改后就可以支持单写多读了
测试一个文件打开一个写入,两个读取的代码如下:
int8_t TestFileWriteRead(void)
{
char szTestFileName[] = "test.txt";
FIL fTestR;
retSD = f_open(&fTestR, szTestFileName, FA_READ);
if(FR_OK != retSD)
{
printf("read test:open file(%s) error : %d\r\n", szTestFileName, retSD);
return -1;
}
FIL fTestW;
retSD = f_open(&fTestW, szTestFileName, FA_WRITE | FA_OPEN_APPEND);
if(FR_OK != retSD)
{
printf("write test:open file(%s) error : %d\r\n", szTestFileName, retSD);
return -1;
}
FIL fTestR2;
retSD = f_open(&fTestR2, szTestFileName, FA_READ);
if(FR_OK != retSD)
{
printf("read2 test:open file(%s) error : %d\r\n", szTestFileName, retSD);
return -1;
}
FIL fTestW2;
retSD = f_open(&fTestW2, szTestFileName, FA_WRITE | FA_OPEN_APPEND);
if(FR_OK != retSD)
{// 这个会返回失败,因为已经打开一个写入
printf("write2 test:open file(%s) error : %d\r\n", szTestFileName, retSD);
//return -1;
}
{
char bufRead[512];
f_lseek(&fTestR, 100);
UINT br = 0;
retSD = f_read(&fTestR, bufRead, 500, &br);
bufRead[br] = 0;
printf("retSD:%d, read Data:%s\r\n", retSD, bufRead);
}
{
char bufWrite[50] = "abcdefghijklmnopqrstuvwxyz";
UINT bw = 0;
retSD = f_write(&fTestW, bufWrite, 26, &bw);
printf("retSD:%d, write Data(%d):%s\r\n", retSD, bw, bufWrite);
}
{
char bufRead[512];
f_lseek(&fTestR2, 10002);
UINT br = 0;
retSD = f_read(&fTestR2, bufRead, 500, &br);
bufRead[br] = 0;
printf("retSD:%d, read Data:%s\r\n", retSD, bufRead);
}
retSD = f_close(&fTestR);
retSD = f_close(&fTestR2);
retSD = f_close(&fTestW);
return 0;
}
由于直接修改FatFs源码,会担心使用STM32CubeMX再次生成工程时覆盖了修改的代码,我的做法是直接复制了一个ff.c文件到工程目录中,然后新建一个分组,将ff.c文件加入这个分组,然后将Middlewares/FatFs分组的ff.c文件右键移除就可以了,虽然每次使用STM32CubeMX重新生成工程都要删除一次,但至少不用担心自己修改的代码被覆盖