⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch375hms.asm

📁 USB 协议基础及ch375 usb芯片
💻 ASM
字号:
; /* 2004.06.05
; ****************************************
; **  Copyright  (C)  W.ch  1999-2004   **
; **  Web:  http://www.winchiphead.com  **
; ****************************************
; **  USB Host File Module      @CH375  **
; ****************************************
; */
; /* U盘文件读写模块, 连接方式: 3线制串口+查询 */
; /* MCS-51单片机ASM语言示例程序 */
; /* 因为使用U盘文件读写模块而不是使用U盘文件级子程序库,所以占用较少的单片机资源,可以使用89C51单片机测试 */
; /* 以字节为单位进行U盘文件读写,单片机的RAM只需要几十个字节,不需要外部RAM */
; 
;$include	(REG51.INC)
$include	(..\CH375HM.INC)

; 电路连接方式,只需要连接3根线,使用串口同步码启动操作
;   单片机    模块
;    TXD   =  SIN
;    RXD   =  SOUT
;             STA# 悬空或接高电平
;             INT# 接地或接低电平
;    GND   =  GND

LED_OUT			BIT		P1.4			;P1.4 低电平驱动LED显示,用于监控演示程序的进度

; 命令参数结构, 长度为20到60字节
mCmdParam		DATA	20H				;默认情况下该结构将占用64字节的RAM,可以修改MAX_PATH_LEN常量,当修改为32时,只占用32字节的RAM
NEW_SIZE_HIGH	DATA	1EH				;文件长度的高字节
NEW_SIZE_LOW	DATA	1FH				;文件长度的低字节

				ORG		0000H
				LJMP	MAIN
;
; 程序数据起始地址
				ORG		0100H
STR_FILE_NAME1:	DB		'\C51\CH375HFT.C',00H	;文件名,该文件在C51子目录下
STR_FILE_DATA1:	DB		'Note: ',0DH,0AH,'这个程序是以字节为单位进行U盘文件读写,',00H
STR_FILE_DATA2:	DB		'单片机只需要有几十字节的RAM就可以了',0DH,0AH,00
STR_FILE_NAME2:	DB		'\双击我吧.TXT',00H		;新文件名,在根目录下

; 从程序空间复制字符串到内部RAM缓冲区,源字符串必须以00H作为结束标志,长度不能超过255
; 入口:   DPTR 指向源字符串的起始地址, R0 目标缓冲区的起始地址
; 出口:   R7 返回字符串的长度(含字符串结束符00H)
; 使用:   DPTR, R0, R7
CopyString:		MOV		R7,#00H
CopyStringChar:	MOV		A,R7
				INC		R7
				MOVC	A,@A+DPTR
				MOV		@R0,A
				INC		R0
				JNZ		CopyStringChar		;不是字符串结束符00H,继续复制
				RET
;
; 延时100毫秒,不精确
; 使用:   R6, R7
mDelay100mS:	MOV  	R7,#0C8H
mDelay100mS_1:	MOV  	R6,#0C8H
mDelay100mS_2:	NOP
				NOP
				NOP
				DJNZ 	R6,mDelay100mS_2
				DJNZ 	R7,mDelay100mS_1
				RET  	

; 发送一个字节数据给CH375模块
; 入口:   ACC 准备发送的数据
mSendByte:		CLR     TI
				MOV     SBUF,A
				JNB     TI,$
				RET

; 从CH375模块接收一个字节数据
; 出口:   ACC 已经接收的数据
mRecvByte:		JNB     RI,$
				MOV     A,SBUF
				CLR     RI
				RET

; 执行命令
; 输入命令码和输入参数长度,返回操作状态码,输入参数和返回参数都在CMD_PARAM结构中
; 入口:   R7 命令码, R5 参数长度
; 出口:   R7 状态码, R5 返回结果的长度
; 使用:   R0, R5, R6, R7
ExecCommand:	MOV     A,#SER_SYNC_CODE1	;发送串口同步码通知模块,说明命令码开始发送,请求开始执行命令
				LCALL   mSendByte
				MOV     A,#SER_SYNC_CODE2	;用两个串口同步码代替STA#的下降沿
				LCALL   mSendByte			;上面两个串口同步码应该连续发送,如果不连续,那么间隔时间不能超过20mS,否则命令无效
				CLR     RI
				MOV     A,R7
				LCALL   mSendByte			;写入命令码
				MOV     A,R5
				LCALL   mSendByte			;写入后续参数的长度
				MOV     A,R5
				JZ      ExecCommand_Wait	;没有参数
				MOV     R0,#mCmdParam		;指向输入参数的起始地址
ExecCmdParam:	MOV     A,@R0
				LCALL   mSendByte			;依次写入参数
				INC     R0
				DJNZ    R5,ExecCmdParam
ExecCommand_Wait:							;处理数据传输,直到操作完成才退出
				LCALL   mRecvByte			;等待模块完成操作并返回操作状态
				MOV     R7,A				;状态码
				XRL     A,#ERR_SUCCESS
				JNZ		ExecCmdStatus1		;不是操作成功状态,需要进一步分析
				LCALL   mRecvByte			;操作成功,则返回结果数据的长度
				MOV     R5,A				;结果数据的长度
				JZ      ExecCmdRet			;没有结果数据,操作成功返回
				MOV     R6,A
				MOV     R0,#mCmdParam		;指向输出参数的起始地址
ExecCmdResult:	LCALL   mRecvByte			;接收结果数据并保存到参数结构中
				MOV     @R0,A
				INC     R0
				DJNZ    R6,ExecCmdResult
				SJMP    ExecCmdRet			;有结果数据,操作成功返回
ExecCmdStatus1:	CJNE	R7,#USB_INT_DISK_READ,ExecCmdStatus2	;正在从U盘读数据块,请求数据读出
				SJMP    ExecCmdRet			;本程序只使用以字节为单位的文件读写子程序,所以正常情况下不会收到该状态码,当作操作失败返回
ExecCmdStatus2:	CJNE	R7,#USB_INT_DISK_WRITE,ExecCmdStatus3	;正在向U盘写数据块,请求数据写入
				SJMP    ExecCmdRet			;本程序只使用以字节为单位的文件读写子程序,所以正常情况下不会收到该状态码,当作操作失败返回
ExecCmdStatus3:	CJNE	R7,#USB_INT_DISK_RETRY,ExecCmdStatus4	;读写数据块失败重试
				SJMP    ExecCmdRet			;本程序只使用以字节为单位的文件读写子程序,所以正常情况下不会收到该状态码,当作操作失败返回
ExecCmdStatus4:	CJNE    R7,#ERR_USB_CONNECT,ExecCmdStatus5
				LCALL   mDelay100mS			;U盘刚刚连接或者断开,应该延时几十毫秒再操作
				MOV     R7,#ERR_USB_CONNECT
ExecCmdStatus5:	SJMP    ExecCmdRet			;操作失败
ExecCmdRet:		RET
; END OF ExecCommand

; 检查操作状态,如果错误则停机
; 输入:   R7 为操作状态码
mStopIfError:	MOV		A,R7
				JNZ		mStopIfError_LED	;状态码是错误
				RET
mStopIfError_LED:							;LED闪烁
				CLR		A
				MOV		C,LED_OUT
				MOV		ACC.0,C
				XRL		A,#01H
				MOV		C,ACC.0
				MOV		LED_OUT,C
				LCALL	mDelay100mS
				SJMP	mStopIfError_LED
;
; 主程序
MAIN:			CLR		A
				MOV		PSW,A
				MOV		IE,A
				MOV		SP,#60H
				MOV		A,#0FFH
				MOV		P0,A
				MOV		P1,A
				MOV		P2,A
				MOV		P3,A
				CLR  	LED_OUT				;开机后LED亮一下以示工作
				LCALL	mDelay100mS			;延时100毫秒,CH375模块上电后需要100毫秒左右的复位时间
				LCALL	mDelay100mS
				SETB 	LED_OUT
;其它电路初始化
;设置与CH375模块通讯的串口
				MOV     SCON,#50H
				MOV     PCON,#80H
				MOV     TMOD,#20H
				MOV     TH1,#0E6H			;24MHz晶振, 4800bps
				SETB    TR1
;初始化完成
				NOP
MAIN_LOOP:									;主循环
;可以在打算读写U盘时再查询,没有必要一直连续不停地查询,可以让单片机做其它事,没事可做就延时等待一会再查询
				MOV		R5,#00H				;没有命令参数
				MOV		R7,#CMD_QueryStatus	;使用查询方式看U盘是否连接
				LCALL	ExecCommand			;查询当前模块的状态
				LCALL	mStopIfError		;错误则停机
				MOV		A,mCmdParam+1		;Status.mDiskStatus
				CLR     C
				SUBB	A,#DISK_CONNECT
				JNC		MAIN_CONNECT		;U盘已经连接
				LCALL	mDelay100mS			;可以让单片机做其它事,没事可做就延时等待一会再查询
				LCALL	mDelay100mS
				SJMP	MAIN_LOOP
MAIN_CONNECT:	LCALL	mDelay100mS			;U盘已经连接,延时,可选操作,有的USB存储器需要几十毫秒的延时
				LCALL	mDelay100mS
				CLR  	LED_OUT				;LED亮说明U盘连接
; 检查U盘是否准备好,大多数U盘不需要这一步,但是某些U盘必须要执行这一步才能工作
				MOV		R3,#05H
WAIT_READY:		LCALL	mDelay100mS
				MOV     R5,#00H
				MOV     R7,#CMD_DiskReady
				LCALL   ExecCommand			;查询磁盘是否准备好
				MOV     A,R7
				JZ		DISK_IS_READY
				DJNZ	R3,WAIT_READY		;U盘尚未准备好
DISK_IS_READY:
;读取原文件
				MOV		DPTR,#STR_FILE_NAME1
				MOV		R0,#mCmdParam		;Open.mPathName
				LCALL	CopyString			;复制文件名
				MOV		A,R7
				MOV		R5,A				;参数长度为文件名长度
				MOV		R7,#CMD_FileOpen	;打开文件
				LCALL	ExecCommand			;执行打开文件操作
				CJNE	R7,#ERR_MISS_DIR,MAIN_OPEN_J1
				SJMP	MAIN_OPEN_MISS		;ERR_MISS_DIR说明没有找到C51子目录
MAIN_OPEN_J1:	CJNE	R7,#ERR_MISS_FILE,MAIN_OPEN_J2
MAIN_OPEN_MISS:								;ERR_MISS_FILE说明没有找到文件
				LJMP	MAIN_CREATE_NEW		;创建新文件
MAIN_OPEN_J2:	LCALL	mStopIfError		;源文件成功打开
;以字节为单位读取原文件
READ_FILE_BYTE:	MOV		R2,#10H				;请求读出16字节数据, 单次读写的长度不能超过 sizeof( mCmdParam.ByteWrite.mByteBuffer )
				MOV		mCmdParam+0,R2		;ByteRead.mByteCount
				MOV		R5,#01H				;只有一个输入参数
				MOV		R7,#CMD_ByteRead	;从文件以字节为单位读取数据块
				LCALL	ExecCommand			;从文件读取数据,如果文件比较大,一次读不完,可以再用命令CMD_ByteRead继续读取,文件指针自动向后移动
				LCALL	mStopIfError
; 在mCmdParam+0单元是实际读出的数据长度,从mCmdParam+1单元开始是读出的数据块
				MOV     A,mCmdParam+0		;ByteRead.mByteCount
				JZ      READ_FILE_END		;实际读出的数据长度小于请求读出的长度则说明文件结束
				MOV     R7,A
				MOV     R0,#mCmdParam+1		;ByteRead.mByteBuffer,从mCmdParam+1单元开始是读出的数据块
GET_READ_BYTE:	MOV     A,@R0
;				MOV     ?,A					;处理刚读出的数据
				INC     R0
				DJNZ    R7,GET_READ_BYTE
				MOV     A,mCmdParam+0		;实际读出的数据长度
				CLR     C
				SUBB    A,R2
				JC      READ_FILE_END		;实际读出的数据长度小于请求读出的长度则说明文件结束
				SJMP    READ_FILE_BYTE		;文件未结束,继续读出数据
READ_FILE_END:	MOV		A,#00H
				MOV		mCmdParam+0,A		;Close.mUpdateLen
				MOV		R5,#01H				;只有一个输入参数
				MOV		R7,#CMD_FileClose	;关闭文件
				LCALL	ExecCommand			;关闭文件
				LCALL	mStopIfError
;产生新文件
MAIN_CREATE_NEW:
				MOV		DPTR,#STR_FILE_NAME2
				MOV		R0,#mCmdParam		;Create.mPathName
				LCALL	CopyString			;复制文件名
				MOV		A,R7
				MOV		R5,A				;参数长度为文件名长度
				MOV		R7,#CMD_FileCreate
				LCALL	ExecCommand			;新建文件并打开,如果文件已经存在则先删除后再新建
				LCALL	mStopIfError
;以字节为单位写入第一组数据块
				MOV		DPTR,#STR_FILE_DATA1
				MOV		R0,#mCmdParam+1		;ByteWrite.mByteBuffer
				LCALL	CopyString			;复制数据块
				MOV     A,R7
				MOV		mCmdParam+0,A		;ByteWrite.mByteCount
				INC     A
				MOV		R5,A				;输入参数的长度为写入数据块的长度加一个长度单元
				MOV		R7,#CMD_ByteWrite	;以字节为单位向文件写入数据
				LCALL	ExecCommand			;向文件写入数据,如果文件比较大,一次写不完,可以再用命令CMD_ByteWrite继续写入,文件指针自动向后移动
				LCALL	mStopIfError
;以字节为单位写入第二组数据块
				MOV		DPTR,#STR_FILE_DATA2
				MOV		R0,#mCmdParam+1		;ByteWrite.mByteBuffer
				LCALL	CopyString			;复制数据块
				MOV     A,R7
				MOV		mCmdParam+0,A		;ByteWrite.mByteCount
				INC     A
				MOV		R5,A				;输入参数的长度为写入数据块的长度加一个长度单元
				MOV		R7,#CMD_ByteWrite	;以字节为单位向文件写入数据
				LCALL	ExecCommand			;向文件写入数据,如果文件比较大,一次写不完,可以再用命令CMD_ByteWrite继续写入,文件指针自动向后移动
				LCALL	mStopIfError
				MOV		A,#01H				;请求模块自动计算文件长度
				MOV		mCmdParam+0,A		;Close.mUpdateLen
				MOV		R5,#01H				;只有一个输入参数
				MOV		R7,#CMD_FileClose	;关闭文件
				LCALL	ExecCommand			;关闭文件
				LCALL	mStopIfError
;等待U盘断开,仅作演示,实际应用中不必考虑U盘是否断开
MAIN_TAKE_OUT:	MOV		R5,#00H				;没有命令参数
				MOV		R7,#CMD_QueryStatus	;使用查询方式看U盘是否断开
				LCALL	ExecCommand			;查询当前模块的状态
				LCALL	mStopIfError		;错误则停机
				MOV		A,mCmdParam+1		;Status.mDiskStatus
				XRL		A,#DISK_DISCONNECT
				JZ		MAIN_DISCONNECT		;U盘已经断开
				LCALL	mDelay100mS			;延时等待一会再查询
				LCALL	mDelay100mS
				SJMP	MAIN_TAKE_OUT
MAIN_DISCONNECT:	SETB	LED_OUT			;LED灭
				LJMP	MAIN_LOOP			;等待下一个U盘连接
; END main
;
END

⌨️ 快捷键说明

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