📄 ch375udevice.c
字号:
//=================================================================================================
//*************************************************************************************************
// Module Name : CH375UDevice.C
// Device object:
// Create date : 2005-11-07
// Modify date : 2005-11-11
// Description : USB 1.1 U Device for CH375
//
// Author : Li yuanzheng
// Version : V1.0
//*************************************************************************************************
//=================================================================================================
//
/* 这个程序用180行C代码就能够读取FAT16文件系统U盘的根目录,可以看到根目录下的文件
名,并可显示
首文件内容,不过,该程序很不严谨,也没有任何错误处理,对U盘兼容性较差,只是用于简单试
验,作为参考.
这个程序可以支持WINDOWS按FAT16格式化的U盘,因为程序精简,所以只兼容超过50%以上的U
盘品牌,如果换
成CH375A芯片则兼容性可提高到85%,当然,如果使用WCH公司的子程序库或者正式版本的C源
程序兼容性更好。
下
欢测试以下U盘通过:郎科/超稳经典64M/超稳迷你128M/U160-64M/超稳普及128M,爱国者/迷
你王16M/邮箱型,
黑匣子/64M,微闪/64M,飙王/32M/64M/128M,晶彩/C200-64M,新科/256M,昂达/128M...,欢迎
提供测试结果
未通过U盘:爱国者/智慧棒128M,清华普天/USB2.0-128M,当然,使用WCH的子程序库或CH375A
都可以测试通过 */
//-------------------------------------------------------------------------------------------------
// Includes
//-------------------------------------------------------------------------------------------------
#include <regx52.h> // 以下定义适用于MCS-51单片机,其它单片机参照修改
#include <stdio.h>
#include "CH375H.H"
#include "CH375UDevice.H"
//=================================================================================================
//
//UINT8X FILE_DATA_BUF[512*32] _at_ 0x0000; // 外部RAM数据缓冲区的起始地址 (DISK_BUFFER)
UINT32 DiskStart; // 逻辑盘的起始绝对扇区号LBA
UINT8 SecPerClus; // 逻辑盘的每簇扇区数
UINT8 ResvdSecCnt; // 逻辑盘的保留扇区数
UINT16 FATSize16; // FAT16逻辑盘的FAT表占用的扇区数
//=================================================================================================
//
//=================================================================================================
//
//=================================================================================================
// BulkOnly传输协议层,被CH375内置了,无需编写单片机程序
//=================================================================================================
//
//=================================================================================================
//=================================================================================================
// RBC/SCSI命令层,虽然被CH375内置了,但是要写程序发出命令及收发数据
//=================================================================================================
//=================================================================================================
//
//-------------------------------------------------------------------------------------------------
// 初始化磁盘
UINT8 mInitDisk( void )
{
xWriteCH375Cmd( CMD_GET_STATUS ); // 产生操作完成中断, 获取中断状态
xQueryInterrupt( ); // 等待中断并获取状态
if( CH375IntStatus == USB_INT_DISCONNECT ) return( CH375IntStatus ); // USB设备断开
xWriteCH375Cmd( CMD_DISK_INIT ); // 初始化USB存储器
xQueryInterrupt( ); // 等待中断并获取状态
if( CH375IntStatus != USB_INT_SUCCESS ) return( CH375IntStatus ); // 出现错误
xWriteCH375Cmd( CMD_DISK_SIZE ); // 获取USB存储器的容量
xQueryInterrupt( ); // 等待中断并获取状态
if( CH375IntStatus != USB_INT_SUCCESS ) // 出错重试
{ // 对于CH375A芯片,建议在此执行一次CMD_DISK_R_SENSE命令
mDelaymS( 250 );
xWriteCH375Cmd( CMD_DISK_SIZE ); // 获取USB存储器的容量
xQueryInterrupt( ); // 等待中断并获取状态
}
if( CH375IntStatus != USB_INT_SUCCESS ) return( CH375IntStatus ); // 出现错误
return( 0 ); // U盘已经成功初始化
}
//-------------------------------------------------------------------------------------------------
// 从USB存储器的指定地点读数据块到外部缓冲区
UINT8 mReadSector( UINT32 iLbaStart, UINT8 iSectorCount, UINT8X *DataBuffer )
{
UINT16 mBlockCount;
// 从USB存储器读数据块
xWriteCH375Cmd( CMD_DISK_READ );
xWriteCH375Data( (UINT8)iLbaStart ); // LBA的最低8位
xWriteCH375Data( (UINT8)( iLbaStart >> 8 ) );
xWriteCH375Data( (UINT8)( iLbaStart >> 16 ) );
xWriteCH375Data( (UINT8)( iLbaStart >> 24 ) ); // LBA的最高8位
xWriteCH375Data( iSectorCount ); // 扇区数
for ( mBlockCount = iSectorCount * 8; mBlockCount != 0; mBlockCount -- )
{
xQueryInterrupt( ); // 等待中断并获取状态
if ( CH375IntStatus == USB_INT_DISK_READ ) // 等待中断并获取状态,请求数据读出
{
xWriteCH375Cmd( CMD_RD_USB_DATA ); // 从CH375缓冲区读取数据块
CH375IntStatus = xReadCH375Data( ); // 后续数据的长度
while ( CH375IntStatus -- ) *DataBuffer++ = xReadCH375Data( );
xWriteCH375Cmd( CMD_DISK_RD_GO ); // 继续执行USB存储器的读操作
}
else break; // 返回错误状态
}
if ( mBlockCount == 0 )
{
xQueryInterrupt( ); // 等待中断并获取状态
if ( CH375IntStatus == USB_INT_SUCCESS ) return( 0 ); // 操作成功
}
return( CH375IntStatus ); // 操作失败
}
//=================================================================================================
//=================================================================================================
// FAT文件系统层,这层程序量实际较大,不过,该程序仅演示极简单的功能,所以精简
//=================================================================================================
//=================================================================================================
//
//-------------------------------------------------------------------------------------------------
// 获取字数据,因为MCS51是大端格式
UINT16 mGetPointWord( UINT8X *iAddr )
{
return( iAddr[0] | (UINT16)iAddr[1] << 8 );
}
//-------------------------------------------------------------------------------------------------
// 以下是非常简单的FAT文件系统的分析,正式应用绝对不应该如此简单
// 识别分析当前逻辑盘
UINT8 mIdenDisk( void )
{
UINT8 Status;
DiskStart = 0;
Status = mReadSector( 0, 1, FILE_DATA_BUF ); // 读取逻辑盘引导信息
if ( Status != 0 ) return( Status );
// if ( (FILE_DATA_BUF[0] != 0xEB) && (FILE_DATA_BUF[0] != 0xE9) )// 不是逻辑引导扇区
// 不是逻辑引导扇区( EB 3E 90 )
if ( (FILE_DATA_BUF[0] != 0xEB) && (FILE_DATA_BUF[1] != 0x3E) && (FILE_DATA_BUF[2] != 0x90) )
{
DiskStart = FILE_DATA_BUF[0x1C6] | (UINT16)FILE_DATA_BUF[0x1C7] << 8 | (UINT32)FILE_DATA_BUF[0x1C8] << 16 | (UINT32)FILE_DATA_BUF[0x1C9] << 24;
Status = mReadSector( DiskStart, 1, FILE_DATA_BUF );
if ( Status != 0 ) return( Status );
}
SecPerClus = FILE_DATA_BUF[0x0D]; // 每簇扇区数
ResvdSecCnt = FILE_DATA_BUF[0x0E]; // 逻辑盘的保留扇区数
// ResvdSecCnt = mGetPointWord( &FILE_DATA_BUF[0x0E] ); // 逻辑盘的保留扇区数
FATSize16 = mGetPointWord( &FILE_DATA_BUF[0x16] ); // FAT表占用扇区数
return( 0 ); // 成功
}
//-------------------------------------------------------------------------------------------------
// 获得指定簇号的链接簇
// 输入: iCluster 当前簇号
// 返回: 原链接簇号, 如果为 0 则说明错误
UINT16 mLinkCluster( UINT16 iCluster )
{
UINT8 Status;
Status = mReadSector( DiskStart + ResvdSecCnt + iCluster / 256, 1, FILE_DATA_BUF );
if ( Status != 0 ) return( 0 ); // 错误
return( mGetPointWord( &FILE_DATA_BUF[ ( iCluster + iCluster ) & 0x01FF ] ) );
}
//-------------------------------------------------------------------------------------------------
// 将簇号转换为绝对LBA扇区地址
UINT32 mClusterToLba( UINT16 iCluster )
{
return( DiskStart + ResvdSecCnt + FATSize16 * 2 + 32 + ( iCluster - 2 ) * SecPerClus );
}
//=================================================================================================
//=================================================================================================
// 用于调试
//=================================================================================================
//=================================================================================================
//
//-------------------------------------------------------------------------------------------------
// 仅用于调试用途及显示内容到PC机,与该程序功能完全无关
void mInitSTDIO( void )
{
SCON = 0x50; PCON = 0x80; TMOD = 0x20; TH1 = 0xf3; TR1=1; TI=1; // 24MHz, 9600bps
}
//-------------------------------------------------------------------------------------------------
// 如果错误则停止运行并显示错误状态
void mStopIfError( UINT8 iErrCode )
{
if ( iErrCode == 0 ) return;
printf( "Error status, %02X\n", (UINT16)iErrCode );
}
//=================================================================================================
//=================================================================================================
// Main
//=================================================================================================
//=================================================================================================
//
void main( void )
{
UINT8 Status;
UINT8X *CurrentDir;
UINT16 Cluster;
mDelaymS( 200 ); // 延时200毫秒
mInitSTDIO( );
xWriteCH375Cmd( CMD_SET_USB_MODE ); // 初始化CH375,设置USB工作模式
xWriteCH375Data( 6 ); // 模式代码,自动检测USB设备连接
while ( 1 )
{
printf( "Insert USB disk\n" );
while ( mWaitInterrupt( ) != USB_INT_CONNECT ); // 等待U盘连接
mDelaymS( 250 ); // 延时等待U盘进入正常工作状态
Status = mInitDisk( ); // 初始化U盘,实际是识别U盘的类型,必须进行此步骤
mStopIfError( Status );
Status = mIdenDisk( ); // 识别分析U盘文件系统,必要操作
mStopIfError( Status );
// 读取FAT16逻辑盘的根目录,通常根目录占用32个扇区
Status = mReadSector( DiskStart + ResvdSecCnt + FATSize16 * 2, 32, FILE_DATA_BUF );
mStopIfError( Status );
for ( CurrentDir = FILE_DATA_BUF; CurrentDir[0] != 0; CurrentDir += 32 )
{
if ( ( CurrentDir[0x0B] & 0x08 ) == 0 && CurrentDir[0] != 0xE5 )
{
CurrentDir[0x0B] = 0; // 为了便于显示,设置文件名或者目录名的结束标志
printf( "Name: %s\n", CurrentDir ); // 通过串口输出显示
}
}
// 以上显示根目录下的所有文件名,以下打开第一个文件,如果是C文件的话
if ( (FILE_DATA_BUF[0x0B]&0x08)==0 && FILE_DATA_BUF[0]!=0xE5 && FILE_DATA_BUF[8]=='C' )
{
Cluster = mGetPointWord( &FILE_DATA_BUF[0x1A] ); // 文件的首簇
while ( Cluster < 0xFFF8 ) // 文件簇未结束
{
if ( Cluster == 0 ) mStopIfError( 0x8F ); // 对于首簇,可能是0长度文件
Status = mReadSector( mClusterToLba( Cluster ), SecPerClus, FILE_DATA_BUF );
mStopIfError( Status ); // 读取首簇到缓冲区
FILE_DATA_BUF[30] = 0;
printf( "Data: %s\n", FILE_DATA_BUF );// 显示首行
Cluster = mLinkCluster( Cluster ); // 获取链接簇,返回0说明错误
}
}
while ( mWaitInterrupt( ) != USB_INT_DISCONNECT ); // 等待U盘拔出
mDelaymS( 250 );
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -