?? ch375hms.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 + -