搜索
您的当前位置:首页正文

CH376о?

来源:六九路网


主要包含4个C文件和和三个头文件 CH376INC.H与CH376HFT.C为u盘主文件

SPI_SW.C为对硬件操作文件单片机与SPI接口 FILE_SYS.H与FILE_SYS.C为ch376接口函数 分别列出

CH376INC.H文件:

/* C Define for CH376 */ /* Website: http://wch.cn */ /* Email: tech@wch.cn */ /* Author: W.ch 2008.10 */ /* V1.0 for CH376 */

#ifndef __CH376INC_H__ #define __CH376INC_H__

#ifdef __cplusplus extern \"C\" { #endif /*

********************************************************************************************************************* */ /* 常用类型和常量定义 */

#ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #ifndef NULL #define NULL 0 #endif

#ifndef UINT8

typedef unsigned char UINT8; #endif

#ifndef UINT16

typedef unsigned short UINT16; #endif

#ifndef UINT32

typedef unsigned long UINT32; #endif

#ifndef PUINT8

typedef unsigned char *PUINT8; #endif

#ifndef PUINT16

typedef unsigned short *PUINT16; #endif

#ifndef PUINT32

typedef unsigned long *PUINT32; #endif

#ifndef UINT8V

typedef unsigned char volatile UINT8V; #endif

#ifndef PUINT8V

typedef unsigned char volatile *PUINT8V; #endif /*

********************************************************************************************************************* */ /* 硬件特性 */

#define CH376_DAT_BLOCK_LEN 0x40 /* USB单个数据包, 数据块的最大长度, 默认缓冲区的长度 */ /*

********************************************************************************************************************* */ /* 命令代码 */

/* 部分命令兼容CH375芯片, 但是输入数据或者输出数据的可能局部不同) */ /* 一个命令操作顺序包含:

一个命令码(对于串口方式,命令码之前还需要两个同步码), 若干个输入数据(可以是0个),

产生中断通知或者若干个输出数据(可以是0个), 二选一, 有中断通知则一定没有输出数据, 有输出数据则一定不产生中断

仅CMD01_WR_REQ_DATA命令例外, 顺序包含: 一个命令码, 一个输出数据, 若干个输入数据

命令码起名规则: CMDxy_NAME

其中的x和y都是数字, x说明最少输入数据个数(字节数), y说明最少输出数据个数(字节数), y如果是H则说明产生中断通知,

有些命令能够实现0到多个字节的数据块读写, 数据块本身的字节数未包含在上述x或y之内 */

/* 本文件默认会同时提供与CH375芯片命令码兼容的命令码格式(即去掉x和y之后), 如果不需要, 那么可以定义_NO_CH375_COMPATIBLE_禁止 */ /*

********************************************************************************************************************* */ /* 主要命令(手册一), 常用 */

#define CMD01_GET_IC_VER 0x01 /* 获取芯片及固件版本 */ /* 输出: 版本号( 位7为0, 位6为1, 位5~位0为版本号 ) */ /* CH376返回版本号的值为041H即版本号为01H */

#define CMD21_SET_BAUDRATE 0x02 /* 串口方式: 设置串口通讯波特率(上电或者复位后的默认波特率为9600bps,由D4/D5/D6引脚选择) */ /* 输入: 波特率分频系数, 波特率分频常数 */

/* 输出: 操作状态( CMD_RET_SUCCESS或CMD_RET_ABORT, 其它值说明操作未完成 ) */

#define CMD00_ENTER_SLEEP 0x03 /* 进入睡眠状态 */

#define CMD00_RESET_ALL 0x05 /* 执行硬件复位 */

#define CMD11_CHECK_EXIST 0x06 /* 测试通讯接口和工作状态 */ /* 输入: 任意数据 */

/* 输出: 输入数据的按位取反 */

#define CMD20_CHK_SUSPEND 0x0B /* 设备方式: 设置检查USB总线挂起状态的方式 */

/* 输入: 数据10H, 检查方式 */

/* 00H=不检查USB挂起, 04H=以50mS为间隔检查USB挂起, 05H=以10mS为间隔检查USB挂起 */

#define CMD20_SET_SDO_INT 0x0B /* SPI接口方式: 设置SPI的SDO引脚的中断方式 */

/* 输入: 数据16H, 中断方式 */

/* 10H=禁止SDO引脚用于中断输出,在SCS片选无效时三态输出禁止, 90H=SDO引脚在SCS片选无效时兼做中断请求输出 */

#define CMD14_GET_FILE_SIZE 0x0C /* 主机文件模式: 获取当前文件长度 */ /* 输入: 数据68H */

/* 输出: 当前文件长度(总长度32位,低字节在前) */

#define CMD50_SET_FILE_SIZE 0x0D /* 主机文件模式: 设置当前文件长度 */ /* 输入: 数据68H, 当前文件长度(总长度32位,低字节在前) */

#define CMD11_SET_USB_MODE 0x15 /* 设置USB工作模式 */ /* 输入: 模式代码 */

/* 00H=未启用的设备方式, 01H=已启用的设备方式并且使用外部固件模式(串口不支持), 02H=已启用的设备方式并且使用内置固件模式 */

/* 03H=SD卡主机模式/未启用的主机模式,用于管理和存取SD卡中的文件 */

/* 04H=未启用的主机方式, 05H=已启用的主机方式, 06H=已启用的主机方式并且自动产生SOF包, 07H=已启用的主机方式并且复位USB总线 */

/* 输出: 操作状态( CMD_RET_SUCCESS或CMD_RET_ABORT, 其它值说明操作未完成 ) */

#define CMD01_GET_STATUS 0x22 /* 获取中断状态并取消中断请求 */ /* 输出: 中断状态 */

#define CMD00_UNLOCK_USB 0x23 /* 设备方式: 释放当前USB缓冲区 */

#define CMD01_RD_USB_DATA0 0x27 /* 从当前USB中断的端点缓冲区或者主机端点的接收缓冲区读取数据块 */ /* 输出: 长度, 数据流 */

#define CMD01_RD_USB_DATA 0x28 /* 设备方式: 从当前USB中断的端点缓冲区读取数据块, 并释放缓冲区, 相当于 CMD01_RD_USB_DATA0 + CMD00_UNLOCK_USB */ /* 输出: 长度, 数据流 */

#define CMD10_WR_USB_DATA7 0x2B /* 设备方式: 向USB端点2的发送缓冲区写入数据块 */

/* 输入: 长度, 数据流 */

#define CMD10_WR_HOST_DATA 0x2C /* 向USB主机端点的发送缓冲区写入数据块 */

/* 输入: 长度, 数据流 */

#define CMD01_WR_REQ_DATA 0x2D /* 向内部指定缓冲区写入请求的数据块 */

/* 输出: 长度 */ /* 输入: 数据流 */

#define CMD20_WR_OFS_DATA 0x2E /* 向内部缓冲区指定偏移地址写入数据块 */

/* 输入: 偏移, 长度, 数据流 */

#define CMD10_SET_FILE_NAME 0x2F /* 主机文件模式: 设置将要操作的文件的文件名 */

/* 输入: 以0结束的字符串(含结束符0在内长度不超过14个字符) */ /*

********************************************************************************************************************* */

/* 主要命令(手册一), 常用, 以下命令总是在操作结束时产生中断通知, 并且总是没有输出数据 */

#define CMD0H_DISK_CONNECT 0x30 /* 主机文件模式/不支持SD卡: 检查磁盘是否连接 */

/* 输出中断 */

#define CMD0H_DISK_MOUNT 0x31 /* 主机文件模式: 初始化磁盘并测试磁盘是否就绪 */

/* 输出中断 */

#define CMD0H_FILE_OPEN 0x32 /* 主机文件模式: 打开文件或者目录(文件夹),或者枚举文件和目录(文件夹) */ /* 输出中断 */

#define CMD0H_FILE_ENUM_GO 0x33 /* 主机文件模式: 继续枚举文件和目录(文件夹) */

/* 输出中断 */

#define CMD0H_FILE_CREATE 0x34 /* 主机文件模式: 新建文件,如果文件已经存在那么先删除 */ /* 输出中断 */

#define CMD0H_FILE_ERASE 0x35 /* 主机文件模式: 删除文件,如果已经打开则直接删除,否则对于文件会先打开再删除,子目录必须先打开 */ /* 输出中断 */

#define CMD1H_FILE_CLOSE 0x36 /* 主机文件模式: 关闭当前已经打开的文件或者目录(文件夹) */

/* 输入: 是否允许更新文件长度 */

/* 00H=禁止更新长度, 01H=允许更新长度 */ /* 输出中断 */

#define CMD1H_DIR_INFO_READ 0x37 /* 主机文件模式: 读取文件的目录信息 */

/* 输入: 指定需要读取的目录信息结构在扇区内的索引号 */

/* 索引号范围为00H~0FH, 索引号0FFH则为当前已经打开的文件 */ /* 输出中断 */

#define CMD0H_DIR_INFO_SAVE 0x38 /* 主机文件模式: 保存文件的目录信息 */

/* 输出中断 */

#define CMD4H_BYTE_LOCATE 0x39 /* 主机文件模式: 以字节为单位移动当前文件指针 */

/* 输入: 偏移字节数(总长度32位,低字节在前) */ /* 输出中断 */

#define CMD2H_BYTE_READ 0x3A /* 主机文件模式: 以字节为单位从当前位置读取数据块 */

/* 输入: 请求读取的字节数(总长度16位,低字节在前) */ /* 输出中断 */

#define CMD0H_BYTE_RD_GO 0x3B /* 主机文件模式: 继续字节读 */ /* 输出中断 */

#define CMD2H_BYTE_WRITE 0x3C /* 主机文件模式: 以字节为单位向当前位置写入数据块 */

/* 输入: 请求写入的字节数(总长度16位,低字节在前) */ /* 输出中断 */

#define CMD0H_BYTE_WR_GO 0x3D /* 主机文件模式: 继续字节写 */ /* 输出中断 */

#define CMD0H_DISK_CAPACITY 0x3E /* 主机文件模式: 查询磁盘物理容量 */ /* 输出中断 */

#define CMD0H_DISK_QUERY 0x3F /* 主机文件模式: 查询磁盘空间信息 */ /* 输出中断 */

#define CMD0H_DIR_CREATE 0x40 /* 主机文件模式: 新建目录(文件夹)并打开,如果目录已经存在那么直接打开 */ /* 输出中断 */

#define CMD4H_SEC_LOCATE 0x4A /* 主机文件模式: 以扇区为单位移动当前文件指针 */

/* 输入: 偏移扇区数(总长度32位,低字节在前) */ /* 输出中断 */

#define CMD1H_SEC_READ 0x4B /* 主机文件模式/不支持SD卡: 以扇区为单位从当前位置读取数据块 */ /* 输入: 请求读取的扇区数 */ /* 输出中断 */

#define CMD1H_SEC_WRITE 0x4C /* 主机文件模式/不支持SD卡: 以扇区为单位在当前位置写入数据块 */ /* 输入: 请求写入的扇区数 */ /* 输出中断 */

#define CMD0H_DISK_BOC_CMD 0x50 /* 主机方式/不支持SD卡: 对USB存储器执行BulkOnly传输协议的命令 */ /* 输出中断 */

#define CMD5H_DISK_READ 0x54 /* 主机方式/不支持SD卡: 从USB存储器读物理扇区 */

/* 输入: LBA物理扇区地址(总长度32位, 低字节在前), 扇区数(01H~FFH) */ /* 输出中断 */

#define CMD0H_DISK_RD_GO 0x55 /* 主机方式/不支持SD卡: 继续执行USB存储器的物理扇区读操作 */ /* 输出中断 */

#define CMD5H_DISK_WRITE 0x56 /* 主机方式/不支持SD卡: 向USB存储器写物理扇区 */

/* 输入: LBA物理扇区地址(总长度32位, 低字节在前), 扇区数(01H~FFH) */ /* 输出中断 */

#define CMD0H_DISK_WR_GO 0x57 /* 主机方式/不支持SD卡: 继续执行USB存储器的物理扇区写操作 */ /* 输出中断 */ /*

********************************************************************************************************************* */

/* 辅助命令(手册二), 不太常用或者是为了与CH375和CH372兼容 */

#define CMD10_SET_USB_SPEED 0x04 /* 设置USB总线速度, 在每次CMD11_SET_USB_MODE设置USB工作模式时会自动恢复到12Mbps全速 */ /* 输入: 总线速度代码 */

/* 00H=12Mbps全速FullSpeed(默认值), 01H=1.5Mbps(仅修改频率), 02H=1.5Mbps低速LowSpeed */

#define CMD11_GET_DEV_RATE 0x0A /* 主机方式: 获取当前连接的USB设备的数据速率类型 */ /* 输入: 数据07H */

/* 输出: 数据速率类型 */

/* 位4为1则是1.5Mbps低速USB设备, 否则是12Mbps全速USB设备 */

#define CMD11_GET_TOGGLE 0x0A /* 获取OUT事务的同步状态 */ /* 输入: 数据1AH */ /* 输出: 同步状态 */

/* 位4为1则OUT事务同步, 否则OUT事务不同步 */

#define CMD11_READ_VAR8 0x0A /* 读取指定的8位文件系统变量 */ /* 输入: 变量地址 */ /* 输出: 数据 */

/*#define CMD11_GET_MAX_LUN = CMD11_READ_VAR8( VAR_UDISK_LUN )*/ /* 主机方式: 获取USB存储器最大和当前逻辑单元号 */

#define CMD20_SET_RETRY 0x0B /* 主机方式: 设置USB事务操作的重试次数 */

/* 输入: 数据25H, 重试次数 */

/* 位7为0则收到NAK时不重试, 位7为1位6为0则收到NAK时无限重试, 位7为1位6为1则收到NAK时最多重试3秒, 位5~位0为超时后的重试次数 */

#define CMD20_WRITE_VAR8 0x0B /* 设置指定的8位文件系统变量 */ /* 输入: 变量地址, 数据 */

/*#define CMD20_SET_DISK_LUN = CMD20_WRITE_VAR8( VAR_UDISK_LUN )*/ /* 主机方式: 设置USB存储器的当前逻辑单元号 */

#define CMD14_READ_VAR32 0x0C /* 读取指定的32位文件系统变量 */ /* 输入: 变量地址 */

/* 输出: 数据(总长度32位,低字节在前) */

#define CMD50_WRITE_VAR32 0x0D /* 设置指定的32位文件系统变量 */ /* 输入: 变量地址, 数据(总长度32位,低字节在前) */

#define CMD01_DELAY_100US 0x0F /* 延时100uS(串口不支持) */ /* 输出: 延时期间输出0,延时结束输出非0 */

#define CMD40_SET_USB_ID 0x12 /* 设备方式: 设置USB厂商VID和产品PID */ /* 输入: 厂商ID低字节, 厂商ID高字节, 产品ID低字节, 产品ID高字节 */

#define CMD10_SET_USB_ADDR 0x13 /* 设置USB地址 */ /* 输入: 地址值 */

#define CMD01_TEST_CONNECT 0x16 /* 主机方式/不支持SD卡: 检查USB设备连接状态 */

/* 输出: 状态( USB_INT_CONNECT或USB_INT_DISCONNECT或USB_INT_USB_READY, 其它值说明操作未完成 ) */

#define CMD00_ABORT_NAK 0x17 /* 主机方式: 放弃当前NAK的重试 */

#define CMD10_SET_ENDP2 0x18 /* 设备方式(串口不支持): 设置USB端点0的接收器 */

/* 输入: 工作方式 */

/* 位7为1则位6为同步触发位, 否则同步触发位不变 */

/* 位3~位0为事务响应方式: 0000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define CMD10_SET_ENDP3 0x19 /* 设备方式(串口不支持): 设置USB端点0的发送器 */

/* 输入: 工作方式 */

/* 位7为1则位6为同步触发位, 否则同步触发位不变 */

/* 位3~位0为事务响应方式: 0000~1000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define CMD10_SET_ENDP4 0x1A /* 设备方式(串口不支持): 设置USB端点1的接收器 */ /* 输入: 工作方式 */

/* 位7为1则位6为同步触发位, 否则同步触发位不变 */

/* 位3~位0为事务响应方式: 0000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define CMD10_SET_ENDP5 0x1B /* 设备方式(串口不支持): 设置USB端点1的发送器 */

/* 输入: 工作方式 */

/* 位7为1则位6为同步触发位, 否则同步触发位不变 */

/* 位3~位0为事务响应方式: 0000~1000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define CMD10_SET_ENDP6 0x1C /* 设置USB端点2/主机端点的接收器 */

/* 输入: 工作方式 */

/* 位7为1则位6为同步触发位, 否则同步触发位不变 */

/* 位3~位0为事务响应方式: 0000-就绪ACK, 1101-就绪但不返回ACK, 1110-正忙NAK, 1111-错误STALL */

#define CMD10_SET_ENDP7 0x1D /* 设置USB端点2/主机端点的发送器 */

/* 输入: 工作方式 */

/* 位7为1则位6为同步触发位, 否则同步触发位不变 */

/* 位3~位0为事务响应方式: 0000-就绪ACK, 1101-就绪但无须应答, 1110-正忙NAK, 1111-错误STALL */

#define CMD00_DIRTY_BUFFER 0x25 /* 主机文件模式: 清除内部的磁盘和文件缓冲区 */

#define CMD10_WR_USB_DATA3 0x29 /* 设备方式(串口不支持): 向USB端点0的发送缓冲区写入数据块 */ /* 输入: 长度, 数据流 */

#define CMD10_WR_USB_DATA5 0x2A /* 设备方式(串口不支持): 向USB端点1的发送缓冲区写入数据块 */ /* 输入: 长度, 数据流 */ /*

********************************************************************************************************************* */

/* 辅助命令(手册二), 不太常用或者是为了与CH375和CH372兼容, 以下命令总是在操作结束时产生中断通知, 并且总是没有输出数据 */

#define CMD1H_CLR_STALL 0x41 /* 主机方式: 控制传输-清除端点错误 */ /* 输入: 端点号 */ /* 输出中断 */

#define CMD1H_SET_ADDRESS 0x45 /* 主机方式: 控制传输-设置USB地址 */ /* 输入: 地址值 */

/* 输出中断 */

#define CMD1H_GET_DESCR 0x46 /* 主机方式: 控制传输-获取描述符 */ /* 输入: 描述符类型 */ /* 输出中断 */

#define CMD1H_SET_CONFIG 0x49 /* 主机方式: 控制传输-设置USB配置 */ /* 输入: 配置值 */ /* 输出中断 */

#define CMD0H_AUTO_SETUP 0x4D /* 主机方式/不支持SD卡: 自动配置USB设备 */ /* 输出中断 */

#define CMD2H_ISSUE_TKN_X 0x4E /* 主机方式: 发出同步令牌, 执行事务, 该命令可代替 CMD10_SET_ENDP6/CMD10_SET_ENDP7 + CMD1H_ISSUE_TOKEN */ /* 输入: 同步标志, 事务属性 */

/* 同步标志的位7为主机端点IN的同步触发位, 位6为主机端点OUT的同步触发位, 位5~位0必须为0 */

/* 事务属性的低4位是令牌, 高4位是端点号 */ /* 输出中断 */

#define CMD1H_ISSUE_TOKEN 0x4F /* 主机方式: 发出令牌, 执行事务, 建议用CMD2H_ISSUE_TKN_X命令 */ /* 输入: 事务属性 */

/* 低4位是令牌, 高4位是端点号 */ /* 输出中断 */

#define CMD0H_DISK_INIT 0x51 /* 主机方式/不支持SD卡: 初始化USB存储器 */

/* 输出中断 */

#define CMD0H_DISK_RESET 0x52 /* 主机方式/不支持SD卡: 控制传输-复位USB存储器 */ /* 输出中断 */

#define CMD0H_DISK_SIZE 0x53 /* 主机方式/不支持SD卡: 获取USB存储器的容量 */

/* 输出中断 */

#define CMD0H_DISK_INQUIRY 0x58 /* 主机方式/不支持SD卡: 查询USB存储器特性 */

/* 输出中断 */

#define CMD0H_DISK_READY 0x59 /* 主机方式/不支持SD卡: 检查USB存储器就绪 */

/* 输出中断 */

#define CMD0H_DISK_R_SENSE 0x5A /* 主机方式/不支持SD卡: 检查USB存储器错误 */ /* 输出中断 */

#define CMD0H_RD_DISK_SEC 0x5B /* 主机文件模式: 从磁盘读取一个扇区的数据到内部缓冲区 */ /* 输出中断 */

#define CMD0H_WR_DISK_SEC 0x5C /* 主机文件模式: 将内部缓冲区的一个扇区的数据写入磁盘 */ /* 输出中断 */

#define CMD0H_DISK_MAX_LUN 0x5D /* 主机方式: 控制传输-获取USB存储器最大逻辑单元号 */ /* 输出中断 */ /*

********************************************************************************************************************* */

/* 以下定义只是为了兼容CH375的INCLUDE文件中的命令名称格式 */

#ifndef _NO_CH375_COMPATIBLE_ #define CMD_GET_IC_VER CMD01_GET_IC_VER #define CMD_SET_BAUDRATE CMD21_SET_BAUDRATE #define CMD_ENTER_SLEEP CMD00_ENTER_SLEEP #define CMD_RESET_ALL CMD00_RESET_ALL #define CMD_CHECK_EXIST CMD11_CHECK_EXIST #define CMD_CHK_SUSPEND CMD20_CHK_SUSPEND #define CMD_SET_SDO_INT CMD20_SET_SDO_INT #define CMD_GET_FILE_SIZE CMD14_GET_FILE_SIZE #define CMD_SET_FILE_SIZE CMD50_SET_FILE_SIZE #define CMD_SET_USB_MODE CMD11_SET_USB_MODE #define CMD_GET_STATUS CMD01_GET_STATUS #define CMD_UNLOCK_USB CMD00_UNLOCK_USB #define CMD_RD_USB_DATA0 CMD01_RD_USB_DATA0 #define CMD_RD_USB_DATA CMD01_RD_USB_DATA #define CMD_WR_USB_DATA7 CMD10_WR_USB_DATA7 #define CMD_WR_HOST_DATA CMD10_WR_HOST_DATA #define CMD_WR_REQ_DATA CMD01_WR_REQ_DATA #define CMD_WR_OFS_DATA CMD20_WR_OFS_DATA #define CMD_SET_FILE_NAME CMD10_SET_FILE_NAME #define CMD_DISK_CONNECT CMD0H_DISK_CONNECT #define CMD_DISK_MOUNT CMD0H_DISK_MOUNT #define CMD_FILE_OPEN CMD0H_FILE_OPEN

#define CMD_FILE_ENUM_GO CMD0H_FILE_ENUM_GO #define CMD_FILE_CREATE CMD0H_FILE_CREATE #define CMD_FILE_ERASE CMD0H_FILE_ERASE #define CMD_FILE_CLOSE CMD1H_FILE_CLOSE #define CMD_DIR_INFO_READ CMD1H_DIR_INFO_READ #define CMD_DIR_INFO_SAVE CMD0H_DIR_INFO_SAVE #define CMD_BYTE_LOCATE CMD4H_BYTE_LOCATE #define CMD_BYTE_READ CMD2H_BYTE_READ #define CMD_BYTE_RD_GO CMD0H_BYTE_RD_GO #define CMD_BYTE_WRITE CMD2H_BYTE_WRITE #define CMD_BYTE_WR_GO CMD0H_BYTE_WR_GO #define CMD_DISK_CAPACITY CMD0H_DISK_CAPACITY

#define CMD_DISK_QUERY CMD0H_DISK_QUERY #define CMD_DIR_CREATE CMD0H_DIR_CREATE #define CMD_SEC_LOCATE CMD4H_SEC_LOCATE #define CMD_SEC_READ CMD1H_SEC_READ #define CMD_SEC_WRITE CMD1H_SEC_WRITE #define CMD_DISK_BOC_CMD CMD0H_DISK_BOC_CMD #define CMD_DISK_READ CMD5H_DISK_READ #define CMD_DISK_RD_GO CMD0H_DISK_RD_GO #define CMD_DISK_WRITE CMD5H_DISK_WRITE #define CMD_DISK_WR_GO CMD0H_DISK_WR_GO #define CMD_SET_USB_SPEED CMD10_SET_USB_SPEED #define CMD_GET_DEV_RATE CMD11_GET_DEV_RATE #define CMD_GET_TOGGLE CMD11_GET_TOGGLE #define CMD_READ_VAR8 CMD11_READ_VAR8 #define CMD_SET_RETRY CMD20_SET_RETRY #define CMD_WRITE_VAR8 CMD20_WRITE_VAR8 #define CMD_READ_VAR32 CMD14_READ_VAR32 #define CMD_WRITE_VAR32 CMD50_WRITE_VAR32 #define CMD_DELAY_100US CMD01_DELAY_100US #define CMD_SET_USB_ID CMD40_SET_USB_ID #define CMD_SET_USB_ADDR CMD10_SET_USB_ADDR #define CMD_TEST_CONNECT CMD01_TEST_CONNECT #define CMD_ABORT_NAK CMD00_ABORT_NAK #define CMD_SET_ENDP2 CMD10_SET_ENDP2 #define CMD_SET_ENDP3 CMD10_SET_ENDP3 #define CMD_SET_ENDP4 CMD10_SET_ENDP4 #define CMD_SET_ENDP5 CMD10_SET_ENDP5 #define CMD_SET_ENDP6 CMD10_SET_ENDP6 #define CMD_SET_ENDP7 CMD10_SET_ENDP7 #define CMD_DIRTY_BUFFER CMD00_DIRTY_BUFFER #define CMD_WR_USB_DATA3 CMD10_WR_USB_DATA3 #define CMD_WR_USB_DATA5 CMD10_WR_USB_DATA5 #define CMD_CLR_STALL CMD1H_CLR_STALL #define CMD_SET_ADDRESS CMD1H_SET_ADDRESS #define CMD_GET_DESCR CMD1H_GET_DESCR #define CMD_SET_CONFIG CMD1H_SET_CONFIG #define CMD_AUTO_SETUP CMD0H_AUTO_SETUP #define CMD_ISSUE_TKN_X CMD2H_ISSUE_TKN_X #define CMD_ISSUE_TOKEN CMD1H_ISSUE_TOKEN #define CMD_DISK_INIT CMD0H_DISK_INIT #define CMD_DISK_RESET CMD0H_DISK_RESET #define CMD_DISK_SIZE CMD0H_DISK_SIZE #define CMD_DISK_INQUIRY CMD0H_DISK_INQUIRY #define CMD_DISK_READY CMD0H_DISK_READY #define CMD_DISK_R_SENSE CMD0H_DISK_R_SENSE #define CMD_RD_DISK_SEC CMD0H_RD_DISK_SEC #define CMD_WR_DISK_SEC CMD0H_WR_DISK_SEC #define CMD_DISK_MAX_LUN CMD0H_DISK_MAX_LUN #endif /*

********************************************************************************************************************* */ /* 并口方式, 状态端口(读命令端口)的位定义 */

#ifndef PARA_STATE_INTB #define PARA_STATE_INTB 0x80 /* 并口方式状态端口的位7: 中断标志,低有效 */

#define PARA_STATE_BUSY 0x10 /* 并口方式状态端口的位4: 忙标志,高有效 */ #endif /*

********************************************************************************************************************* */ /* 串口方式, 操作命令前的引导同步码 */ #ifndef SER_CMD_TIMEOUT #define SER_CMD_TIMEOUT 32 /* 串口命令超时时间, 单位为mS, 同步码之间及同步码与命令码之间的间隔应该尽量短, 超时后的处理方式为丢弃 */ #define SER_SYNC_CODE1 0x57 /* 启动操作的第1个串口同步码 */ #define SER_SYNC_CODE2 0xAB /* 启动操作的第2个串口同步码 */ #endif /*

********************************************************************************************************************* */ /* 操作状态 */

#ifndef CMD_RET_SUCCESS #define CMD_RET_SUCCESS 0x51 /* 命令操作成功 */ #define CMD_RET_ABORT 0x5F /* 命令操作失败 */ #endif /*

********************************************************************************************************************* */ /* USB中断状态 */

#ifndef USB_INT_EP0_SETUP

/* 以下状态代码为特殊事件中断, 如果通过CMD20_CHK_SUSPEND启用USB总线挂起检查, 那么必须处理USB总线挂起和睡眠唤醒的中断状态 */ #define USB_INT_USB_SUSPEND0x05 /* USB总线挂起事件 */ #define USB_INT_WAKE_UP 0x06 /* 从睡眠中被唤醒事件 */

/* 以下状态代码0XH用于USB设备方式 */

/* 内置固件模式下只需要处理: USB_INT_EP1_OUT, USB_INT_EP1_IN, USB_INT_EP2_OUT, USB_INT_EP2_IN */

/* 位7-位4为0000 */

/* 位3-位2指示当前事务, 00=OUT, 10=IN, 11=SETUP */

/* 位1-位0指示当前端点, 00=端点0, 01=端点1, 10=端点2, 11=USB总线复位 */ #define USB_INT_EP0_SETUP 0x0C /* USB端点0的SETUP */ #define USB_INT_EP0_OUT 0x00 /* USB端点0的OUT */ #define USB_INT_EP0_IN 0x08 /* USB端点0的IN */ #define USB_INT_EP1_OUT 0x01 /* USB端点1的OUT */ #define USB_INT_EP1_IN 0x09 /* USB端点1的IN */ #define USB_INT_EP2_OUT 0x02 /* USB端点2的OUT */

#define USB_INT_EP2_IN 0x0A /* USB端点2的IN */ /* USB_INT_BUS_RESET 0x0000XX11B */ /* USB总线复位 */ #define USB_INT_BUS_RESET1 0x03 /* USB总线复位 */ #define USB_INT_BUS_RESET2 0x07 /* USB总线复位 */ #define USB_INT_BUS_RESET3 0x0B /* USB总线复位 */ #define USB_INT_BUS_RESET4 0x0F /* USB总线复位 */

#endif

/* 以下状态代码2XH-3XH用于USB主机方式的通讯失败代码 */ /* 位7-位6为00 */ /* 位5为1 */

/* 位4指示当前接收的数据包是否同步 */

/* 位3-位0指示导致通讯失败时USB设备的应答: 0010=ACK, 1010=NAK, 1110=STALL, 0011=DATA0, 1011=DATA1, XX00=超时 */ /* USB_INT_RET_ACK 0x001X0010B */ /* 错误:对于IN事务返回ACK */ /* USB_INT_RET_NAK 0x001X1010B */ /* 错误:返回NAK */ /* USB_INT_RET_STALL 0x001X1110B */ /* 错误:返回STALL */ /* USB_INT_RET_DATA0 0x001X0011B */ /* 错误:对于OUT/SETUP事务返回DATA0 */ /* USB_INT_RET_DATA1 0x001X1011B */ /* 错误:对于OUT/SETUP事务返回DATA1 */ /* USB_INT_RET_TOUT 0x001XXX00B */ /* 错误:返回超时 */ /* USB_INT_RET_TOGX 0x0010X011B */ /* 错误:对于IN事务返回数据不同步 */ /* USB_INT_RET_PID 0x001XXXXXB */ /* 错误:未定义 */

/* 以下状态代码1XH用于USB主机方式的操作状态代码 */ #ifndef USB_INT_SUCCESS #define USB_INT_SUCCESS 0x14 /* USB事务或者传输操作成功 */ #define USB_INT_CONNECT 0x15 /* 检测到USB设备连接事件, 可能是新连接或者断开后重新连接 */

#define USB_INT_DISCONNECT 0x16 /* 检测到USB设备断开事件 */ #define USB_INT_BUF_OVER 0x17 /* USB传输的数据有误或者数据太多缓冲区溢出 */

#define USB_INT_USB_READY 0x18 /* USB设备已经被初始化(已经分配USB地址) */

#define USB_INT_DISK_READ 0x1D /* USB存储器请求数据读出 */ #define USB_INT_DISK_WRITE 0x1E /* USB存储器请求数据写入 */ #define USB_INT_DISK_ERR 0x1F /* USB存储器操作失败 */ #endif

/* 以下状态代码用于主机文件模式下的文件系统错误码 */ #ifndef ERR_DISK_DISCON #define ERR_DISK_DISCON 0x82 /* 磁盘尚未连接,可能磁盘已经断开 */ #define ERR_LARGE_SECTOR 0x84 /* 磁盘的扇区太大,只支持每扇区512字节 */

#define ERR_TYPE_ERROR 0x92 /* 磁盘分区类型不支持,只支持FAT12/FAT16/BigDOS/FAT32,需要由磁盘管理工具重新分区 */ #define ERR_BPB_ERROR 0xA1 /* 磁盘尚未格式化,或者参数错误,需要由WINDOWS采用默认参数重新格式化 */ #define ERR_DISK_FULL 0xB1 /* 磁盘文件太满,剩余空间太少或者已经没有,需要磁盘整理 */

#define ERR_FDT_OVER 0xB2 /* 目录(文件夹)内文件太多,没有空闲的目录

项,FAT12/FAT16根目录下的文件数应该少于512个,需要磁盘整理 */ #define ERR_FILE_CLOSE 0xB4 /* 文件已经关闭,如果需要使用,应该重新打开文件 */

#define ERR_OPEN_DIR 0x41 /* 指定路径的目录(文件夹)被打开 */ #define ERR_MISS_FILE 0x42 /* 指定路径的文件没有找到,可能是文件名称错误 */

#define ERR_FOUND_NAME 0x43 /* 搜索到相匹配的文件名,或者是要求打开目录(文件夹)而实际结果却打开了文件 */ /* 以下文件系统错误码用于文件系统子程序 */ #define ERR_MISS_DIR 0xB3 /* 指定路径的某个子目录(文件夹)没有找到,可能是目录名称错误 */

#define ERR_LONG_BUF_OVER 0x48 /* 长文件缓冲区溢出 */ #define ERR_LONG_NAME_ERR 0x49 /* 短文件名没有对应的长文件名或者长文件名错误 */

#define ERR_NAME_EXIST 0x4A /* 同名的短文件已经存在,建议重新生成另外一个短文件名 */ #endif /*

********************************************************************************************************************* */

/* 以下状态代码用于主机文件模式下的磁盘及文件状态, VAR_DISK_STATUS */ #ifndef DEF_DISK_UNKNOWN

#define DEF_DISK_UNKNOWN 0x00 /* 尚未初始化,未知状态 */ #define DEF_DISK_DISCONN 0x01 /* 磁盘没有连接或者已经断开 */ #define DEF_DISK_CONNECT 0x02 /* 磁盘已经连接,但是尚未初始化或者无法识别该磁盘 */

#define DEF_DISK_MOUNTED 0x03 /* 磁盘已经初始化成功,但是尚未分析文件系统或者文件系统不支持 */ #define DEF_DISK_READY 0x10 /* 已经分析磁盘的文件系统并且能够支持 */

#define DEF_DISK_OPEN_ROOT 0x12 /* 已经打开根目录,使用后必须关闭,注意FAT12/FAT16根目录是固定长度 */ #define DEF_DISK_OPEN_DIR 0x13 /* 已经打开子目录(文件夹) */ #define DEF_DISK_OPEN_FILE 0x14 /* 已经打开文件 */ #endif /*

********************************************************************************************************************* */ /* 文件系统常用定义 */

#ifndef DEF_SECTOR_SIZE #define DEF_SECTOR_SIZE 512 /* U盘或者SD卡默认的物理扇区的大小 */ #endif

#ifndef DEF_WILDCARD_CHAR

#define DEF_WILDCARD_CHAR 0x2A /* 路径名的通配符 '*' */ #define DEF_SEPAR_CHAR1 0x5C /* 路径名的分隔符 '\\' */ #define DEF_SEPAR_CHAR2 0x2F /* 路径名的分隔符 '/' */ #define DEF_FILE_YEAR 2004 /* 默认文件日期: 2004年 */

#define DEF_FILE_MONTH 1 /* 默认文件日期: 1月 */ #define DEF_FILE_DATE 1 /* 默认文件日期: 1日 */ #endif

#ifndef ATTR_DIRECTORY

/* FAT数据区中文件目录信息 */ typedef struct _FAT_DIR_INFO { UINT8 DIR_Name[11]; /* 00H,文件名,共11字节,不足处填空格 */ UINT8 DIR_Attr; /* 0BH,文件属性,参考后面的说明 */ UINT8 DIR_NTRes; /* 0CH */ UINT8 DIR_CrtTimeTenth; /* 0DH,文件创建的时间,以0.1秒单位计数 */ UINT16 DIR_CrtTime; /* 0EH,文件创建的时间 */ UINT16 DIR_CrtDate; /* 10H,文件创建的日期 */ UINT16 DIR_LstAccDate; /* 12H,最近一次存取操作的日期 */ UINT16 DIR_FstClusHI; /* 14H */ UINT16 DIR_WrtTime; /* 16H,文件修改时间,参考前面的宏MAKE_FILE_TIME */ UINT16 DIR_WrtDate; /* 18H,文件修改日期,参考前面的宏MAKE_FILE_DATE */ UINT16 DIR_FstClusLO; /* 1AH */ UINT32 DIR_FileSize; /* 1CH,文件长度 */ } FAT_DIR_INFO, *P_FAT_DIR_INFO; /* 20H */

/* 文件属性 */

#define ATTR_READ_ONLY 0x01 /* 文件为只读属性 */ #define ATTR_HIDDEN 0x02 /* 文件为隐含属性 */ #define ATTR_SYSTEM 0x04 /* 文件为系统属性 */ #define ATTR_VOLUME_ID 0x08 /* 卷标 */ #define ATTR_DIRECTORY 0x10 /* 子目录(文件夹) */ #define ATTR_ARCHIVE 0x20 /* 文件为存档属性 */ #define ATTR_LONG_NAME ( ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID ) /* 长文件名属性 */ #define ATTR_LONG_NAME_MASK ( ATTR_LONG_NAME | ATTR_DIRECTORY | ATTR_ARCHIVE )

/* 文件属性 UINT8 */

/* bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 */ /* 只隐系卷目存未定义 */ /* 读藏统标录档 */ /* 文件时间 UINT16 */

/* Time = (Hour<<11) + (Minute<<5) + (Second>>1) */ #define MAKE_FILE_TIME( h, m, s ) ( (h<<11) + (m<<5) + (s>>1) ) /* 生成指定时分秒的文件时间数据 */

/* 文件日期 UINT16 */

/* Date = ((Year-1980)<<9) + (Month<<5) + Day */ #define MAKE_FILE_DATE( y, m, d ) ( ((y-1980)<<9) + (m<<5) + d ) /* 生成指定年月日的文件日期数据 */

#define LONE_NAME_MAX_CHAR (255*2) /* 长文件名最多字符数/字节数 */ #define LONG_NAME_PER_DIR (13*2) /* 长文件名在每个文件目录信息结构中的字符数/字节数 */

#endif /*

********************************************************************************************************************* */ /* SCSI命令和数据输入输出结构 */

#ifndef SPC_CMD_INQUIRY

/* SCSI命令码 */

#define SPC_CMD_INQUIRY 0x12 #define SPC_CMD_READ_CAPACITY 0x25 #define SPC_CMD_READ10 0x28 #define SPC_CMD_WRITE10 0x2A #define SPC_CMD_TEST_READY 0x00 #define SPC_CMD_REQUEST_SENSE 0x03 #define SPC_CMD_MODESENSE6 0x1A #define SPC_CMD_MODESENSE10 0x5A #define SPC_CMD_START_STOP 0x1B

/* BulkOnly协议的命令块 */ typedef struct _BULK_ONLY_CBW { UINT32 CBW_Sig; UINT32 CBW_Tag; UINT8 CBW_DataLen0; /* 08H,输入: 数据传输长度,对于输入数据其有效值是0到48,对于输出数据其有效值为0到33 */ UINT8 CBW_DataLen1; UINT16 CBW_DataLen2; UINT8 CBW_Flag; /* 0CH,输入: 传输方向等标志,位7为1则输入数据,位为0则输出数据或者没有数据 */ UINT8 CBW_LUN; UINT8 CBW_CB_Len; /* 0EH,输入: 命令块的长度,有效值是1到16 */ UINT8 CBW_CB_Buf[16]; /* 0FH,输入: 命令块,该缓冲区最多为16个字节 */

} BULK_ONLY_CBW, *P_BULK_ONLY_CBW; /* BulkOnly协议的命令块, 输入CBW结构 */

/* INQUIRY命令的返回数据 */ typedef struct _INQUIRY_DATA { UINT8 DeviceType; /* 00H, 设备类型 */ UINT8 RemovableMedia; /* 01H, 位7为1说明是移动存储 */ UINT8 Versions; /* 02H, 协议版本 */ UINT8 DataFormatAndEtc; /* 03H, 指定返回数据格式 */ UINT8 AdditionalLength; /* 04H, 后续数据的长度 */ UINT8 Reserved1; UINT8 Reserved2; UINT8 MiscFlag; /* 07H, 一些控制标志 */ UINT8 VendorIdStr[8]; /* 08H, 厂商信息 */ UINT8 ProductIdStr[16]; /* 10H, 产品信息 */ UINT8 ProductRevStr[4]; /* 20H, 产品版本 */ } INQUIRY_DATA, *P_INQUIRY_DATA; /* 24H */

/* REQUEST SENSE命令的返回数据 */ typedef struct _SENSE_DATA { UINT8 ErrorCode; /* 00H, 错误代码及有效位 */ UINT8 SegmentNumber; UINT8 SenseKeyAndEtc; /* 02H, 主键码 */ UINT8 Information0; UINT8 Information1; UINT8 Information2; UINT8 Information3; UINT8 AdditSenseLen; /* 07H, 后续数据的长度 */ UINT8 CmdSpecInfo[4]; UINT8 AdditSenseCode; /* 0CH, 附加键码 */ UINT8 AddSenCodeQual; /* 0DH, 详细的附加键码 */ UINT8 FieldReplaUnit; UINT8 SenseKeySpec[3]; } SENSE_DATA, *P_SENSE_DATA; /* 12H */

#endif /*

********************************************************************************************************************* */ /* 主机文件模式下的数据输入和输出结构 */

#ifndef MAX_FILE_NAME_LEN

#define MAX_FILE_NAME_LEN (13+1) /* 文件名最大长度,最大长度是1个根目录符+8个主文件名+1个小数点+3个类型名+结束符=14 */

/* 命令的输入数据和输出数据 */ typedef union _CH376_CMD_DATA { struct { UINT8 mBuffer[ MAX_FILE_NAME_LEN ]; } Default; INQUIRY_DATA DiskMountInq; /* 返回: INQUIRY命令的返回数据 */ /* CMD0H_DISK_MOUNT: 初始化磁盘并测试磁盘是否就绪,首次执行时 */ FAT_DIR_INFO OpenDirInfo; /* 返回: 枚举到的文件目录信息 */ /* CMD0H_FILE_OPEN: 枚举文件和目录(文件夹) */ FAT_DIR_INFO EnumDirInfo; /* 返回: 枚举到的文件目录信息 */ /* CMD0H_FILE_ENUM_GO: 继续枚举文件和目录(文件夹) */ struct { UINT8 mUpdateFileSz; /* 输入参数: 是否允许更新文件长度, 0则禁止更新长度 */ } FileCLose; /* CMD1H_FILE_CLOSE: 关闭当前已经打开的文件 */

struct { UINT8 mDirInfoIndex; /* 输入参数: 指定需要读取的目录信息结构在扇区内的索引号, 0FFH则为当前已经打开的文件 */ } DirInfoRead; /* CMD1H_DIR_INFO_READ: 读取文件的目录信息 */ union { UINT32 mByteOffset; /* 输入参数: 偏移字节数,以字节为单位的偏移量(总长度32位,低字节在前) */ UINT32 mSectorLba; /* 返回: 当前文件指针对应的绝对线性扇区号,0FFFFFFFFH则已到文件尾(总长度32位,低字节在前) */ } ByteLocate; /* CMD4H_BYTE_LOCATE: 以字节为单位移动当前文件指针 */ struct { UINT16 mByteCount; /* 输入参数: 请求读取的字节数(总长度16位,低字节在前) */ } ByteRead; /* CMD2H_BYTE_READ: 以字节为单位从当前位置读取数据块 */ struct { UINT16 mByteCount; /* 输入参数: 请求写入的字节数(总长度16位,低字节在前) */ } ByteWrite; /* CMD2H_BYTE_WRITE: 以字节为单位向当前位置写入数据块 */ union { UINT32 mSectorOffset; /* 输入参数: 偏移扇区数,以扇区为单位的偏移量(总长度32位,低字节在前) */ UINT32 mSectorLba; /* 返回: 当前文件指针对应的绝对线性扇区号,0FFFFFFFFH则已到文件尾(总长度32位,低字节在前) */ } SectorLocate; /* CMD4H_SEC_LOCATE: 以扇区为单位移动当前文件指针 */ struct { UINT8 mSectorCount; /* 输入参数: 请求读取的扇区数 */ /* 返回: 允许读取的扇区数 */ UINT8 mReserved1; UINT8 mReserved2; UINT8 mReserved3; UINT32 mStartSector; /* 返回: 允许读取的扇区块的起始绝对线性扇区号(总长度32位,低字节在前) */ } SectorRead; /* CMD1H_SEC_READ: 以扇区为单位从当前位置读取数据块 */ struct { UINT8 mSectorCount; /* 输入参数: 请求写入的扇区数 */ /* 返回: 允许写入的扇区数 */ UINT8 mReserved1; UINT8 mReserved2; UINT8 mReserved3;

UINT32 mStartSector; 扇区号(总长度32位,低字节在前) */ } SectorWrite;

位置写入数据块 */ struct { UINT32 mDiskSizeSec; 32位,低字节在前) */ } DiskCapacity;

量 */ struct { UINT32 mTotalSector; 位,低字节在前) */ UINT32 mFreeSector; 32位,低字节在前) */ UINT8 mDiskFat; 型,1-FAT12,2-FAT16,3-FAT32 */ } DiskQuery; BULK_ONLY_CBW DiskBocCbw;

器执行BulkOnly传输协议的命令 */ struct { UINT8 mMaxLogicUnit; */ } DiskMaxLun;

USB存储器最大逻辑单元号 */ INQUIRY_DATA DiskInitInq; */ INQUIRY_DATA DiskInqData; 器特性 */ SENSE_DATA ReqSenseData; */

器错误 */ struct { UINT32 mDiskSizeSec; 32位,高字节在前) */ } DiskSize;

容量 */ struct { UINT32 mStartSector; 节在前) */

/* 返回: 允许写入的扇区块的起始绝对线性/* CMD1H_SEC_WRITE: 以扇区为单位在当前

/* 返回: 整个物理磁盘的总扇区数(总长度/* CMD0H_DISK_CAPACITY: 查询磁盘物理容

/* 返回: 当前逻辑盘的总扇区数(总长度32/* 返回: 当前逻辑盘的剩余扇区数(总长度/* 返回: 当前逻辑盘的FAT

/* CMD_DiskQuery, 查询磁盘信息 */ /* 输入参数: CBW命令结构 */

/* CMD0H_DISK_BOC_CMD: 对USB存储

/* 返回: USB存储器的最大逻辑单元号

/* CMD0H_DISK_MAX_LUN: 控制传输-获取

/* 返回: INQUIRY命令的返回数据 */ /* CMD0H_DISK_INIT: 初始化USB存储器

/* 返回: INQUIRY命令的返回数据 */ /* CMD0H_DISK_INQUIRY: 查询USB存储

/* 返回: REQUEST SENSE命令的返回数据

/* CMD0H_DISK_R_SENSE: 检查USB存储

/* 返回: 整个物理磁盘的总扇区数(总长度

/* CMD0H_DISK_SIZE: 获取USB存储器的

/* 输入参数: LBA扇区地址(总长度32位,低字

UINT8 mSectorCount; /* 输入参数: 请求读取的扇区数 */ } DiskRead; /* CMD5H_DISK_READ: 从USB存储器读数据块(以扇区为单位) */ struct { UINT32 mStartSector; /* 输入参数: LBA扇区地址(总长度32位,低字节在前) */ UINT8 mSectorCount; /* 输入参数: 请求写入的扇区数 */ } DiskWrite; /* CMD5H_DISK_WRITE: 向USB存储器写数据块(以扇区为单位) */

} CH376_CMD_DATA, *P_CH376_CMD_DATA;

#endif /*

********************************************************************************************************************* */ /* 主机文件模式下的文件系统变量的地址 */

#ifndef VAR_FILE_SIZE

/* 8位/单字节变量 */

#define VAR_SYS_BASE_INFO 0x20 /* 当前系统的基本信息 */

/* 位6用于指示USB存储设备的子类别SubClass-Code, 位6为0则说明子类别为6, 位6为1则说明子类别是非6的其它值 */

/* 位5用于指示USB设备方式下的USB配置状态和USB主机方式下的USB设备连接状态 */

/* USB设备方式下, 位5为1则USB配置完成, 位5位0则尚未配置 */ /* USB主机方式下, 位5为1则USB端口存在USB设备, 位5位0则USB端口没有USB设备 */

/* 位4用于指示USB设备方式下的缓冲区锁定状态, 位4为1则说明USB缓冲区处于锁定状态, 位6为1则说明已经释放 */ /* 其它位, 保留,请勿修改 */ #define VAR_RETRY_TIMES 0x25 /* USB事务操作的重试次数 */

/* 位7为0则收到NAK时不重试, 位7为1位6为0则收到NAK时无限重试(可以用CMD_ABORT_NAK命令放弃重试), 位7为1位6为1则收到NAK时最多重试3秒 */ /* 位5~位0为超时后的重试次数 */ #define VAR_FILE_BIT_FLAG 0x26 /* 主机文件模式下的位标志 */

/* 位1和位0, 逻辑盘的FAT文件系统标志, 00-FAT12, 01-FAT16, 10-FAT32, 11-非法 */

/* 位2, 当前缓冲区中的FAT表数据是否被修改标志, 0-未修改, 1-已修改 */ /* 位3, 文件长度需要修改标志, 当前文件被追加数据, 0-未追加无需修改, 1-已追加需要修改 */

/* 其它位, 保留,请勿修改 */ #define VAR_DISK_STATUS 0x2B /* 主机文件模式下的磁盘及文件状态 */ #define VAR_SD_BIT_FLAG 0x30 /* 主机文件模式下SD卡的位标志 */ /* 位0, SD卡版本, 0-只支持SD第一版,1-支持SD第二版 */ /* 位1, 自动识别, 0-SD卡, 1-MMC卡 */

/* 位2, 自动识别, 0-标准容量SD卡, 1-大容量SD卡(HC-SD) */ /* 位4, ACMD41命令超时 */ /* 位5, CMD1命令超时 */

/* 位6, CMD58命令超时 */ /* 其它位, 保留,请勿修改 */ #define VAR_UDISK_TOGGLE 0x31 /* USB存储设备的BULK-IN/BULK-OUT端点的同步标志 */

/* 位7, Bulk-In端点的同步标志 */ /* 位6, Bulk-In端点的同步标志 */ /* 位5~位0, 必须为0 */ #define VAR_UDISK_LUN 0x34 /* USB存储设备的逻辑单元号 */

/* 位7~位4, USB存储设备的当前逻辑单元号,CH376初始化USB存储设备后,默认是访问0#逻辑单元 */

/* 位3~位0, USB存储设备的最大逻辑单元号,加1后等于逻辑单元数 */ #define VAR_SEC_PER_CLUS 0x38 /* 逻辑盘的每簇扇区数 */ #define VAR_FILE_DIR_INDEX 0x3B /* 当前文件目录信息在扇区内的索引号 */ #define VAR_CLUS_SEC_OFS 0x3C /* 当前文件指针在簇内的扇区偏移,为0xFF则指向文件末尾,簇结束 */

/* 32位/4字节变量 */

#define VAR_DISK_ROOT 0x44 /* 对于FAT16盘为根目录占用扇区数,对于FAT32盘为根目录起始簇号(总长度32位,低字节在前) */ #define VAR_DSK_TOTAL_CLUS 0x48 /* 逻辑盘的总簇数(总长度32位,低字节在前) */

#define VAR_DSK_START_LBA 0x4C /* 逻辑盘的起始绝对扇区号LBA(总长度32位,低字节在前) */

#define VAR_DSK_DAT_START 0x50 /* 逻辑盘的数据区域的起始LBA(总长度32位,低字节在前) */

#define VAR_LBA_BUFFER 0x54 /* 当前磁盘数据缓冲区的数据对应的LBA(总长度32位,低字节在前) */ #define VAR_LBA_CURRENT 0x58 /* 当前读写的磁盘起始LBA地址(总长度32位,低字节在前) */

#define VAR_FAT_DIR_LBA 0x5C /* 当前文件目录信息所在的扇区LBA地址(总长度32位,低字节在前) */

#define VAR_START_CLUSTER 0x60 /* 当前文件或者目录(文件夹)的起始簇号(总长度32位,低字节在前) */

#define VAR_CURRENT_CLUST 0x64 /* 当前文件的当前簇号(总长度32位,低字节在前) */

#define VAR_FILE_SIZE 0x68 /* 当前文件的长度(总长度32位,低字节在前) */ #define VAR_CURRENT_OFFSET 0x6C /* 当前文件指针,当前读写位置的字节偏移(总长度32位,低字节在前) */

#endif /*

********************************************************************************************************************* */ /* 常用USB定义 */

/* USB的包标识PID, 主机方式可能用到 */ #ifndef DEF_USB_PID_SETUP #define DEF_USB_PID_NULL 0x00 /* 保留PID, 未定义 */ #define DEF_USB_PID_SOF 0x05 #define DEF_USB_PID_SETUP 0x0D #define DEF_USB_PID_IN 0x09

#define DEF_USB_PID_OUT 0x01 #define DEF_USB_PID_ACK 0x02 #define DEF_USB_PID_NAK 0x0A #define DEF_USB_PID_STALL 0x0E #define DEF_USB_PID_DATA0 0x03 #define DEF_USB_PID_DATA1 0x0B #define DEF_USB_PID_PRE 0x0C #endif

/* USB请求类型, 外置固件模式可能用到 */ #ifndef DEF_USB_REQ_TYPE

#define DEF_USB_REQ_READ 0x80 /* 控制读操作 */ #define DEF_USB_REQ_WRITE 0x00 /* 控制写操作 */ #define DEF_USB_REQ_TYPE 0x60 /* 控制请求类型 */ #define DEF_USB_REQ_STAND 0x00 /* 标准请求 */ #define DEF_USB_REQ_CLASS 0x20 /* 设备类请求 */ #define DEF_USB_REQ_VENDOR 0x40 /* 厂商请求 */ #define DEF_USB_REQ_RESERVE 0x60 /* 保留请求 */ #endif

/* USB标准设备请求, RequestType的位6位5=00(Standard), 外置固件模式可能用到 */ #ifndef DEF_USB_GET_DESCR

#define DEF_USB_CLR_FEATURE 0x01 #define DEF_USB_SET_FEATURE 0x03 #define DEF_USB_GET_STATUS 0x00 #define DEF_USB_SET_ADDRESS 0x05 #define DEF_USB_GET_DESCR 0x06 #define DEF_USB_SET_DESCR 0x07 #define DEF_USB_GET_CONFIG 0x08 #define DEF_USB_SET_CONFIG 0x09 #define DEF_USB_GET_INTERF 0x0A #define DEF_USB_SET_INTERF 0x0B #define DEF_USB_SYNC_FRAME 0x0C #endif /*

********************************************************************************************************************* */

#ifdef __cplusplus }

#endif

#endif

CH376HFT.C文件

/* 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 #include #include

#define EN_DISK_QUERY 1 /* 启用磁盘查询 */

#include \"HAL.H\"

#include \"HAL_BASE.C\" #include \"DEBUG.H\" #include \"DEBUG.C\" #include \"SPI_SW.C\" /* 软件模拟SPI方式 */ #include \"FILE_SYS.H\" #include \"FILE_SYS.C\"

UINT8 idata buf[64];

void main(void) { UINT8 i, s; UINT16 len; 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( \"等待U盘或者SD卡插入---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( \"U盘厂商---UdiskInfo: %s\\n\ } /* 读取文件 */ 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\ 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; /* 强制文件名字符串结束以便打印输出 */ /* 打印名称,原始8+3格式,未整理成含小数点分隔符 */ printf( \"*** EnumName: %s\\n\ } 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 );

printf(\"\\n\");

strcpy( TarName, \"\\\\YI.TXT\" ); /* 目标文件名 */

/*--- 新建另外一个文件;文件名:yimingtest.txt ---*/ printf( \"创建另外一个文件---Create\\n\" ); s = CH376FileCreatePath( TarName ); mStopIfError( s );

printf( \"新文件被写入----Write\\n\" ); strcpy( buf, \"恭喜发财了!\\xd\\n\" );

s = CH376ByteWrite( buf, strlen(buf) , NULL); mStopIfError ( s );

printf( \"文件被关闭 --- Close\\n\" ); s = CH376FileClose(TRUE); mStopIfError( s );

/*--- 追加文件数据的例子 ---*/ printf( \"\\n\" );

strcpy( SrcName, \"\\\\YI.TXT\" );

s = CH376FileOpenPath( SrcName ); printf( \"Error: %02X\\n\ if( s == USB_INT_SUCCESS ) { //成功打开文件 strcpy( buf, \"是不是要请哥们吃饭了呢?\\xd\\n\" ); CH376ByteLocate(0xFFFFFFFF); s = CH376ByteWrite( buf, strlen(buf) , NULL); mStopIfError ( s ); printf( \"文件被关闭 --- Close\\n\" ); s = CH376FileClose(TRUE); mStopIfError( s ); } else mStopIfError( s );

/*--- 读取文件数据的例子 ---*/ printf( \"\\n\" );

printf( \"读取文件数据的例子:\\n\" ); strcpy( SrcName, \"\\\\YI.TXT\" );

s = CH376FileOpenPath( SrcName ); printf( \"Error: %02X\\n\ if( s == USB_INT_SUCCESS ) { len = CH376GetFileSize(); CH376ByteLocate( 0 ); CH376ByteRead(buf,len,NULL); printf( \"%s \\n\ mStopIfError ( s ); printf( \"文件被关闭 --- Close\\n\" ); s = CH376FileClose(TRUE); mStopIfError( s ); } else

mStopIfError( s ); } else { /* 找到文件或者出错 */ mStopIfError( s ); }

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

SPI_SW.C文件

/* CH376芯片软件模拟SPI串行连接的硬件抽象层 V1.0 */ /* 提供I/O接口子程序 */

#include \"HAL.H\"

/* 本例中的硬件连接方式如下(实际应用电路可以参照修改下述定义及子程序) */ /* 单片机的引脚 CH376芯片的引脚 P1.4 SCS P1.5 SDI P1.6 SDO

P1.7 SCK */ sbit P24 = P2^4; sbit P25 = P2^5; sbit P26 = P2^6; sbit P27 = P2^7; #define CH376_SPI_SCS P24 /* 假定CH376的SCS引脚 */ #define CH376_SPI_SDI P25 /* 假定CH376的SDI引脚 */ #define CH376_SPI_SDO P26 /* 假定CH376的SDO引脚 */ #define CH376_SPI_SCK P27 /* 假定CH376的SCK引脚 */

#define CH376_INT_WIRE INT0 /* 假定CH376的INT#引脚,如果未连接那么也可以通过查询兼做中断输出的SDO引脚状态实现 */

void CH376_PORT_INIT( void ) /* 由于使用软件模拟SPI读写时序,所以进行初始化 */ {

/* 如果是硬件SPI接口,那么可使用mode3(CPOL=1&CPHA=1)或mode0(CPOL=0&CPHA=0),CH376在时钟上升沿采样输入,下降沿输出,数据位是高位在前 */ CH376_SPI_SCS = 1; /* 禁止SPI片选 */ CH376_SPI_SCK = 1; /* 默认为高电平,SPI模式3,也可以用SPI模式0,但模拟程序可能需稍做修改 */

/* 对于双向I/O引脚模拟SPI接口,那么必须在此设置SPI_SCS,SPI_SCK,SPI_SDI为输出方向,SPI_SDO为输入方向 */ }

void mDelay0_5uS( void ) /* 至少延时0.5uS,根据单片机主频调整 */ { }

void Spi376OutByte( UINT8 d ) /* SPI输出8个位数据 */ { /* 如果是硬件SPI接口,应该是先将数据写入SPI数据寄存器,然后查询SPI状态寄存器以等待SPI字节传输完成 */ UINT8 i; for ( i = 0; i < 8; i ++ ) { CH376_SPI_SCK = 0; if ( d & 0x80 ) CH376_SPI_SDI = 1; else CH376_SPI_SDI = 0; d <<= 1; /* 数据位是高位在前 */ CH376_SPI_SCK = 1; /* CH376在时钟上升沿采样输入 */ } }

UINT8 Spi376InByte( void ) /* SPI输入8个位数据 */ { /* 如果是硬件SPI接口,应该是先查询SPI状态寄存器以等待SPI字节传输完成,然后从SPI数据寄存器读出数据 */ UINT8 i, d; d = 0; for ( i = 0; i < 8; i ++ ) { CH376_SPI_SCK = 0; /* CH376在时钟下降沿输出 */ d <<= 1; /* 数据位是高位在前 */ if ( CH376_SPI_SDO ) d ++; CH376_SPI_SCK = 1; } return( d ); }

#define xEndCH376Cmd( ) { CH376_SPI_SCS = 1; } /* SPI片选无效,结束CH376命令,仅用于SPI接口方式 */

void xWriteCH376Cmd( UINT8 mCmd ) /* 向CH376写命令 */ { CH376_SPI_SCS = 1; /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */ mDelay0_5uS( ); /* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出方向,SPI_SDO为输入方向 */ CH376_SPI_SCS = 0; /* SPI片选有效 */

Spi376OutByte( mCmd ); /* 发出命令码 */ /* 延时1.5uS确保读写周期大于1.5uS,或者用上面一行的状态查询代替 */ mDelay0_5uS( ); mDelay0_5uS( ); mDelay0_5uS( ); }

#ifdef FOR_LOW_SPEED_MCU /* 不需要延时 */

#define xWriteCH376Data( d ) { Spi376OutByte( d ); } /* 向CH376写数据 */ #define xReadCH376Data( ) ( Spi376InByte( ) ) /* 从CH376读数据 */ #else

void xWriteCH376Data( UINT8 mData ) /* 向CH376写数据 */ { Spi376OutByte( mData ); }

UINT8 xReadCH376Data( void ) /* 从CH376读数据 */ { return( Spi376InByte( ) ); }

#endif

/* 查询CH376中断(INT#低电平) */ UINT8 Query376Interrupt( void ) { /* 如果连接了CH376的中断引脚则直接查询中断引脚 */ return( CH376_INT_WIRE ? FALSE : TRUE ); }

UINT8 mInitCH376Host( void ) /* 初始化CH376 */ { UINT8 res; CH376_PORT_INIT( ); /* 接口硬件初始化 */ xWriteCH376Cmd( CMD11_CHECK_EXIST ); /* 测试单片机与CH376之间的通讯接口 */ xWriteCH376Data( 0x65 ); //0110 0101 res = xReadCH376Data( ); xEndCH376Cmd( ); if ( res != 0x9A ) //1001 1010 return( ERR_USB_UNKNOWN ); /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */ xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */ xWriteCH376Data( 0x06 ); mDelayuS( 20 ); /*--- 设置CH376工作模式之后,读取其结果状态 ---*/ res = xReadCH376Data( ); xEndCH376Cmd( ); if ( res == CMD_RET_SUCCESS ) return( USB_INT_SUCCESS ); else return( ERR_USB_UNKNOWN ); /* 设置模式错误 */ }

FILE_SYS.H文件

/* CH376芯片文件系统层 V1.0 */

/* 提供文件系统常用子程序,提供命令打包 */

/* 不使用的子程序可以注释掉,从而节约单片机的程序ROM空间和数据RAM空间 */

/* 这里的子程序是通过括号中的变量传递参数,如果参数较多,为了节约RAM,也可以参考CH375子程序库改成通过同一全局变量/联合结构CH376_CMD_DATA传递 */

/* name 参数是指短文件名, 可以包括根目录符, 但不含有路径分隔符, 总长度不超过1+8+1+3+1字节 */

/* PathName 参数是指全路径的短文件名, 包括根目录符、多级子目录及路径分隔符、文件名/目录名 */

/* LongName 参数是指长文件名, 以UNICODE小端顺序编码, 以两个0字节结束, 使用长文件名子程序必须先定义全局缓冲区GlobalBuf, 长度不小于64字节, 可以与其它子程序共用 */

/* 定义 NO_DEFAULT_CH376_INT 用于禁止默认的Wait376Interrupt子程序,禁止后,应用程序必须自行定义一个同名子程序 */

/* 定义 DEF_INT_TIMEOUT 用于设置默认的Wait376Interrupt子程序中的等待中断的超时时间/循环计数值, 0则不检查超时而一直等待 */

/* 定义 EN_DIR_CREATE 用于提供新建多级子目录的子程序,默认是不提供 */

/* 定义 EN_DISK_QUERY 用于提供磁盘容量查询和剩余空间查询的子程序,默认是不提供 */

/* 定义 EN_SECTOR_ACCESS 用于提供以扇区为单位读写文件的子程序,默认是不提供 */ /* 定义 EN_LONG_NAME 用于提供支持长文件名的子程序,默认是不提供 */

#ifndef __CH376_FS_H__ #define __CH376_FS_H__

#include \"HAL.H\"

#define STRUCT_OFFSET( s, m ) ( (UINT8)( & ((s *)0) -> m ) ) /* 定义获取结构成员相对偏移地址的宏 */

#ifdef EN_LONG_NAME

#ifndef LONG_NAME_BUF_LEN

#define LONG_NAME_BUF_LEN ( LONG_NAME_PER_DIR * 20 ) /* 自行定义的长文件名缓冲区长度,最小值为LONG_NAME_PER_DIR*1 */ #endif #endif

UINT8 CH376ReadBlock( PUINT8 buf ); /* 从当前主机端点的接收缓冲区读取数据块,返回长度 */

UINT8 CH376WriteReqBlock( PUINT8 buf ); /* 向内部指定缓冲区写入请求的数据块,返回长度 */

void CH376WriteHostBlock( PUINT8 buf, UINT8 len ); /* 向USB主机端点的发送缓冲区写入数据块 */

void CH376WriteOfsBlock( PUINT8 buf, UINT8 ofs, UINT8 len ); /* 向内部缓冲区指定偏移地

址写入数据块 */

void CH376SetFileName( PUINT8 name ); /* 设置将要操作的文件的文件名 */

UINT32 CH376Read32bitDat( void ); /* 从CH376芯片读取32位的数据并结束命令 */

UINT8 CH376ReadVar8( UINT8 var ); /* 读CH376芯片内部的8位变量 */

void CH376WriteVar8( UINT8 var, UINT8 dat ); /* 写CH376芯片内部的8位变量 */

UINT32 CH376ReadVar32( UINT8 var ); /* 读CH376芯片内部的32位变量 */

void CH376WriteVar32( UINT8 var, UINT32 dat ); /* 写CH376芯片内部的32位变量 */

void CH376EndDirInfo( void ); /* 在调用CH376DirInfoRead获取FAT_DIR_INFO结构之后应该通知CH376结束 */

UINT32 CH376GetFileSize( void ); /* 读取当前文件长度 */

UINT8 CH376GetDiskStatus( void ); /* 获取磁盘和文件系统的工作状态 */

UINT8 CH376GetIntStatus( void ); /* 获取中断状态并取消中断请求 */

#ifndef NO_DEFAULT_CH376_INT

UINT8 Wait376Interrupt( void ); /* 等待CH376中断(INT#低电平),返回中断状态码, 超时则返回ERR_USB_UNKNOWN */ #endif

UINT8 CH376SendCmdWaitInt( UINT8 mCmd ); /* 发出命令码后,等待中断 */

UINT8 CH376SendCmdDatWaitInt( UINT8 mCmd, UINT8 mDat ); /* 发出命令码和一字节数据后,等待中断 */

UINT8 CH376DiskReqSense( void ); /* 检查USB存储器错误 */

UINT8 CH376DiskConnect( void ); /* 检查U盘是否连接,不支持SD卡 */

UINT8 CH376DiskMount( void ); /* 初始化磁盘并测试磁盘是否就绪 */

UINT8 CH376FileOpen( PUINT8 name ); /* 在根目录或者当前目录下打开文件或者目录(文件夹) */

UINT8 CH376FileCreate( PUINT8 name ); /* 在根目录或者当前目录下新建文件,如果文件已经存在那么先删除 */

UINT8 CH376DirCreate( PUINT8 name ); /* 在根目录下新建目录(文件夹)并打开,如果目录已经存在那么直接打开 */

UINT8 CH376SeparatePath( PUINT8 path ); /* 从路径中分离出最后一级文件名或者目录(文件夹)名,返回最后一级文件名或者目录名的字节偏移 */

UINT8 CH376FileOpenDir( PUINT8 PathName, UINT8 StopName ); /* 打开多级目录下的文

件或者目录的上级目录,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */ /* StopName 指向最后一级文件名或者目录名 */

UINT8 CH376FileOpenPath( PUINT8 PathName ); /* 打开多级目录下的文件或者目录(文件夹),支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

UINT8 CH376FileCreatePath( PUINT8 PathName ); /* 新建多级目录下的文件,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

#ifdef EN_DIR_CREATE

UINT8 CH376DirCreatePath( PUINT8 PathName ); /* 新建多级目录下的目录(文件夹)并打开,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */ #endif

UINT8 CH376FileErase( PUINT8 PathName ); /* 删除文件,如果已经打开则直接删除,否则对于文件会先打开再删除,支持多级目录路径 */

UINT8 CH376FileClose( UINT8 UpdateSz ); /* 关闭当前已经打开的文件或者目录(文件夹) */

UINT8 CH376DirInfoRead( void ); /* 读取当前文件的目录信息 */

UINT8 CH376DirInfoSave( void ); /* 保存文件的目录信息 */

UINT8 CH376ByteLocate( UINT32 offset ); /* 以字节为单位移动当前文件指针 */

UINT8 CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ); /* 以字节为单位从当前位置读取数据块 */

UINT8 CH376ByteWrite( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ); /* 以字节为单位向当前位置写入数据块 */

#ifdef EN_DISK_QUERY

UINT8 CH376DiskCapacity( PUINT32 DiskCap ); /* 查询磁盘物理容量,扇区数 */

UINT8 CH376DiskQuery( PUINT32 DiskFre ); /* 查询磁盘剩余空间信息,扇区数 */

#endif

UINT8 CH376SecLocate( UINT32 offset ); /* 以扇区为单位移动当前文件指针 */

#ifdef EN_SECTOR_ACCESS

UINT8 CH376DiskReadSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ); /* 从U盘读取多个扇区的数据块到缓冲区,不支持SD卡 */

UINT8 CH376DiskWriteSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ); /* 将缓冲区中的多个扇区的数据块写入U盘,不支持SD卡 */

UINT8 CH376SecRead( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ); /* 以扇区为单位从当前位置读取数据块,不支持SD卡 */

UINT8 CH376SecWrite( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ); /* 以扇区为单位在当前位置写入数据块,不支持SD卡 */

#endif

#ifdef EN_LONG_NAME

UINT8 CH376LongNameWrite( PUINT8 buf, UINT16 ReqCount ); /* 长文件名专用的字节写子程序 */

UINT8 CH376CheckNameSum( PUINT8 DirName ); /* 计算长文件名的短文件名检验和,输入为无小数点分隔符的固定11字节格式 */

UINT8 CH376LocateInUpDir( PUINT8 PathName ); /* 在上级目录(文件夹)中移动文件指针到当前文件目录信息所在的扇区 */

/* 另外,顺便将当前文件目录信息所在的扇区的前一个扇区的LBA地址写入CH376内部VAR_FAT_DIR_LBA变量(为了方便收集长文件名时向前搜索,否则要多移动一次) */ /* 使用了全局缓冲区GlobalBuf的前12个字节 */

UINT8 CH376GetLongName( PUINT8 PathName, PUINT8 LongName ); /* 由短文件名或者目录(文件夹)名获得相应的长文件名 */

/* 需要输入短文件名的完整路径PathName,需要提供缓冲区接收长文件名LongName(以UNICODE小端编码,以双0结束) */

/* 使用了全局缓冲区GlobalBuf的前34个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)+2 */

UINT8 CH376CreateLongName( PUINT8 PathName, PUINT8 LongName ); /* 新建具有长文件名的文件,关闭文件后返回,LongName输入路径必须在RAM中 */

/* 需要输入短文件名的完整路径PathName(请事先参考FAT规范由长文件名自行产生),需要输入以UNICODE小端编码的以双0结束的长文件名LongName */

/* 使用了全局缓冲区GlobalBuf的前64个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)*2 */

#endif

#endif

FILE_SYS.C文件

/* CH376芯片文件系统层 V1.1 */

/* 提供文件系统常用子程序,提供命令打包 */

/* 不使用的子程序可以注释掉,从而节约单片机的程序ROM空间和数据RAM空间 */

/* 这里的子程序是通过括号中的变量传递参数,如果参数较多,为了节约RAM,也可以参考CH375子程序库改成通过同一全局变量/联合结构CH376_CMD_DATA传递 */

/* name 参数是指短文件名, 可以包括根目录符, 但不含有路径分隔符, 总长度不超过1+8+1+3+1字节 */

/* PathName 参数是指全路径的短文件名, 包括根目录符、多级子目录及路径分隔符、文件名/目录名 */

/* LongName 参数是指长文件名, 以UNICODE小端顺序编码, 以两个0字节结束, 使用长文件名子程序必须先定义全局缓冲区GlobalBuf, 长度不小于64字节, 可以与其它子程序共用 */

/* 定义 NO_DEFAULT_CH376_INT 用于禁止默认的Wait376Interrupt子程序,禁止后,应用程

序必须自行定义一个同名子程序 */

/* 定义 DEF_INT_TIMEOUT 用于设置默认的Wait376Interrupt子程序中的等待中断的超时时间/循环计数值, 0则不检查超时而一直等待 */

/* 定义 EN_DIR_CREATE 用于提供新建多级子目录的子程序,默认是不提供 */

/* 定义 EN_DISK_QUERY 用于提供磁盘容量查询和剩余空间查询的子程序,默认是不提供 */

/* 定义 EN_SECTOR_ACCESS 用于提供以扇区为单位读写文件的子程序,默认是不提供 */ /* 定义 EN_LONG_NAME 用于提供支持长文件名的子程序,默认是不提供 */

#include \"FILE_SYS.H\"

UINT8 CH376ReadBlock( PUINT8 buf ) /* 从当前主机端点的接收缓冲区读取数据块,返回长度 */ { UINT8 s, l; xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); s = l = xReadCH376Data( ); /* 长度 */ if ( l ) { do { *buf = xReadCH376Data( ); buf ++; }while ( -- l ); } xEndCH376Cmd( ); return( s ); }

UINT8 CH376WriteReqBlock( PUINT8 buf ) /* 向内部指定缓冲区写入请求的数据块,返回长度 */ { UINT8 s, l; xWriteCH376Cmd( CMD01_WR_REQ_DATA ); s = l = xReadCH376Data( ); /* 长度 */ if ( l ) { do { xWriteCH376Data( *buf ); buf ++; } while ( -- l ); } xEndCH376Cmd( ); return( s ); }

void CH376WriteHostBlock( PUINT8 buf, UINT8 len ) /* 向USB主机端点的发送缓冲区写入数据块 */ { xWriteCH376Cmd( CMD10_WR_HOST_DATA ); xWriteCH376Data( len ); /* 长度 */ if ( len ) { do {

xWriteCH376Data( *buf ); buf ++; } while ( -- len ); } xEndCH376Cmd( ); }

void CH376WriteOfsBlock( PUINT8 buf, UINT8 ofs, UINT8 len ) /* 向内部缓冲区指定偏移地址写入数据块 */ { xWriteCH376Cmd( CMD20_WR_OFS_DATA ); xWriteCH376Data( ofs ); /* 偏移地址 */ xWriteCH376Data( len ); /* 长度 */ if ( len ) { do { xWriteCH376Data( *buf ); buf ++; } while ( -- len ); } xEndCH376Cmd( ); }

void CH376SetFileName( PUINT8 name ) /* 设置将要操作的文件的文件名 */ { UINT8 c; xWriteCH376Cmd( CMD10_SET_FILE_NAME ); c = *name; xWriteCH376Data( c ); while ( c ) { name ++; c = *name; if ( c == DEF_SEPAR_CHAR1 || c == DEF_SEPAR_CHAR2 ) c = 0; /* 强行将文件名截止 */ xWriteCH376Data( c ); } xEndCH376Cmd( ); }

UINT32 CH376Read32bitDat( void ) /* 从CH376芯片读取32位的数据并结束命令 */ { UINT8 c0, c1, c2, c3; c0 = xReadCH376Data( ); c1 = xReadCH376Data( ); c2 = xReadCH376Data( ); c3 = xReadCH376Data( ); xEndCH376Cmd( ); return( c0 | (UINT16)c1 << 8 | (UINT32)c2 << 16 | (UINT32)c3 << 24 ); }

UINT8 CH376ReadVar8( UINT8 var ) /* 读CH376芯片内部的8位变量 */ {

UINT8 c0; xWriteCH376Cmd( CMD11_READ_VAR8 ); xWriteCH376Data( var ); c0 = xReadCH376Data( ); xEndCH376Cmd( ); return( c0 ); }

void CH376WriteVar8( UINT8 var, UINT8 dat ) /* 写CH376芯片内部的8位变量 */ { xWriteCH376Cmd( CMD20_WRITE_VAR8 ); xWriteCH376Data( var ); xWriteCH376Data( dat ); xEndCH376Cmd( ); }

UINT32 CH376ReadVar32( UINT8 var ) /* 读CH376芯片内部的32位变量 */ { xWriteCH376Cmd( CMD14_READ_VAR32 ); xWriteCH376Data( var ); return( CH376Read32bitDat( ) ); /* 从CH376芯片读取32位的数据并结束命令 */ }

void CH376WriteVar32( UINT8 var, UINT32 dat ) /* 写CH376芯片内部的32位变量 */ { xWriteCH376Cmd( CMD50_WRITE_VAR32 ); xWriteCH376Data( var ); xWriteCH376Data( (UINT8)dat ); xWriteCH376Data( (UINT8)( (UINT16)dat >> 8 ) ); xWriteCH376Data( (UINT8)( dat >> 16 ) ); xWriteCH376Data( (UINT8)( dat >> 24 ) ); xEndCH376Cmd( ); }

void CH376EndDirInfo( void ) /* 在调用CH376DirInfoRead获取FAT_DIR_INFO结构之后应该通知CH376结束 */ { CH376WriteVar8( 0x0D, 0x00 ); }

UINT32 CH376GetFileSize( void ) /* 读取当前文件长度 */ { return( CH376ReadVar32( VAR_FILE_SIZE ) ); }

UINT8 CH376GetDiskStatus( void ) /* 获取磁盘和文件系统的工作状态 */ { return( CH376ReadVar8( VAR_DISK_STATUS ) ); }

UINT8 CH376GetIntStatus( void ) /* 获取中断状态并取消中断请求 */ { UINT8 s;

xWriteCH376Cmd( CMD01_GET_STATUS ); s = xReadCH376Data( ); xEndCH376Cmd( ); return( s ); }

UINT8 Wait376Interrupt( void ) /* 等待CH376中断(INT#低电平),返回中断状态码, 超时则返回ERR_USB_UNKNOWN */ { UINT32 i; for ( i = 0; i < 5000000; i ++ ) { /* 计数防止超时,默认的超时时间,与单片机主频有关 */ if ( Query376Interrupt( ) ) return( CH376GetIntStatus( ) ); /* 检测到中断 */ /* 在等待CH376中断的过程中,可以做些需要及时处理的其它事情 */ } return( ERR_USB_UNKNOWN ); /* 不应该发生的情况 */ }

UINT8 CH376SendCmdWaitInt( UINT8 mCmd ) /* 发出命令码后,等待中断 */ { xWriteCH376Cmd( mCmd ); xEndCH376Cmd( ); return( Wait376Interrupt( ) ); }

UINT8 CH376SendCmdDatWaitInt( UINT8 mCmd, UINT8 mDat ) /* 发出命令码和一字节数据后,等待中断 */ { xWriteCH376Cmd( mCmd ); xWriteCH376Data( mDat ); xEndCH376Cmd( ); return( Wait376Interrupt( ) ); }

UINT8 CH376DiskReqSense( void ) /* 检查USB存储器错误 */ { UINT8 s; mDelaymS( 5 ); s = CH376SendCmdWaitInt( CMD0H_DISK_R_SENSE ); mDelaymS( 5 ); return( s ); }

UINT8 CH376DiskConnect( void ) /* 检查U盘是否连接,不支持SD卡 */ { if( Query376Interrupt( ) ) /*--- 就是简单的查询中断口的电平状态 ---*/ CH376GetIntStatus( ); /* 检测到中断 */ return( CH376SendCmdWaitInt( CMD0H_DISK_CONNECT ) ); }

UINT8 CH376DiskMount( void ) /* 初始化磁盘并测试磁盘是否就绪 */ { return( CH376SendCmdWaitInt( CMD0H_DISK_MOUNT ) ); }

UINT8 CH376FileOpen( PUINT8 name ) /* 在根目录或者当前目录下打开文件或者目录(文件夹) */ { CH376SetFileName( name ); /* 设置将要操作的文件的文件名 */ if ( name[0] == DEF_SEPAR_CHAR1 || name[0] == DEF_SEPAR_CHAR2 ) CH376WriteVar32( VAR_CURRENT_CLUST, 0 ); return( CH376SendCmdWaitInt( CMD0H_FILE_OPEN ) ); }

UINT8 CH376FileCreate( PUINT8 name ) /* 在根目录或者当前目录下新建文件,如果文件已经存在那么先删除 */ { if ( name ) CH376SetFileName( name ); /* 设置将要操作的文件的文件名 */ return( CH376SendCmdWaitInt( CMD0H_FILE_CREATE ) ); }

UINT8 CH376DirCreate( PUINT8 name ) /* 在根目录下新建目录(文件夹)并打开,如果目录已经存在那么直接打开 */ { CH376SetFileName( name ); /* 设置将要操作的文件的文件名 */ if ( name[0] == DEF_SEPAR_CHAR1 || name[0] == DEF_SEPAR_CHAR2 ) CH376WriteVar32( VAR_CURRENT_CLUST, 0 ); return( CH376SendCmdWaitInt( CMD0H_DIR_CREATE ) ); }

UINT8 CH376SeparatePath( PUINT8 path ) /* 从路径中分离出最后一级文件名或者目录(文件夹)名,返回最后一级文件名或者目录名的字节偏移 */ { PUINT8 pName; for ( pName = path; *pName != 0; ++ pName ); /* 到文件名字符串结束位置 */ while ( *pName != DEF_SEPAR_CHAR1 && *pName != DEF_SEPAR_CHAR2 && pName != path ) pName --; /* 搜索倒数第一个路径分隔符 */ if ( pName != path ) pName ++; /* 找到了路径分隔符,则修改指向目标文件的最后一级文件名,跳过前面的多级目录名及路径分隔符 */ return( pName - path ); }

/* 打开多级目录下的文件或者目录的上级目录,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

UINT8 CH376FileOpenDir( PUINT8 PathName, UINT8 StopName ) /* StopName 指向最后一级文件名或者目录名 */ { UINT8 i, s; s = 0; i = 1; /* 跳过有可能的根目录符 */ while ( 1 ) {

while ( PathName[i] != DEF_SEPAR_CHAR1 && PathName[i] != DEF_SEPAR_CHAR2 && PathName[i] != 0 ) ++ i; /* 搜索下一个路径分隔符或者路径结束符 */ if ( PathName[i] ) i ++; /* 找到了路径分隔符,修改指向目标文件的最后一级文件名 */ else i = 0; /* 路径结束 */ s = CH376FileOpen( &PathName[s] ); /* 打开文件或者目录 */ if ( i && i != StopName ) { /* 路径尚未结束 */ 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 ); /* 操作出错 */ } s = i; /* 从下一级目录开始继续 */ } else return( s ); /* 路径结束,USB_INT_SUCCESS为成功打开文件,ERR_OPEN_DIR为成功打开目录(文件夹),其它为操作出错 */ } }

UINT8 CH376FileOpenPath( PUINT8 PathName ) /* 打开多级目录下的文件或者目录(文件夹),支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */ { return( CH376FileOpenDir( PathName, 0xFF ) ); }

UINT8 CH376FileCreatePath( PUINT8 PathName ) /* 新建多级目录下的文件,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */ { UINT8 s; UINT8 Name; Name = CH376SeparatePath( PathName ); /* 从路径中分离出最后一级文件名,返回最后一级文件名的偏移 */ if ( Name ) { /* 是多级目录 */ s = CH376FileOpenDir( PathName, Name ); /* 打开多级目录下的最后一级目录,即打开新建文件的上级目录 */ 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 ); /* 操作出错 */ } } return( CH376FileCreate( &PathName[Name] ) ); /* 在根目录或者当前目录下新建文件 */ }

#ifdef EN_DIR_CREATE

UINT8 CH376DirCreatePath( PUINT8 PathName ) /* 新建多级目录下的目录(文件夹)并打开,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */ { UINT8 s; UINT8 Name; UINT8 ClustBuf[4]; Name = CH376SeparatePath( PathName ); /* 从路径中分离出最后一级目录名,返回最后一级文件名的偏移 */ if ( Name ) { /* 是多级目录 */ s = CH376FileOpenDir( PathName, Name ); /* 打开多级目录下的最后一级目录,即打开新建目录的上级目录 */ 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 ); /* 操作出错 */ } xWriteCH376Cmd( CMD14_READ_VAR32 ); xWriteCH376Data( VAR_START_CLUSTER ); /* 上级目录的起始簇号 */ for ( s = 0; s != 4; s ++ ) ClustBuf[ s ] = xReadCH376Data( ); xEndCH376Cmd( ); s = CH376DirCreate( &PathName[Name] ); /* 在当前目录下新建目录 */ if ( s != USB_INT_SUCCESS ) return( s ); s = CH376ByteLocate( sizeof(FAT_DIR_INFO) + STRUCT_OFFSET( FAT_DIR_INFO, DIR_FstClusHI ) ); /* 移动文件指针 */ if ( s != USB_INT_SUCCESS ) return( s ); s = CH376ByteWrite( &ClustBuf[2], 2, NULL ); /* 写入上级目录的起始簇号的高16位 */ if ( s != USB_INT_SUCCESS ) return( s ); s = CH376ByteLocate( sizeof(FAT_DIR_INFO) + STRUCT_OFFSET( FAT_DIR_INFO, DIR_FstClusLO ) ); /* 移动文件指针 */ if ( s != USB_INT_SUCCESS ) return( s ); s = CH376ByteWrite( ClustBuf, 2, NULL ); /* 写入上级目录的起始簇号的低16位 */ if ( s != USB_INT_SUCCESS ) return( s ); s = CH376ByteLocate( 0 ); /* 移动文件指针,恢复到目录头位置 */ if ( s != USB_INT_SUCCESS ) return( s ); }

else { /* 不是多级目录 */ if ( PathName[0] == DEF_SEPAR_CHAR1 || PathName[0] == DEF_SEPAR_CHAR2 ) return( CH376DirCreate( PathName ) ); /* 在根目录下新建目录 */ else return( ERR_MISS_DIR ); /* 必须提供完整路径才能实现在当前目录下新建目录 */ } }

#endif

UINT8 CH376FileErase( PUINT8 PathName ) /* 删除文件,如果已经打开则直接删除,否则对于文件会先打开再删除,支持多级目录路径 */ { UINT8 s; if ( PathName ) { /* 文件尚未打开 */ for ( s = 1; PathName[s] != DEF_SEPAR_CHAR1 && PathName[s] != DEF_SEPAR_CHAR2 && PathName[s] != 0; ++ s ); /* 搜索下一个路径分隔符或者路径结束符 */ if ( PathName[s] ) { /* 有路径分隔符,是多级目录下的文件或者目录 */ s = CH376FileOpenPath( PathName ); /* 打开多级目录下的文件或者目录 */ if ( s != USB_INT_SUCCESS && s != ERR_OPEN_DIR ) return( s ); /* 操作出错 */ } else CH376SetFileName( PathName ); /* 没有路径分隔符,是根目录或者当前目录下的文件或者目录,设置将要操作的文件的文件名 */ } return( CH376SendCmdWaitInt( CMD0H_FILE_ERASE ) ); }

UINT8 CH376FileClose( UINT8 UpdateSz ) /* 关闭当前已经打开的文件或者目录(文件夹) */ { return( CH376SendCmdDatWaitInt( CMD1H_FILE_CLOSE, UpdateSz ) ); }

UINT8 CH376DirInfoRead( void ) /* 读取当前文件的目录信息 */ { return( CH376SendCmdDatWaitInt( CMD1H_DIR_INFO_READ, 0xFF ) ); }

UINT8 CH376DirInfoSave( void ) /* 保存文件的目录信息 */ { return( CH376SendCmdWaitInt( CMD0H_DIR_INFO_SAVE ) ); }

UINT8 CH376ByteLocate( UINT32 offset ) /* 以字节为单位移动当前文件指针 */ { xWriteCH376Cmd( CMD4H_BYTE_LOCATE ); xWriteCH376Data( (UINT8)offset ); xWriteCH376Data( (UINT8)((UINT16)offset>>8) ); xWriteCH376Data( (UINT8)(offset>>16) ); xWriteCH376Data( (UINT8)(offset>>24) ); xEndCH376Cmd( ); return( Wait376Interrupt( ) ); }

UINT8 CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ) /* 以字节为单位从当前位置读取数据块 */ { UINT8 s; xWriteCH376Cmd( CMD2H_BYTE_READ ); xWriteCH376Data( (UINT8)ReqCount ); xWriteCH376Data( (UINT8)(ReqCount>>8) ); xEndCH376Cmd( ); if ( RealCount ) *RealCount = 0; while ( 1 ) { s = Wait376Interrupt( ); if ( s == USB_INT_DISK_READ ) { s = CH376ReadBlock( buf ); /* 从当前主机端点的接收缓冲区读取数据块,返回长度 */ xWriteCH376Cmd( CMD0H_BYTE_RD_GO ); xEndCH376Cmd( ); buf += s; if ( RealCount ) *RealCount += s; } /* else if ( s == USB_INT_SUCCESS ) return( s );*/ /* 结束 */ else return( s ); /* 错误 */ } }

UINT8 CH376ByteWrite( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount ) /* 以字节为单位向当前位置写入数据块 */ { UINT8 s; xWriteCH376Cmd( CMD2H_BYTE_WRITE ); xWriteCH376Data( (UINT8)ReqCount ); xWriteCH376Data( (UINT8)(ReqCount>>8) ); xEndCH376Cmd( ); if ( RealCount ) *RealCount = 0; while ( 1 ) { s = Wait376Interrupt( ); if ( s == USB_INT_DISK_WRITE ) { s = CH376WriteReqBlock( buf ); /* 向内部指定缓冲区写入请求的数据块,返回长度 */ xWriteCH376Cmd( CMD0H_BYTE_WR_GO ); xEndCH376Cmd( ); buf += s; if ( RealCount ) *RealCount += s; } else return( s ); /* 错误 */ } }

#ifdef EN_DISK_QUERY

UINT8 CH376DiskCapacity( PUINT32 DiskCap ) /* 查询磁盘物理容量,扇区数 */ { UINT8 s; s = CH376SendCmdWaitInt( CMD0H_DISK_CAPACITY ); if ( s == USB_INT_SUCCESS ) { /* 参考CH376INC.H文件中CH376_CMD_DATA结构的DiskCapacity */ xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); xReadCH376Data( ); /* 长度总是sizeof(CH376_CMD_DATA.DiskCapacity) */ *DiskCap = CH376Read32bitDat( ); /* CH376_CMD_DATA.DiskCapacity.mDiskSizeSec,从CH376芯片读取32位的数据并结束命令 */ } else *DiskCap = 0; return( s ); }

UINT8 CH376DiskQuery( PUINT32 DiskFre ) /* 查询磁盘剩余空间信息,扇区数 */ { UINT8 s; UINT8 c0, c1, c2, c3; s = CH376SendCmdWaitInt( CMD0H_DISK_QUERY ); if ( s == USB_INT_SUCCESS ) { /* 参考CH376INC.H文件中CH376_CMD_DATA结构的DiskQuery */ xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); xReadCH376Data( ); /* 长度总是sizeof(CH376_CMD_DATA.DiskQuery) */ xReadCH376Data( ); /* CH376_CMD_DATA.DiskQuery.mTotalSector */ xReadCH376Data( ); xReadCH376Data( ); xReadCH376Data( ); c0 = xReadCH376Data( ); /* CH376_CMD_DATA.DiskQuery.mFreeSector */ c1 = xReadCH376Data( ); c2 = xReadCH376Data( ); c3 = xReadCH376Data( ); *DiskFre = c0 | (UINT16)c1 << 8 | (UINT32)c2 << 16 | (UINT32)c3 << 24; xReadCH376Data( ); /* CH376_CMD_DATA.DiskQuery.mDiskFat */ xEndCH376Cmd( ); } else *DiskFre = 0; return( s ); }

#endif

UINT8 CH376SecLocate( UINT32 offset ) /* 以扇区为单位移动当前文件指针 */ { xWriteCH376Cmd( CMD4H_SEC_LOCATE ); xWriteCH376Data( (UINT8)offset ); xWriteCH376Data( (UINT8)((UINT16)offset>>8) ); xWriteCH376Data( (UINT8)(offset>>16) ); xWriteCH376Data( 0 ); /* 超出最大文件尺寸 */ xEndCH376Cmd( ); return( Wait376Interrupt( ) ); }

#ifdef EN_SECTOR_ACCESS

UINT8 CH376DiskReadSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ) /* 从U盘读取多个扇区的数据块到缓冲区,不支持SD卡 */

/* iLbaStart 是准备读取的线性起始扇区号, iSectorCount 是准备读取的扇区数 */ { UINT8 s, err; UINT16 mBlockCount; for ( err = 0; err != 3; ++ err ) { /* 出错重试 */ xWriteCH376Cmd( CMD5H_DISK_READ ); /* 从USB存储器读扇区 */ xWriteCH376Data( (UINT8)iLbaStart ); /* LBA的最低8位 */ xWriteCH376Data( (UINT8)( (UINT16)iLbaStart >> 8 ) ); xWriteCH376Data( (UINT8)( iLbaStart >> 16 ) ); xWriteCH376Data( (UINT8)( iLbaStart >> 24 ) ); /* LBA的最高8位 */ xWriteCH376Data( iSectorCount ); /* 扇区数 */ xEndCH376Cmd( ); for ( mBlockCount = iSectorCount * DEF_SECTOR_SIZE / CH376_DAT_BLOCK_LEN; mBlockCount != 0; -- mBlockCount ) { /* 数据块计数 */ s = Wait376Interrupt( ); /* 等待中断并获取状态 */ if ( s == USB_INT_DISK_READ ) { /* USB存储器读数据块,请求数据读出 */ s = CH376ReadBlock( buf ); /* 从当前主机端点的接收缓冲区读取数据块,返回长度 */ xWriteCH376Cmd( CMD0H_DISK_RD_GO ); /* 继续执行USB存储器的读操作 */ xEndCH376Cmd( ); buf += s; } else break; /* 返回错误状态 */ } if ( mBlockCount == 0 ) { s = Wait376Interrupt( ); /* 等待中断并获取状态 */ if ( s == USB_INT_SUCCESS ) return( USB_INT_SUCCESS ); /* 操作成功 */ } if ( s == USB_INT_DISCONNECT ) return( s ); /* U盘被移除 */ CH376DiskReqSense( ); /* 检查USB存储器错误 */ } return( s ); /* 操作失败 */ }

UINT8 CH376DiskWriteSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount ) /* 将缓冲区中的多个扇区的数据块写入U盘,不支持SD卡 */

/* iLbaStart 是写入的线起始性扇区号, iSectorCount 是写入的扇区数 */ { UINT8 s, err; UINT16 mBlockCount; for ( err = 0; err != 3; ++ err ) { /* 出错重试 */ xWriteCH376Cmd( CMD5H_DISK_WRITE ); /* 向USB存储器写扇区 */ xWriteCH376Data( (UINT8)iLbaStart ); /* LBA的最低8位 */ xWriteCH376Data( (UINT8)( (UINT16)iLbaStart >> 8 ) ); xWriteCH376Data( (UINT8)( iLbaStart >> 16 ) ); xWriteCH376Data( (UINT8)( iLbaStart >> 24 ) ); /* LBA的最高8位 */ xWriteCH376Data( iSectorCount ); /* 扇区数 */

xEndCH376Cmd( ); for ( mBlockCount = iSectorCount * DEF_SECTOR_SIZE / CH376_DAT_BLOCK_LEN; mBlockCount != 0; -- mBlockCount ) { /* 数据块计数 */ s = Wait376Interrupt( ); /* 等待中断并获取状态 */ if ( s == USB_INT_DISK_WRITE ) { /* USB存储器写数据块,请求数据写入 */ CH376WriteHostBlock( buf, CH376_DAT_BLOCK_LEN ); /* 向USB主机端点的发送缓冲区写入数据块 */ xWriteCH376Cmd( CMD0H_DISK_WR_GO ); /* 继续执行USB存储器的写操作 */ xEndCH376Cmd( ); buf += CH376_DAT_BLOCK_LEN; } else break; /* 返回错误状态 */ } if ( mBlockCount == 0 ) { s = Wait376Interrupt( ); /* 等待中断并获取状态 */ if ( s == USB_INT_SUCCESS ) return( USB_INT_SUCCESS ); /* 操作成功 */ } if ( s == USB_INT_DISCONNECT ) return( s ); /* U盘被移除 */ CH376DiskReqSense( ); /* 检查USB存储器错误 */ } return( s ); /* 操作失败 */ }

UINT8 CH376SecRead( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ) /* 以扇区为单位从当前位置读取数据块,不支持SD卡 */ { UINT8 s; UINT8 cnt; UINT32 StaSec; UINT32 fsz, fofs; if ( RealCount ) *RealCount = 0; do { xWriteCH376Cmd( CMD01_GET_IC_VER ); cnt = xReadCH376Data( ); if ( cnt == 0x41 ) { xWriteCH376Cmd( CMD14_READ_VAR32 ); xWriteCH376Data( VAR_FILE_SIZE ); xReadCH376Data( ); fsz = xReadCH376Data( ); fsz |= (UINT16)(xReadCH376Data( )) << 8; cnt = xReadCH376Data( ); fsz |= (UINT32)cnt << 16; xWriteCH376Cmd( CMD14_READ_VAR32 ); xWriteCH376Data( VAR_CURRENT_OFFSET ); xReadCH376Data( ); fofs = xReadCH376Data( ); fofs |= (UINT16)(xReadCH376Data( )) << 8; fofs |= (UINT32)(xReadCH376Data( )) << 16; if ( fsz >= fofs + 510 ) CH376WriteVar8( VAR_FILE_SIZE + 3, 0xFF ); else cnt = 0xFF; } else cnt = 0xFF;

xWriteCH376Cmd( CMD1H_SEC_READ ); xWriteCH376Data( ReqCount ); xEndCH376Cmd( ); s = Wait376Interrupt( ); if ( cnt != 0xFF ) CH376WriteVar8( VAR_FILE_SIZE + 3, cnt ); if ( s != USB_INT_SUCCESS ) return( s ); xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); xReadCH376Data( ); /* 长度总是sizeof(CH376_CMD_DATA.SectorRead) */ cnt = xReadCH376Data( ); /* CH376_CMD_DATA.SectorRead.mSectorCount */ xReadCH376Data( ); xReadCH376Data( ); xReadCH376Data( ); StaSec = CH376Read32bitDat( ); /* CH376_CMD_DATA.SectorRead.mStartSector,从CH376芯片读取32位的数据并结束命令 */ if ( cnt == 0 ) break; s = CH376DiskReadSec( buf, StaSec, cnt ); /* 从U盘读取多个扇区的数据块到缓冲区 */ if ( s != USB_INT_SUCCESS ) return( s ); buf += cnt * DEF_SECTOR_SIZE; if ( RealCount ) *RealCount += cnt; ReqCount -= cnt; } while ( ReqCount ); return( s ); }

UINT8 CH376SecWrite( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount ) /* 以扇区为单位在当前位置写入数据块,不支持SD卡 */ { UINT8 s; UINT8 cnt; UINT32 StaSec; if ( RealCount ) *RealCount = 0; do { xWriteCH376Cmd( CMD1H_SEC_WRITE ); xWriteCH376Data( ReqCount ); xEndCH376Cmd( ); s = Wait376Interrupt( ); if ( s != USB_INT_SUCCESS ) return( s ); xWriteCH376Cmd( CMD01_RD_USB_DATA0 ); xReadCH376Data( ); /* 长度总是sizeof(CH376_CMD_DATA.SectorWrite) */ cnt = xReadCH376Data( ); /* CH376_CMD_DATA.SectorWrite.mSectorCount */ xReadCH376Data( ); xReadCH376Data( ); xReadCH376Data( ); StaSec = CH376Read32bitDat( ); /* CH376_CMD_DATA.SectorWrite.mStartSector,从CH376芯片读取32位的数据并结束命令 */ if ( cnt == 0 ) break; s = CH376DiskWriteSec( buf, StaSec, cnt ); /* 将缓冲区中的多个扇区的数据块写入U盘 */ if ( s != USB_INT_SUCCESS ) return( s ); buf += cnt * DEF_SECTOR_SIZE; if ( RealCount ) *RealCount += cnt; ReqCount -= cnt;

} while ( ReqCount ); return( s ); }

#endif

#ifdef EN_LONG_NAME

UINT8 CH376LongNameWrite( PUINT8 buf, UINT16 ReqCount ) /* 长文件名专用的字节写子程序 */ { UINT8 s, c; c = CH376ReadVar8( VAR_DISK_STATUS ); if ( c == DEF_DISK_OPEN_ROOT ) CH376WriteVar8( VAR_DISK_STATUS, DEF_DISK_OPEN_DIR ); xWriteCH376Cmd( CMD2H_BYTE_WRITE ); xWriteCH376Data( (UINT8)ReqCount ); xWriteCH376Data( (UINT8)(ReqCount>>8) ); xEndCH376Cmd( ); while ( 1 ) { s = Wait376Interrupt( ); if ( s == USB_INT_DISK_WRITE ) { if ( buf ) buf += CH376WriteReqBlock( buf ); /* 向内部指定缓冲区写入请求的数据块,返回长度 */ else { xWriteCH376Cmd( CMD01_WR_REQ_DATA ); /* 向内部指定缓冲区写入请求的数据块 */ s = xReadCH376Data( ); /* 长度 */ while ( s -- ) xWriteCH376Data( 0 ); /* 填充0 */ } xWriteCH376Cmd( CMD0H_BYTE_WR_GO ); xEndCH376Cmd( ); } /* else if ( s == USB_INT_SUCCESS ) return( s );*/ /* 结束 */ else { if ( c == DEF_DISK_OPEN_ROOT ) CH376WriteVar8( VAR_DISK_STATUS, c ); return( s ); /* 错误 */ } } }

UINT8 CH376CheckNameSum( PUINT8 DirName ) /* 计算长文件名的短文件名检验和,输入为无小数点分隔符的固定11字节格式 */ { UINT8 NameLen; UINT8 CheckSum; CheckSum = 0;

for ( NameLen = 0; NameLen != 11; NameLen ++ ) CheckSum = ( CheckSum & 1 ? 0x80 : 0x00 ) + ( CheckSum >> 1 ) + *DirName++; return( CheckSum ); }

UINT8 CH376LocateInUpDir( PUINT8 PathName ) /* 在上级目录(文件夹)中移动文件指针

到当前文件目录信息所在的扇区 */

/* 另外,顺便将当前文件目录信息所在的扇区的前一个扇区的LBA地址写入CH376内部VAR_FAT_DIR_LBA变量(为了方便收集长文件名时向前搜索,否则要多移动一次) */ /* 使用了全局缓冲区GlobalBuf的前12个字节 */ { UINT8 s; xWriteCH376Cmd( CMD14_READ_VAR32 ); xWriteCH376Data( VAR_FAT_DIR_LBA ); /* 当前文件目录信息所在的扇区LBA地址 */ for ( s = 4; s != 8; s ++ ) GlobalBuf[ s ] = xReadCH376Data( ); /* 临时保存于全局缓冲区中,节约RAM */ xEndCH376Cmd( ); s = CH376SeparatePath( PathName ); /* 从路径中分离出最后一级文件名或者目录名,返回最后一级文件名或者目录名的偏移 */ if ( s ) s = CH376FileOpenDir( PathName, s ); /* 是多级目录,打开多级目录下的最后一级目录,即打开文件的上级目录 */ else s = CH376FileOpen( \"/\" ); /* 根目录下的文件,则打开根目录 */ if ( s != ERR_OPEN_DIR ) return( s ); *(PUINT32)(&GlobalBuf[0]) = 0; /* 目录扇区偏移扇区数,保存在全局缓冲区中,节约RAM */ while ( 1 ) { /* 不断移动文件指针,直到与当前文件目录信息所在的扇区LBA地址匹配 */ s = CH376SecLocate( *(PUINT32)(&GlobalBuf[0]) ); /* 以扇区为单位在上级目录中移动文件指针 */ if ( s != USB_INT_SUCCESS ) return( s ); CH376ReadBlock( &GlobalBuf[8] ); /* 从内存缓冲区读取CH376_CMD_DATA.SectorLocate.mSectorLba数据块,返回长度总是sizeof(CH376_CMD_DATA.SectorLocate) */ if ( *(PUINT32)(&GlobalBuf[8]) == *(PUINT32)(&GlobalBuf[4]) ) return( USB_INT_SUCCESS ); /* 已到当前文件目录信息扇区 */ xWriteCH376Cmd( CMD50_WRITE_VAR32 ); xWriteCH376Data( VAR_FAT_DIR_LBA ); /* 得到前一个扇区,设置为新的文件目录信息扇区LBA地址 */ for ( s = 8; s != 12; s ++ ) xWriteCH376Data( GlobalBuf[ s ] ); xEndCH376Cmd( ); ++ *(PUINT32)(&GlobalBuf[0]); } }

UINT8 CH376GetLongName( PUINT8 PathName, PUINT8 LongName ) /* 由短文件名或者目录(文件夹)名获得相应的长文件名 */

/* 需要输入短文件名的完整路径PathName,需要提供缓冲区接收长文件名LongName(以UNICODE小端编码,以双0结束) */

/* 使用了全局缓冲区GlobalBuf的前34个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)+2 */ { UINT8 s; UINT16 NameCount; /* 长文件名字节计数 */ s = CH376FileOpenPath( PathName ); /* 打开多级目录下的文件或者目录 */ if ( s != USB_INT_SUCCESS && s != ERR_OPEN_DIR ) return( s ); s = CH376DirInfoRead( ); /* 读取当前文件的目录信息FAT_DIR_INFO,将相关数据调到内存中 */ if ( s != USB_INT_SUCCESS ) return( s ); CH376ReadBlock( GlobalBuf ); /* 从内存缓冲区读取FAT_DIR_INFO数据块,返回长度总

是sizeof(FAT_DIR_INFO) */ CH376EndDirInfo( ); /* 获取完FAT_DIR_INFO结构 */ GlobalBuf[32] = CH376CheckNameSum( GlobalBuf ); /* 计算长文件名的短文件名检验和,保存在全局缓冲区中,节约RAM */ GlobalBuf[33] = CH376ReadVar8( VAR_FILE_DIR_INDEX ); /* 当前文件目录信息在扇区内的索引号,保存在全局缓冲区中,节约RAM */ NameCount = 0; while ( 1 ) { if ( GlobalBuf[33] == 0 ) { /* 当前的文件目录信息扇区处理结束,转到前一个扇区 */ s = CH376LocateInUpDir( PathName ); /* 在上级目录中移动文件指针到当前文件目录信息所在的扇区 */ if ( s != USB_INT_SUCCESS ) break; if ( CH376ReadVar32( VAR_CURRENT_OFFSET ) == 0 ) { /* 当前已经处于目录扇区的开始,无法获取长文件名 */ s = ERR_LONG_NAME_ERR; break; } GlobalBuf[33] = DEF_SECTOR_SIZE / sizeof( FAT_DIR_INFO ); /* 指向前一个扇区的最后一个文件目录信息 */ } GlobalBuf[33] --; /* 从后向前搜索文件目录信息 */ s = CH376SendCmdDatWaitInt( CMD1H_DIR_INFO_READ, GlobalBuf[33] ); /* 读取指定的目录信息FAT_DIR_INFO,将相关数据调到内存中 */ if ( s != USB_INT_SUCCESS ) break; CH376ReadBlock( GlobalBuf ); /* 从内存缓冲区读取FAT_DIR_INFO数据块,返回长度总是sizeof(FAT_DIR_INFO) */ CH376EndDirInfo( ); /* 获取完FAT_DIR_INFO结构 */ if ( ( GlobalBuf[11] & ATTR_LONG_NAME_MASK ) != ATTR_LONG_NAME || GlobalBuf[13] != GlobalBuf[32] ) { /* 类型错误或者校验和错误 */ s = ERR_LONG_NAME_ERR; break; /* 没有直接返回是因为如果是打开了根目录那么必须要关闭后才能返回 */ } for ( s = 1; s < sizeof( FAT_DIR_INFO ); s += 2 ) { /* 收集长文件名,长文件名的字符在磁盘上UNICODE用小端方式存放 */ if ( s == 1 + 5 * 2 ) s = 14; /* 从长文件名的第一组1-5个字符跳到第二组6-11个字符 */ else if ( s == 14 + 6 * 2 ) s = 28; /* 从长文件名的第二组6-11个字符跳到第三组12-13个字符 */ LongName[ NameCount++ ] = GlobalBuf[ s ]; LongName[ NameCount++ ] = GlobalBuf[ s + 1 ]; if ( GlobalBuf[ s ] == 0 && GlobalBuf[ s + 1 ] == 0 ) break; /* 长文件名结束 */ if ( NameCount >= LONG_NAME_BUF_LEN ) { /* 长文件名缓冲区溢出 */ s = ERR_LONG_BUF_OVER; goto CH376GetLongNameE; } } if ( GlobalBuf[0] & 0x40 ) { /* 长文件名目录信息块结束 */ if ( s >= sizeof( FAT_DIR_INFO ) ) *(PUINT16)( &LongName[ NameCount ] ) = 0x0000; /* 尚未收集到长文件名的结束符,则强制结束 */ s = USB_INT_SUCCESS; /* 成功完成长文件名收集完成 */

break; } }

CH376GetLongNameE: CH376FileClose( FALSE ); /* 对于根目录则必须要关闭 */ return( s ); }

UINT8 CH376CreateLongName( PUINT8 PathName, PUINT8 LongName ) /* 新建具有长文件名的文件,关闭文件后返回,LongName输入路径必须在RAM中 */

/* 需要输入短文件名的完整路径PathName(请事先参考FAT规范由长文件名自行产生),需要输入以UNICODE小端编码的以双0结束的长文件名LongName */

/* 使用了全局缓冲区GlobalBuf的前64个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)*2 */ { UINT8 s, i; UINT8 DirBlockCnt; /* 长文件名占用文件目录结构的个数 */ UINT16 count; /* 临时变量,用于计数,用于字节读文件方式下实际读取的字节数 */ UINT16 NameCount; /* 长文件名字节计数 */ UINT32 NewFileLoc; /* 当前文件目录信息在上级目录中的起始位置,偏移地址 */ for ( count = 0; count < LONG_NAME_BUF_LEN; count += 2 ) if ( *(PUINT16)(&LongName[count]) == 0 ) break; /* 到结束位置 */ if ( count == 0 || count >= LONG_NAME_BUF_LEN || count > LONE_NAME_MAX_CHAR ) return( ERR_LONG_NAME_ERR ); /* 长文件名无效 */ DirBlockCnt = count / LONG_NAME_PER_DIR; /* 长文件名占用文件目录结构的个数 */ i = count - DirBlockCnt * LONG_NAME_PER_DIR; if ( i ) { /* 有零头 */ if ( ++ DirBlockCnt * LONG_NAME_PER_DIR > LONG_NAME_BUF_LEN ) return( ERR_LONG_BUF_OVER ); /* 缓冲区溢出 */ count += 2; /* 加上0结束符后的长度 */ i += 2; if ( i < LONG_NAME_PER_DIR ) { /* 最末的文件目录结构不满 */ while ( i++ < LONG_NAME_PER_DIR ) LongName[count++] = 0xFF; /* 把剩余数据填为0xFF */ } } s = CH376FileOpenPath( PathName ); /* 打开多级目录下的文件 */ if ( s == USB_INT_SUCCESS ) { /* 短文件名存在则返回错误 */ s = ERR_NAME_EXIST; goto CH376CreateLongNameE; } if ( s != ERR_MISS_FILE ) return( s ); s = CH376FileCreatePath( PathName ); /* 新建多级目录下的文件 */ if ( s != USB_INT_SUCCESS ) return( s ); i = CH376ReadVar8( VAR_FILE_DIR_INDEX ); /* 临时用于保存当前文件目录信息在扇区内的索引号 */ s = CH376LocateInUpDir( PathName ); /* 在上级目录中移动文件指针到当前文件目录信息所在的扇区 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; /* 没有直接返回是因为如果是打开了根目录那么必须要关闭后才能返回 */ NewFileLoc = CH376ReadVar32( VAR_CURRENT_OFFSET ) + i * sizeof(FAT_DIR_INFO); /*

计算当前文件目录信息在上级目录中的起始位置,偏移地址 */ s = CH376ByteLocate( NewFileLoc ); /* 在上级目录中移动文件指针到当前文件目录信息的位置 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; s = CH376ByteRead( &GlobalBuf[ sizeof(FAT_DIR_INFO) ], sizeof(FAT_DIR_INFO), NULL ); /* 以字节为单位读取数据,获得当前文件的目录信息FAT_DIR_INFO */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; for ( i = DirBlockCnt; i != 0; -- i ) { /* 搜索空闲的文件目录结构用于存放长文件名 */ s = CH376ByteRead( GlobalBuf, sizeof(FAT_DIR_INFO), &count ); /* 以字节为单位读取数据,获得下一个文件目录信息FAT_DIR_INFO */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; if ( count == 0 ) break; /* 无法读出数据,上级目录结束了 */ if ( GlobalBuf[0] && GlobalBuf[0] != 0xE5 ) { /* 后面有正在使用的文件目录结构,由于长文件名必须连接存放,所以空间不够,必须放弃当前位置并向后转移 */ s = CH376ByteLocate( NewFileLoc ); /* 在上级目录中移动文件指针到当前文件目录信息的位置 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; GlobalBuf[ 0 ] = 0xE5; /* 文件删除标志 */ for ( s = 1; s != sizeof(FAT_DIR_INFO); s ++ ) GlobalBuf[ s ] = GlobalBuf[ sizeof(FAT_DIR_INFO) + s ]; s = CH376LongNameWrite( GlobalBuf, sizeof(FAT_DIR_INFO) ); /* 写入一个文件目录结构,用于删除之前新建的文件,实际上稍后会将之转移到目录的最末位置 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; do { /* 向后搜索空闲的文件目录结构 */ s = CH376ByteRead( GlobalBuf, sizeof(FAT_DIR_INFO), &count ); /* 以字节为单位读取数据,获得下一个文件目录信息FAT_DIR_INFO */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; } while ( count && GlobalBuf[0] ); /* 如果仍然是正在使用的文件目录结构则继续向后搜索,直到上级目录结束或者有尚未使用过的文件目录结构 */ NewFileLoc = CH376ReadVar32( VAR_CURRENT_OFFSET ); /* 用上级目录的当前文件指针作为当前文件目录信息在上级目录中的起始位置 */ i = DirBlockCnt + 1; /* 需要的空闲的文件目录结构的个数,包括短文件名本身一个和长文件名 */ if ( count == 0 ) break; /* 无法读出数据,上级目录结束了 */ NewFileLoc -= sizeof(FAT_DIR_INFO); /* 倒回到刚才搜索到的空闲的文件目录结构的起始位置 */ } } if ( i ) { /* 空闲的文件目录结构不足以存放长文件名,原因是上级目录结束了,下面增加上级目录的长度 */ s = CH376ReadVar8( VAR_SEC_PER_CLUS ); /* 每簇扇区数 */ if ( s == 128 ) { /* FAT12/FAT16的根目录,容量是固定的,无法增加文件目录结构 */ s = ERR_FDT_OVER; /* FAT12/FAT16根目录下的文件数应该少于512个,需要磁盘整理 */ goto CH376CreateLongNameE; } count = s * DEF_SECTOR_SIZE; /* 每簇字节数 */ if ( count < i * sizeof(FAT_DIR_INFO) ) count <<= 1; /* 一簇不够则增加一簇,这种情况只会发生于每簇为512字节的情况下 */ s = CH376LongNameWrite( NULL, count ); /* 以字节为单位向当前位置写入全0数据块,清空新增加的文件目录簇 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE;

} s = CH376ByteLocate( NewFileLoc ); /* 在上级目录中移动文件指针到当前文件目录信息的位置 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; GlobalBuf[11] = ATTR_LONG_NAME; GlobalBuf[12] = 0x00; GlobalBuf[13] = CH376CheckNameSum( &GlobalBuf[ sizeof(FAT_DIR_INFO) ] ); /* 计算长文件名的短文件名检验和 */ GlobalBuf[26] = 0x00; GlobalBuf[27] = 0x00; for ( s = 0; DirBlockCnt != 0; ) { /* 长文件名占用的文件目录结构计数 */ GlobalBuf[0] = s ? DirBlockCnt : DirBlockCnt | 0x40; /* 首次要置长文件名入口标志 */ DirBlockCnt --; NameCount = DirBlockCnt * LONG_NAME_PER_DIR; for ( s = 1; s < sizeof( FAT_DIR_INFO ); s += 2 ) { /* 输出长文件名,长文件名的字符在磁盘上UNICODE用小端方式存放 */ if ( s == 1 + 5 * 2 ) s = 14; /* 从长文件名的第一组1-5个字符跳到第二组6-11个字符 */ else if ( s == 14 + 6 * 2 ) s = 28; /* 从长文件名的第二组6-11个字符跳到第三组12-13个字符 */ GlobalBuf[ s ] = LongName[ NameCount++ ]; GlobalBuf[ s + 1 ] = LongName[ NameCount++ ]; } s = CH376LongNameWrite( GlobalBuf, sizeof(FAT_DIR_INFO) ); /* 以字节为单位写入一个文件目录结构,长文件名 */ if ( s != USB_INT_SUCCESS ) goto CH376CreateLongNameE; } s = CH376LongNameWrite( &GlobalBuf[ sizeof(FAT_DIR_INFO) ], sizeof(FAT_DIR_INFO) ); /* 以字节为单位写入一个文件目录结构,这是转移来的之前新建的文件的目录信息 */ CH376CreateLongNameE: CH376FileClose( FALSE ); /* 对于根目录则必须要关闭 */ return( s ); }

#endif

因篇幅问题不能全部显示,请点此查看更多更全内容

Top