ch376hft.c

来自「基于CH376的U盘读写例子」· C语言 代码 · 共 246 行

C
246
字号
/* 2008.10.18
****************************************
**  Copyright  (C)  W.ch  1999-2009   **
**  Web:  http://www.winchiphead.com  **
****************************************
**  USB Host File Interface for CH376 **
**  TC2.0@PC, KC7.0@MCS51             **
****************************************
*/
/* CH376 主机文件系统接口 */

/* MCS-51单片机C语言的U盘文件读写示例程序 */
/* 本程序演示字节读写,文件枚举,文件复制, 用于将U盘中的/C51/CH376HFT.C文件中的小写字母转成大写字母后, 写到新建的文件NEWFILE.TXT中,
   如果找不到原文件CH376HFT.C, 那么该程序将显示C51子目录下所有以CH376开头的文件名, 并新建NEWFILE.TXT文件并写入提示信息,
   如果找不到C51子目录, 那么该程序将显示根目录下的所有文件名, 并新建NEWFILE.TXT文件并写入提示信息 */

/* C51   CH376HFT.C */
/* LX51  CH376HFT.OBJ */
/* OHX51 CH376HFT */

#include <reg52.h>
#include <stdio.h>
#include <string.h>

#include "..\HAL.H"
#include "..\HAL_BASE.C"
#include "..\DEBUG.H"
#include "..\DEBUG.C"
#include "..\PARA_HW.C"		/* 硬件总线并口连接方式 */
//#include "..\PARA_SW.C"		/* 软件模拟并口连接方式 */
//#include "..\SPI_HW.C"		/* 硬件SPI连接方式 */
//#include "..\SPI_SW.C"		/* 软件模拟SPI方式 */
//#include "..\UART_HW.C"		/* 硬件异步串口连接方式 */
#define		EN_DISK_QUERY		1	/* 启用磁盘查询 */
#include "..\FILE_SYS.H"
#include "..\FILE_SYS.C"

UINT8		idata	buf[64];


/* 检查操作状态,如果错误则显示错误代码并停机,应该替换为实际的处理措施,例如显示错误信息,等待用户确认后重试等 */
void	mStopIfError( UINT8 iError )
{
	if ( iError == USB_INT_SUCCESS ) return;  /* 操作成功 */
	printf( "Error: %02X\n", (UINT16)iError );  /* 显示错误 */
	while ( 1 ) {
/*		LED_OUT_ACT( );*/  /* LED闪烁 */
		mDelaymS( 200 );
/*		LED_OUT_INACT( );*/
		mDelaymS( 200 );
	}
}

/* 为printf和getkey输入输出初始化串口 */
void	mInitSTDIO( void )
{
	SCON = 0x50;
	PCON = 0x80;
	TMOD = 0x21;
	TH1 = 0xf3;  /* 24MHz晶振, 9600bps */
	TR1 = 1;
	TI = 1;
}

UINT8	CopyAndConvertFile( PUINT8 SrcFileName, PUINT8 TarFileName ) {  /* 文件复制,以字节方式复制,缓冲区越大速度越快 */
/* SrcFileName 源文件名,支持路径分隔符和多级目录,字符串必须存放于RAM中
   TarFileName 目标文件名,支持路径分隔符和多级目录,字符串必须存放于RAM中 */
	UINT8	s;
	UINT16	ThisLen, cnt;
	UINT32	FileSize, ByteCount;
	UINT8	TarName;
	UINT32	TarUpDirClust;
	ByteCount = 0;
	do {
		printf( "OpenSrc\n" );  /* 注意打印输出会浪费时间,减低平均复制速度 */
		s = CH376FileOpenPath( SrcFileName );  /* 打开多级目录下的文件,输入缓冲区必须在RAM中 */
		if ( s != USB_INT_SUCCESS ) return( s );
		if ( ByteCount == 0 ) {  /* 首次 */
			FileSize = CH376GetFileSize( );  /* 读取当前文件长度 */
			printf( "SrcFileSz=%ld\n", FileSize );
		}
		else {  /* 再次进入 */
			s = CH376ByteLocate( ByteCount );  /* 以字节为单位移动当前文件指针到上次复制结束位置 */
			if ( s != USB_INT_SUCCESS ) return( s );
		}
		printf( "Read\n" );
		s = CH376ByteRead( buf, sizeof( buf ), &ThisLen );  /* 以字节为单位从当前位置读取数据块,请求长度同缓冲区大小,返回实际长度在ThisLen中 */
		if ( s != USB_INT_SUCCESS ) return( s );
//		s = CH376FileClose( FALSE );  /* 关闭文件,对于读操作可以不必关闭文件 */
//		if ( s != USB_INT_SUCCESS ) return( s );

		for ( cnt=0; cnt < ThisLen; cnt ++ ) {  /* 将缓冲区中的小写字符转换为大写 */
			s = buf[ cnt ];
			if ( s >= 'a' && s <= 'z' ) buf[ cnt ] = s - ( 'a' - 'A' );
		}

		if ( ByteCount == 0 ) {  /* 首次,目标文件尚未存在 */
			printf( "CreateTar\n" );
			TarName = CH376SeparatePath( TarFileName );  /* 从路径中分离出最后一级文件名或目录名,返回最后一级文件名或目录名的偏移 */
			if ( TarName ) {  /* 是多级目录 */
				s = CH376FileOpenDir( TarFileName, TarName );  /* 打开多级目录下的最后一级目录,即打开新建文件的上级目录 */
				if ( s != ERR_OPEN_DIR ) {  /* 因为是打开上级目录,所以,如果不是成功打开了目录,那么说明有问题 */
					if ( s == USB_INT_SUCCESS ) return( ERR_FOUND_NAME );  /* 中间路径必须是目录名,如果是文件名则出错 */
					else if ( s == ERR_MISS_FILE ) return( ERR_MISS_DIR );  /* 中间路径的某个子目录没有找到,可能是目录名称错误 */
					else return( s );  /* 操作出错 */
				}
				TarUpDirClust = CH376ReadVar32( VAR_START_CLUSTER );  /* 上级目录的起始簇号 */
			}
			else TarUpDirClust = 0;  /* 默认是根目录的起始簇号 */
/* 在当前目录下进行文件新建或者打开操作,比全路径多级目录下的文件新建或者打开操作的速度快,
   所以目标文件的新建或者打开采用此法处理(本程序源文件是直接打开全路径多级目录下的文件,为提高速度,也可参照此法加快文件打开),
   为了实现当前目录下的文件新建或者打开操作,参考上面几行代码,
   首先,要获得文件所在的上级目录的起始簇号,相当于打开上级目录,通过CH376FileOpenPath打开上级目录获得,
   其次,要获得文件的直接短文件名(去掉上级目录名,不含任何路径分隔符,保留最后一级文件名),通过CH376SeparatePath分析目标文件名获得 */
			s = CH376FileCreate( &TarFileName[TarName] );  /* 在根目录或者当前目录下新建文件,如果文件已经存在那么先删除 */
			if ( s != USB_INT_SUCCESS ) return( s );
		}
		else {  /* 再次进入,目标文件已存在 */
			printf( "OpenTar\n" );
			CH376WriteVar32( VAR_START_CLUSTER, TarUpDirClust );  /* 将目标文件所在的上级目录的起始簇号设置为当前簇号,相当于打开上级目录 */
			s = CH376FileOpen( &TarFileName[TarName] );  /* 打开文件 */
			if ( s != USB_INT_SUCCESS ) return( s );
			s = CH376ByteLocate( ByteCount );  /* 以字节为单位移动当前文件指针到上次复制结束位置 */
			if ( s != USB_INT_SUCCESS ) return( s );
		}
		printf( "Write\n" );
		s = CH376ByteWrite( buf, ThisLen, NULL );  /* 以字节为单位向当前位置写入数据块,除非没有磁盘空间,否则返回实际长度总是与ThisLen相等 */
		if ( s != USB_INT_SUCCESS ) return( s );
		printf( "CloseTar\n" );
		s = CH376FileClose( TRUE );  /* 关闭文件,对于字节读写建议自动更新文件长度 */
		if ( s != USB_INT_SUCCESS ) return( s );
		ByteCount += ThisLen;
		if ( ThisLen < sizeof( buf ) ) {  /* 实际读出字节数小于请求读出字节数,说明原文件结束 */
			if ( ByteCount != FileSize ) printf( "Error on SourceFile reading" );
			break;
		}
	} while( ByteCount < FileSize );
	return( USB_INT_SUCCESS );
}

main( ) {
	UINT8			i, s;
	P_FAT_DIR_INFO	pDir;
	UINT8	xdata	SrcName[64];
	UINT8	xdata	TarName[64];
	mDelaymS( 100 );  /* 延时100毫秒 */
	mInitSTDIO( );  /* 为了让计算机通过串口监控演示过程 */
	printf( "Start\n" );

	s = mInitCH376Host( );  /* 初始化CH376 */
	mStopIfError( s );
/* 其它电路初始化 */

	while ( 1 ) {
		printf( "Wait Udisk/SD\n" );
		while ( CH376DiskConnect( ) != USB_INT_SUCCESS ) {  /* 检查U盘是否连接,等待U盘插入,对于SD卡,可以由单片机直接查询SD卡座的插拔状态引脚 */
			mDelaymS( 100 );
		}
		mDelaymS( 200 );  /* 延时,可选操作,有的USB存储器需要几十毫秒的延时 */

/* 对于检测到USB设备的,最多等待100*50mS,主要针对有些MP3太慢,对于检测到USB设备并且连接DISK_MOUNTED的,最多等待5*50mS,主要针对DiskReady不过的 */
		for ( i = 0; i < 100; i ++ ) {  /* 最长等待时间,100*50mS */
			mDelaymS( 50 );
			printf( "Ready ?\n" );
			s = CH376DiskMount( );  /* 初始化磁盘并测试磁盘是否就绪 */
			if ( s == USB_INT_SUCCESS ) break;  /* 准备好 */
			else if ( s == ERR_DISK_DISCON ) break;  /* 检测到断开,重新检测并计时 */
			if ( CH376GetDiskStatus( ) >= DEF_DISK_MOUNTED && i >= 5 ) break;  /* 有的U盘总是返回未准备好,不过可以忽略,只要其建立连接MOUNTED且尝试5*50mS */
		}
		if ( s == ERR_DISK_DISCON ) {  /* 检测到断开,重新检测并计时 */
			printf( "Device gone\n" );
			continue;
		}
		if ( CH376GetDiskStatus( ) < DEF_DISK_MOUNTED ) {  /* 未知USB设备,例如USB键盘、打印机等 */
			printf( "Unknown device\n" );
			goto UnknownUsbDevice;
		}
		i = CH376ReadBlock( buf );  /* 如果需要,可以读取数据块CH376_CMD_DATA.DiskMountInq,返回长度 */
		if ( i == sizeof( INQUIRY_DATA ) ) {  /* U盘的厂商和产品信息 */
			buf[ i ] = 0;
			printf( "UdiskInfo: %s\n", ((P_INQUIRY_DATA)buf) -> VendorIdStr );
		}

/* 读取原文件 */
		strcpy( SrcName, "\\C51\\CH376HFT.C" );  /* 源文件名,多级目录下的文件名和路径名必须复制到RAM中再处理,而根目录或者当前目录下的文件名可以在RAM或者ROM中 */
		strcpy( TarName, "\\NEWFILE.TXT" );  /* 目标文件名 */
		printf( "Open\n" );
		s = CH376FileOpenPath( SrcName );  /* 打开文件,该文件在C51子目录下 */
		if ( s == ERR_MISS_DIR || s == ERR_MISS_FILE ) {  /* 没有找到目录或者没有找到文件 */
/* 列出文件,完整枚举可以参考EXAM13全盘枚举 */
			if ( s == ERR_MISS_DIR ) strcpy( buf, "\\*" );  /* C51子目录不存在则列出根目录下的文件 */
			else strcpy( buf, "\\C51\\CH376*" );  /* CH376HFT.C文件不存在则列出\C51子目录下的以CH376开头的文件 */
			printf( "List file %s\n", buf );
			s = CH376FileOpenPath( buf );  /* 枚举多级目录下的文件或者目录,输入缓冲区必须在RAM中 */
			while ( s == USB_INT_DISK_READ ) {  /* 枚举到匹配的文件 */
				CH376ReadBlock( buf );  /* 读取枚举到的文件的FAT_DIR_INFO结构,返回长度总是sizeof( FAT_DIR_INFO ) */
				pDir = (P_FAT_DIR_INFO)buf;  /* 当前文件目录信息 */
				if ( pDir -> DIR_Name[0] != '.' ) {  /* 不是本级或者上级目录名则继续,否则必须丢弃不处理 */
					if ( pDir -> DIR_Name[0] == 0x05 ) pDir -> DIR_Name[0] = 0xE5;  /* 特殊字符替换 */
					pDir -> DIR_Attr = 0;  /* 强制文件名字符串结束以便打印输出 */
					printf( "*** EnumName: %s\n", pDir -> DIR_Name );  /* 打印名称,原始8+3格式,未整理成含小数点分隔符 */
				}
				xWriteCH376Cmd( CMD0H_FILE_ENUM_GO );  /* 继续枚举文件和目录 */
				xEndCH376Cmd( );
				s = Wait376Interrupt( );
			}
			if ( s != ERR_MISS_FILE ) mStopIfError( s );  /* 操作出错 */
			printf( "Create\n" );
			s = CH376FileCreatePath( TarName );  /* 新建多级目录下的文件,支持多级目录路径,输入缓冲区必须在RAM中 */
			mStopIfError( s );
			printf( "Write\n" );
			strcpy( buf, "找不到/C51/CH376HFT.C文件\xd\n" );
			s = CH376ByteWrite( buf, strlen(buf), NULL );  /* 以字节为单位向当前位置写入数据块 */
			mStopIfError( s );
			printf( "Close\n" );
			s = CH376FileClose( TRUE );  /* 关闭文件,对于字节读写建议自动更新文件长度 */
			mStopIfError( s );
		}
		else {  /* 找到文件或者出错 */
			mStopIfError( s );
			s = CopyAndConvertFile( SrcName, TarName );  /* 文件复制 */
			mStopIfError( s );
		}

/* 删除某文件 */
/*		printf( "Erase\n" );
		s = CH376FileErase( "/OLD" );  删除文件
		if ( s != USB_INT_SUCCESS ) printf( "Error: %02X\n", (UINT16)s );  显示错误
*/

/* 检查U盘或者SD卡的剩余空间 */
/*		printf( "DiskQuery: " );
		s = CH376DiskQuery( (PUINT32)buf );  查询磁盘剩余空间信息,扇区数
		mStopIfError( s );
//		printf( "free cap = %ld MB\n", *(PUINT32)buf * DEF_SECTOR_SIZE / 1000000 );  未用扇区数乘以扇区大小后,除以1M换算为兆字节单位
		printf( "free cap = %ld MB\n", *(PUINT32)buf / ( 1000000 / DEF_SECTOR_SIZE ) );  上面一行的计算可能会溢出,换个顺序计算 */

UnknownUsbDevice:
		printf( "Take out\n" );
		while ( CH376DiskConnect( ) == USB_INT_SUCCESS ) {  /* 检查U盘是否连接,等待U盘拔出 */
			mDelaymS( 100 );
		}
		mDelaymS( 200 );
	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?