之前做过一个读取U盘内指定文件内数据的项目。读取方式有很多的限制,比如在程序中文件名是定死的,只有当程序中写的文件名跟U盘内的文件名一模一样时,才能从U盘内读取数据。这样,后期需要修改文件名的话还要在程序中修改成一模一样的名字,这样是很不方便的。
我在寻找解决办法的时候看了看ch376数据手册,在这上面查到了有关方面的资料,找到了下面一段话:
上面这段话是我在数据手册上看到的,里面提到可以搜索目标U盘内的所有文件名,并且包含了例程:
typedef struct _FILE_NAME {
UINT32 DirStartClust; /* 文件所在目录的起始簇号 */
// UINT32 Size; /* 文件长度 */
UINT8 Name[8+1+3+1]; /* 文件名,共8+3字节,分隔符,结束符,因为未包含上级目录名所以是相对路径 */
UINT8 Attr; /* 文件属性 */
} FILE_NAME;
#define MAX_FILE_COUNT 40
FILE_NAME xdata FileNameBuffer[ MAX_FILE_COUNT ]; /* 文件名结构 */
UINT16 FileCount;
UINT8 idata buf[64];
/* 例子:列举指定序号的目录下的所有文件 */
UINT8 ListFile( UINT8 index )
/* 输入参数index是指目录在结构中的序号 */
{
UINT8 s;
P_FAT_DIR_INFO pDir;
PUINT8 pNameBuf;
UINT32 CurrentDirStartClust; /* 保存当前目录的起始簇号,用于加快文件枚举和打开速度 */
CH376WriteVar32( VAR_START_CLUSTER, FileNameBuffer[ index ].DirStartClust ); /* 将当前目录的上级目录的起始簇号设置为当前簇号,相当于打开上级目录 */
printf( "List Directory: %s\n", FileNameBuffer[ index ].Name ); /* 显示当前要列举的目录名 */
s = CH376FileOpen( FileNameBuffer[ index ].Name ); /* 打开目录,仅为了获取目录的起始簇号以提高速度 */
if ( s == USB_INT_SUCCESS ) return( ERR_FOUND_NAME ); /* 应该是打开了目录,但是返回结果是打开了文件 */
else if ( s != ERR_OPEN_DIR ) return( s );
if ( index ) CurrentDirStartClust = CH376ReadVar32( VAR_START_CLUSTER ); /* 不是根目录,获取目录的起始簇号 */
else CurrentDirStartClust = 0; /* 是根目录 */
CH376FileClose( FALSE ); /* 对于根目录一定要关闭 */
CH376WriteVar32( VAR_START_CLUSTER, CurrentDirStartClust ); /* 当前目录的起始簇号,相当于打开当前目录 */
CH376SetFileName( "*" ); /* 设置将要操作的文件的文件名,通配符支持所有文件和子目录 */
xWriteCH376Cmd( CMD0H_FILE_OPEN ); /* 枚举文件和目录 */
xEndCH376Cmd( );
while ( 1 ) {
s = Wait376Interrupt( );
if ( s == USB_INT_DISK_READ ) { /* 请求数据读出 */
/* 在文件枚举过程中,不能执行其它可能产生中断的操作命令,例如,如果需要获取长文件名,那么可以将枚举出的文件名保存并在枚举结束后获取其长文件名 */
CH376ReadBlock( buf ); /* 读取枚举到的文件的FAT_DIR_INFO结构,返回长度总是sizeof( FAT_DIR_INFO ) */
xWriteCH376Cmd( CMD0H_FILE_ENUM_GO ); /* 继续枚举文件和目录,先发出下一个命令再分析上行读出的数据可以让CH376与单片机分别同时工作,提高速度 */
xEndCH376Cmd( );
pDir = (P_FAT_DIR_INFO)buf; /* 当前文件目录信息 */
if ( pDir -> DIR_Name[0] != '.' ) { /* 不是本级或者上级目录名则继续,否则必须丢弃不处理 */
if ( pDir -> DIR_Name[0] == 0x05 ) pDir -> DIR_Name[0] = 0xE5; /* 特殊字符替换 */
if ( pDir -> DIR_Name[8] == 'H' && pDir -> DIR_Name[9] == ' ' /* 比较文件扩展名分析文件类型的范例 */
|| pDir -> DIR_Name[8] == 'E' && pDir -> DIR_Name[9] == 'X' && pDir -> DIR_Name[10] == 'E' ) {
printf( "This is a .H or .EXE file\n" );
}
if ( FileCount < MAX_FILE_COUNT ) { /* 文件名结构缓冲区足够 */
pNameBuf = & FileNameBuffer[ FileCount ].Name; /* 文件名结构中的文件名缓冲区 */
for ( s = 0; s < 11; s ++ ) { /* 复制文件名,长度为11个字符 */
if ( pDir -> DIR_Name[ s ] != 0x20 ) { /* 有效字符 */
if ( s == 8 ) { /* 处理扩展名 */
*pNameBuf = '.'; /* 分隔符 */
pNameBuf ++;
}
*pNameBuf = pDir -> DIR_Name[ s ]; /* 复制文件名的一个字符 */
pNameBuf ++;
}
}
*pNameBuf = 0; /* 当前文件名完整路径的结束符 */
FileNameBuffer[ FileCount ].DirStartClust = CurrentDirStartClust; /* 记录当前目录的起始簇号,用于加快文件打开速度 */
FileNameBuffer[ FileCount ].Attr = pDir -> DIR_Attr; /* 记录文件属性 */
if ( pDir -> DIR_Attr & ATTR_DIRECTORY ) printf( "Dir %4d#: %s\n", FileCount, FileNameBuffer[ FileCount ].Name ); /* 判断是目录名 */
else printf( "File%4d#: %s\n", FileCount, FileNameBuffer[ FileCount ].Name ); /* 判断是文件名 */
FileCount ++; /* 子目录计数 */
}
else { /* 文件名结构缓冲区太小,结构数量不足 */
printf( "FileName Structure Full\n" );
s = Wait376Interrupt( );
CH376EndDirInfo( ); /* 获取完FAT_DIR_INFO结构 */
break; /* 强行终止枚举 */
}
}
}
else {
if ( s == ERR_MISS_FILE ) s = USB_INT_SUCCESS; /* 没有找到更多的匹配文件 */
break;
}
}
/* if ( s == USB_INT_SUCCESS ) return( s );*/ /* 操作成功 */
return( s );
}
UINT8 ListAll( void ) /* 以广度优先的算法枚举整个U盘中的所有文件及目录 */
{
UINT8 s;
UINT16 OldFileCount;
UINT16 RealReadCount;
FileNameBuffer[ 0 ].Name[0] = '/'; /* 根目录,是完整路径名,除根目录是绝对路径之外都是相对路径 */
FileNameBuffer[ 0 ].Name[1] = 0;
FileNameBuffer[ 0 ].DirStartClust = 0; /* 根目录的起始簇号 */
FileNameBuffer[ 0 ].Attr = ATTR_DIRECTORY; /* 根目录也是目录,作为第一个记录保存 */
for ( OldFileCount = 0, FileCount = 1; OldFileCount < FileCount; OldFileCount ++ ) { /* 尚有新枚举到的文件名结构未进行分析,FileCount处于变化之中 */
if ( FileNameBuffer[ OldFileCount ].Attr & ATTR_DIRECTORY ) { /* 是目录则继续进行深度搜索 */
s = ListFile( OldFileCount ); /* 枚举目录,记录保存到结构中,FileCount可能会改变 */
if ( s != USB_INT_SUCCESS ) return( s );
}
}
上面的代码中包含了两个函数, ListFile( UINT8 index ) 和 ListAll( void )
ListFile( UINT8 index ) 函数是搜索目标U盘内所有文件名及目录,而ListAll( void ) 是把所有搜索到的文件名列举出来,保存到一个结构体数组里面。这样的话,我们就可以在定义的数组里面看到目标U盘内所有的文件名了,stm32 debug调试的时候可以查看结构体数组里面存着的U盘内所有的文件名。
然后我们回到一开始的解决每次都需要更改文件名的问题。解决方法就是我们只定死文件名的前几个字母(文件名不能用中文),然后在需要修改文件名的时候只修改后面几个就行,在需要读取目标文件内的数据时,程序可以写成把前几个字母和U盘内所有的文件名作比较,只要符合条件的文件名就可以读里面的数据。
比如:程序中只比较 QWE 这三个字母,而U盘中的文件名是 QWERTY。前三个字母符合的话就可以读取 QWERTY 这个文件内的数据了。但是这样做也有一个缺点,就是U盘内不止有 QWERTY 这个文件,还有QWERTYU 这个文件,那这样的话该怎么办。是不是就没法读数据了。实际情况是, ListAll( void )函数把所有的文件是按一定顺序保存到一个数组里面的。那么系统在和文件名作比较时,只要搜索到符合条件的文件名就终止搜索了,直接读取符合条件的文件。但是你怎么确保你会把想让它读的文件排在前面呢。估计还有人会说我只在U盘内放入一个符合条件的文件不就行了吗,这样做也不是不可以,但你需要每次都把不需要的文件删掉再添加新的文件,这样不是很方便,而且万一哪次你忘记删掉呢。那么怎么解决这个问题呢。
为了解决这个问题,我只能加入编号了,还是举例说明:可以这样定义文件名,QWE001,这样的话我只需要在符合前面三个字母的前提下再比较编号的大小就可以解决了(估计还会有人说干脆直接把文件名定成一串数字不就行了,这样有什么坏处你自己想想),所以你只需要把每次新的文件编号加1就可以放心的放到U盘内取读了。
以上是我自己在实践中总结的东西,可能会有一些地方有出入的,还是希望读者指出有不足的地方,我们共同进步!!!!!!感谢!!!