哈哈,前阵子画了板CH374的评估版,焊了块CH372做USB设备试验. CH372在外置固件下枚举成U盘,C语言版本很早就搞定了. 网上遇一网友,说只会汇编,打赌说我无法搞出个汇编版的. 俺就不信这个邪,苦战半夜,终于搞定
源程序如下:
;古老牌U盘汇编版 ;CH372+STC89C54单片机 ;作者邮箱:xg_2004_sy@126.com
$INCLUDE (CH375INC.ASM)
STACK EQU 80H ;堆栈区栈顶(向上延伸) ; ; 需要主程序定义的参数 CH375_CMD_PORT EQU 0xB100 ;CH375命令口的地址,地址译码后自动片选 CH375_DAT_PORT EQU 0xB000 ;CH375命令口的地址,地址译码后自动片选 ;
CH375_CON_ACT BIT P1.2
VAR_NeedTransBlockCount Data 30h VAR_TransCompleteBlockCount Data 31H VAR_MainRXDataPackCount Data 32h VAR_ucTransFinishPageCount DATA 33H VAR_ucNeedTransPageCount DATA 34
VAR_ExtSRAMDataLocationPoint_L Data 35H VAR_ExtSRAMDataLocationPoint_H Data 36H
VAR_CSW_BYTE_4 DATA 37H VAR_CSW_BYTE_5 DATA 38H VAR_CSW_BYTE_6 DATA 39H VAR_CSW_BYTE_7 DATA 3AH
var_EP2_UP_STATUS DATA 3BH ;上传数据状态切换变量
CMD_BUFFER DATA 3FH ;命令包缓冲区,本程序约定命令包长度为CONST_CMD_LEN CBW_Buffer DATA 10H ;cbW占用数据长度为12
VAR_SETUP_REQUEST EQU 72H ;USB请求码 VAR_SETUP_LENGTH EQU 73H ;后续数据长度 VAR_SETUP_DESCR EQU 74H ;描述符偏移地址 VAR_SN_STRING EQU 75H ;产品序列号字符串缓冲区,ANSI字符串 VAR_USB_ADDRESS EQU 76H
Reload_Count EQU 0F3H
c_Char_0 EQU 30H c_Char_1 EQU 31H c_Char_2 EQU 32H c_Char_3 EQU 33H c_Char_4 EQU 34H c_Char_5 EQU 35H c_Char_6 EQU 36H c_Char_7 EQU 37H c_Char_8 EQU 38H c_Char_9 EQU 39H
; 定义位标志 CH375_CON_FLAG BIT 2FH.0 ;375芯片配置完成标志 VAR_MainRXDataStatusMac Bit 2FH.1 ;主端点数据接收状态机标志 ; ;**************************************************************************** ;主程序 ORG 0000H ;复位后单片机入口 LJMP START ORG 0013H ;CH375中断 LJMP CH375_INTER
ORG 0100H ;---------------------------------------------------------------------------------
Initial_UART: Mov SCON,#50h Mov TMOD,#21h Mov TH1,#Reload_Count Mov TL1,#Reload_Count ORL PCON,#80h SETB TR1 ;启动定时器 Ret
UART_Send_Char: ;通过串口发送字符,待发送的字符在ACC中 CLR TI Mov SBUF,A Uart_Wait_Send_Finish: JNB TI,UART_Wait_Send_Finish CLR TI Ret ;--------------------------------------------------------------------------------- START: ; 以下初始化指令不是必要的,将单片机恢复为默认状态 CLR EA ;关中断 MOV SP,#STACK ;置堆栈初值 MOV A,#0FFH MOV P0,A MOV P1,A ;清端口状态 MOV P2,A MOV P3,A CLR A MOV IE,A ;清中断允许控制寄存器 MOV IP,A ;清中断优先级控制寄存器 MOV PSW,A ;清程序状态字 MOV TCON,A ;清定时器/计数器控制寄存器 MOV TMOD,A ;清定时器/计数器工作方式寄存器 MOV PCON,A ;清电源控制寄存器 MOV R7,#0FFH CALL DELAY_MS ;延时等待CH375初始化完成 ; 初始化 Call Initial_UART UART_SendTestChar: Mov a,#c_Char_0 call UART_Send_Char Mov a,#0DH call UART_Send_Char Mov a,#0AH call UART_Send_Char
Mov a,#c_Char_8 call UART_Send_Char Mov a,#0DH call UART_Send_Char Mov a,#0AH call UART_Send_Char
; AJMP UART_SendTestChar MOV R6,#0; MOV R7,#0; MOV DPTR,#0 ACALL READ_FLASH_SECTOR
MOV DPTR,#0 MOV R4,#4 TEST_SEND_FLASHSECTOR_PAGE_LOOP: MOV R5,#128 TEST_SEND_FLASHSECTOR_BYTE_LOOP: MOVX A,@DPTR call UART_Send_Char INC DPTR DJNZ R5,TEST_SEND_FLASHSECTOR_BYTE_LOOP DJNZ R4,TEST_SEND_FLASHSECTOR_PAGE_LOOP
CALL CH375_DisConnect ;配置为断开USB连接的状态 CALL CH375_INIT ;初始化 call Init_EP2_FUNC_VAlRegister setb EA ljmp $ ;主程序 ;
;------------------------------------------------------------------- ;------ STC MCU Flash PGM -------------------------------- ISP_DATA DATA 0E2H ISP_ADDRH DATA 0E3H ISP_ADDRL DATA 0E4H ISP_CMD DATA 0E5H ISP_TRIG DATA 0E6H ISP_CONTR DATA 0E7H ;定义Flash操作等待时间性 ENABLE_ISP EQU 80H
MCU_FLASH_START_ADDRESS_HIGH EQU 80H
;读一字节 ;调用前需打开IAP功能 ;入口:DPTR = 字节地址 ;返回:A = 读出字节 MCU_FLASH_READ_BYTE: MOV ISP_CONTR,#ENABLE_ISP MOV ISP_CMD,#01 MOV ISP_ADDRH,DPH MOV ISP_ADDRL,DPL CLR EA MOV ISP_TRIG,#46H MOV ISP_TRIG,#0B9H NOP MOV A,ISP_DATA SETB EA ;Now in processing(CPU will hart here before completing) ; ACALL IAP_Disable RET
;字节编程 ;调用前需打开IAP功能 ;入口:DPTR = 字节地址,A= 须编程字节的数据 MCU_FLASH_WRITE_BYTE: MOV ISP_CONTR,#ENABLE_ISP MOV ISP_CMD,#02 MOV ISP_ADDRH,DPH MOV ISP_ADDRL,DPL MOV ISP_DATA,A CLR EA MOV ISP_TRIG,#46H MOV ISP_TRIG,#0B9H NOP SETB EA ;Now in processing(CPU will hart here before completing) ACALL IAP_Disable RET ;擦除扇区 ;入口:DPTR = 扇区地址 MCU_FLASH_ERASE_SECTOR: MOV ISP_CONTR,#ENABLE_ISP MOV ISP_CMD,#03 MOV ISP_ADDRH,DPH MOV ISP_ADDRL,DPL CLR EA MOV ISP_TRIG,#46H MOV ISP_TRIG,#0B9H NOP SETB EA ;Now in processing(CPU will hart here before completing) ; ACALL IAP_Disable RET IAP_Disable: MOV ISP_CONTR,#0 MOV ISP_CMD,#0 MOV ISP_TRIG,#0 RET
;写数据到扇区 ;入口地址 (R6R7)扇区号 ;DPTR指向数据缓冲区,数据长度恒定为512字节 WRITE_FLASH_SECTOR: PUSH DPH ;数据缓冲区指针入栈保护 PUSH DPL
MOV A,R7 ADD A,0E0H ;A =A*2 ADD A,#MCU_FLASH_START_ADDRESS_HIGH MOV R6,A MOV R7,#00 ;完成起始地址的计算 数据在R6,R7中
MOV DPH,R6 MOV DPL,R7 ACALL MCU_FLASH_ERASE_SECTOR
MOV R3,#4 WRITE_SECTOR_LOOP_WRITE_PAGE: MOV R4,#128; WRITE_SECTOR_LOOP_Write_Byte: POP DPL POP DPH MOVX A,@DPTR; INC DPTR PUSH DPH PUSH DPL
MOV DPH,R6 MOV DPL,R7 ACALL MCU_FLASH_WRITE_BYTE INC DPTR MOV R7,DPL MOV R6,DPH
DJNZ R4,WRITE_SECTOR_LOOP_Write_Byte DJNZ R3,WRITE_SECTOR_LOOP_Write_Page
POP DPL POP DPH RET
;从扇区读数据到缓冲区 ;入口地址 (R6R7)扇区号 ;DPTR指向数据缓冲区,数据长度恒定为512字节 READ_FLASH_SECTOR: PUSH DPH ;数据缓冲区指针入栈保护 PUSH DPL
MOV A,R7 ADD A,0E0H ;A =A*2 ADD A,#MCU_FLASH_START_ADDRESS_HIGH MOV R6,A MOV R7,#00 ;完成起始地址的计算 数据在R6,R7中
MOV DPH,R6 MOV DPL,R7
MOV R3,#4 READ_SECTOR_LOOP_READ_PAGE: MOV R4,#128; READ_SECTOR_LOOP_READ_Byte: MOV DPH,R6 MOV DPL,R7 LCALL MCU_FLASH_READ_BYTE INC DPTR MOV R7,DPL MOV R6,DPH
POP DPL POP DPH MOVX @DPTR,A; INC DPTR PUSH DPH PUSH DPL
DJNZ R4,READ_SECTOR_LOOP_READ_Byte DJNZ R3,READ_SECTOR_LOOP_READ_PAGE
POP DPL POP DPH RET
;------------------Flash PGM END----------------------------------------
; 输出命令码给CH375 ; 输入: ACC 为命令码 ; 输出: DPTR 为数据口 ; USE: ACC, DPTR WR_CMD_TO_375: MOV DPTR,#CH375_CMD_PORT ;命令口地址 MOVX @DPTR,A ;设置USB工作模式 NOP ;如果时钟频率低于20MHz则无需该指令延时,高于30MHz要多加2条指令 MOV DPTR,#CH375_DAT_PORT ;数据口地址 RET ; ; 初始化子程序 ; USE: ACC, R7, DPTR CH375_INIT: MOV A,#CMD_SET_USB_MODE ;设置USB工作模式 CALL WR_CMD_TO_375 ;输出命令码 ; MOV A,#02H ;设置为内置固件模式 MOV A,#01H ;设置为外置固件模式 MOVX @DPTR,A ;设置为使用内置固件的USB设备方式 NOP ;如果时钟频率低于16MHz则无需该指令延时 NOP CH375_INIT_WT: MOVX A,@DPTR ;返回操作状态,也可以等待20uS XRL A,#CMD_RET_SUCCESS JNZ CH375_INIT_WT ;等待操作成功,通常需要等待10uS-20uS ; 下述三条指令用于启用中断 CLR IT1 ;置外部信号为低电平触发 SETB PX1 ;置高优先级 CLR IE1 ;清中断标志 SETB EX1 ;允许CH375中断 RET ;************************************************************************************** CH375_DisConnect: MOV A,#CMD_SET_USB_MODE ;设置USB工作模式 CALL WR_CMD_TO_375 ;输出命令码 MOV A,#00H ;设置为未配置模式 MOVX @DPTR,A ;设置为使用内置固件的USB设备方式 NOP ;如果时钟频率低于16MHz则无需该指令延时 NOP CH375_DisConnect_WT: MOVX A,@DPTR ;返回操作状态,也可以等待20uS XRL A,#CMD_RET_SUCCESS JNZ CH375_DisConnect_WT ;等待操作成功,通常需要等待10uS-20uS ; 下述三条指令用于启用中断 CLR IT1 ;置外部信号为低电平触发 SETB PX1 ;置高优先级 CLR IE1 ;清中断标志 SETB EX1 ;允许CH375中断 RET
;写代码定间的数据到EP2的端点 ;数据长度在R4中,数据缓冲区指针在DPTR中,采用MOVC取数 CH375_EP2_WRITE_CODEDATA: MOV R6,DPL MOV R7,DPH ; SAVE DATA POINT
MOV A,#CMD_WR_USB_DATA7 ;发出写上传端点命令 CALL WR_CMD_TO_375 MOV A,R4 MOVX @DPTR,A ;写数据长度
JZ CH375_EP2_WRITE_CODE_FINISH ;长度为0
EP2_WRITE_CODE_LOOP1: MOV DPL,R6 ;Get Data Point from backup DPTR Image MOV DPH,R7
CLR A MOVC A,@A+DPTR
INC DPTR MOV R6,DPL MOV R7,DPH ; SAVE DATA POINT MOV DPTR,#CH375_DAT_PORT MOVX @DPTR,A DJNZ R4,EP2_WRITE_CODE_LOOP1 CH375_EP2_WRITE_CODE_FINISH: RET ;--------------------------------------------------------------------------------------- ;写数据空间的内容到CH372的EP端点 ;数据长度在R4中,数据缓冲区指针在DPTR中,采用MOVX取数
CH375_EP2_WRITE_XDATA: MOV R6,DPL MOV R7,DPH ; SAVE DATA POINT
MOV A,#CMD_WR_USB_DATA7 ;发出写上传端点命令 CALL WR_CMD_TO_375 MOV A,R4 MOVX @DPTR,A ;写数据长度
JZ CH375_EP2_WRITE_CODE_FINISH ;长度为0
EP2_WRITE_XDATA_LOOP1: MOV DPL,R6 ;Get Data Point from backup DPTR Image MOV DPH,R7
MOVX A,@DPTR
INC DPTR MOV R6,DPL MOV R7,DPH ; SAVE DATA POINT MOV DPTR,#CH375_DAT_PORT MOVX @DPTR,A DJNZ R4,EP2_WRITE_XDATA_LOOP1 CH375_EP2_WRITE_XDATA_FINISH: RET
;----------------------------------------------------------------------------------------
c_EP2_UPSTATUS_TRANSMIT_IDLE EQU 00H c_EP2_UPSTATUS_TRANSMIT_CSW EQU 01H c_EP2_UPSTATUS_TRANSMIT_STANDPAGE EQU 02H c_EP2_UPSTATUS_TRANSMIT_SHORTPAGE EQU 03H
;CMD_INQUIRY_FUNCTION FUNC_SCSI_CMD10_INQUIRY: MOV DPTR,#USB_InquiryData_TAB MOV R4,#36 CALL CH375_EP2_WRITE_CODEDATA MOV var_EP2_UP_STATUS,#c_EP2_UPSTATUS_TRANSMIT_CSW ;发送完数据发状态 RET ;CMD_READ_CAPACITY
FUNC_SCSI_CMD25_READ_CAPACITY: MOV DPTR,#USB_UDISK_Capacity MOV R4,#8 CALL CH375_EP2_WRITE_CODEDATA MOV var_EP2_UP_STATUS,#c_EP2_UPSTATUS_TRANSMIT_CSW ;发送完数据发状态 RET ;CMD_READ_FORMAT_CAPACITIES FUNC_SCSI_CMD23_READ_FORMAT_CAPACITYIES: MOV DPTR,#USB_UDISK_FORMAT_Capaities MOV R4,#20 CALL CH375_EP2_WRITE_CODEDATA MOV var_EP2_UP_STATUS,#c_EP2_UPSTATUS_TRANSMIT_CSW ;发送完数据发状态 RET
;MCD00_TEST_UNITE_READY FUNC_SCSI_CMD00_TEST_UNITE_READY: ACALL TransitCSW RET ;CMD_MODE_SENSE FUNC_SCSI_CMD1A_MODE_SENSE: MOV A,CBW_Buffer+2 ;CBW[2] Value send to ACC CJNE A,#1CH,MODE_SENSE_NEXT1 MOV DPTR,#USB_UDISK_MODE_SENSE_TPP MOV R4,#18 CALL CH375_EP2_WRITE_CODEDATA
AJMP MODE_SENSE_END MODE_SENSE_NEXT1: CJNE A,#3FH,MODE_SENSE_NEXT2 MOV DPTR,#USB_UDISK_MODE_SENSE_ALL MOV R4,#12 CALL CH375_EP2_WRITE_CODEDATA AJMP MODE_SENSE_END MODE_SENSE_NEXT2: MOV DPTR,#USB_UDISK_MODE_SENSE_ZERO MOV R4,#8 CALL CH375_EP2_WRITE_CODEDATA
AJMP MODE_SENSE_END MODE_SENSE_END: MOV var_EP2_UP_STATUS,#c_EP2_UPSTATUS_TRANSMIT_CSW ;发送完数据发状态 RET ;CMD28_READ10 FUNC_SCSI_CMD28_READ10: MOV A, CBW_Buffer+8 JNZ CMD28_Need_Trans_Data_Package ACALL TransitCSW AJMP CMD28_READ10_END CMD28_Need_Trans_Data_Package: MOV VAR_NeedTransBlockCount,A MOV VAR_TransCompleteBlockCount,#0 MOV R7, CBW_Buffer+5 MOV R6, CBW_Buffer+4 MOV VAR_ucTransFinishPageCount,#0 MOV VAR_ucNeedTransPageCount,#8 MOV DPTR,#0 ACALL READ_FLASH_SECTOR MOV var_EP2_UP_STATUS,#c_EP2_UPSTATUS_TRANSMIT_STANDPAGE ;发送完数据发状态 MOV DPTR,#0 MOV R4,#64 CALL CH375_EP2_WRITE_XDATA INC VAR_ucTransFinishPageCount CMD28_READ10_END: RET
;CMD2A_WRITE10: FUNC_SCSI_CMD2A_WRITE10: SETB VAR_MainRXDataStatusMac
MOV A, CBW_Buffer+8 MOV VAR_NeedTransBlockCount,A
MOV VAR_MainRXDataPackCount,#0 MOV VAR_TransCompleteBlockCount,#0 JNZ CMD2A_WRITE10_FINISH CLR VAR_MainRXDataStatusMac CMD2A_WRITE10_FINISH: RET ;--------------------------------------------------------------------------------------------- CMD12_INQUIRY EQU 12H CMD1A_MODE_SENSE EQU 1AH CMD28_READ10 EQU 28H CMD25_READ_CAPACITY EQU 25H CMD23_READ_FORMAT_CAPACITIES EQU 23H CMD00_TEST_UNIT_READY EQU 00H CMD2F_VERIRY EQU 2FH CMD2A_WRITE10 EQU 2AH CMD1E_MEDIUM_REMOVAL EQU 1EH
;对EP2接收到的CBW进行解析和处理 ;R4为数据长度 ;数据内容在公共缓冲区中 EP2CMDProcess: MOV R0,#CMD_BUFFER+4 ;拷贝CSW内容 MOV A,@R0 MOV VAR_CSW_BYTE_4,A iNC R0 MOV A,@R0 MOV VAR_CSW_BYTE_5,A iNC R0 MOV A,@R0 MOV VAR_CSW_BYTE_6,A iNC R0 MOV A,@R0 MOV VAR_CSW_BYTE_7,A
MOv R4,#12 MOV R0,#CMD_BUFFER+15 ;拷贝UFI内容 MOV R1,#CBW_Buffer COPY_CBW_LOOP_1: MOV A,@R0 MOV @R1,A INC R0 INC R1 DJNZ R4,COPY_CBW_LOOP_1 Mov a,#c_Char_1 call UART_Send_Char Mov a,#c_Char_1 call UART_Send_Char MOV A,CBW_Buffer call UART_Send_Char
Mov a,#0DH call UART_Send_Char Mov a,#0AH call UART_Send_Char
MOV A,CBW_Buffer
CJNE A,#CMD12_INQUIRY,NO_CMD10_SEGMENT CALL FUNC_SCSI_CMD10_INQUIRY AJMP EP2_CMD_END NO_CMD10_SEGMENT: CJNE A,#CMD25_READ_CAPACITY,NO_CMD25_SEGMENT CALL FUNC_SCSI_CMD25_READ_CAPACITY AJMP EP2_CMD_END NO_CMD25_SEGMENT: CJNE A,#CMD23_READ_FORMAT_CAPACITIES,NO_CMD23_SEGMENT CALL FUNC_SCSI_CMD23_READ_FORMAT_CAPACITYIES AJMP EP2_CMD_END NO_CMD23_SEGMENT: CJNE A,#CMD1A_MODE_SENSE,NO_CMD1A_SEGMENT CALL FUNC_SCSI_CMD1A_MODE_SENSE AJMP EP2_CMD_END NO_CMD1A_SEGMENT: CJNE A,#CMD28_READ10,NO_CMD28_READ10 CALL FUNC_SCSI_CMD28_READ10 AJMP EP2_CMD_END NO_CMD28_READ10: CJNE A,#CMD00_TEST_UNIT_READY,NO_CMD00_TESTUNITREADY CALL FUNC_SCSI_CMD00_TEST_UNITE_READY AJMP EP2_CMD_END NO_CMD00_TESTUNITREADY: CJNE A,#CMD2A_WRITE10,NO_CMD2A_WRITE10 CALL FUNC_SCSI_CMD2A_WRITE10 AJMP EP2_CMD_END NO_CMD2A_WRITE10: EP2_CMD_END: RET
; Mov a,#c_Char_1 ; call UART_Send_Char ; Mov a,#c_Char_0 ; call UART_Send_Char ; Mov a,#0DH ; call UART_Send_Char ; Mov a,#0AH ; call UART_Send_Char ; ; Ret
DataFlash_WriteSector: ; Mov a,#c_Char_1 ; call UART_Send_Char ; Mov a,#c_Char_1 ; call UART_Send_Char ; Mov a,#0DH ; call UART_Send_Char ; Mov a,#0AH ; call UART_Send_Char MOV R7, CBW_Buffer+5 MOV R6, CBW_Buffer+4
MOV A,R7 ADD A,VAR_TransCompleteBlockCount MOV R7,A
CLR A ADDC A,R6 MOV R6,A
MOV DPTR,#0 ACALL WRITE_FLASH_SECTOR Ret
;------------------------------------------------------------------------------------------------- TransitCSW: ; Mov a,#c_Char_1 ; call UART_Send_Char ; Mov a,#c_Char_2 ; call UART_Send_Char ; Mov a,#0DH ; call UART_Send_Char ; Mov a,#0AH ; call UART_Send_Char ; MOV R6,DPL ; MOV R7,DPH ; SAVE DATA POINT
MOV A,#CMD_WR_USB_DATA7 ;发出写上传端点命令 CALL WR_CMD_TO_375 MOV A,#13 MOVX @DPTR,A ;写数据长度
MOV A,#0x55 MOVX @DPTR,A ;写数据长度 MOV A,#0x53 MOVX @DPTR,A ;写数据长度 MOV A,#0x42 MOVX @DPTR,A ;写数据长度 MOV A,#0x53 MOVX @DPTR,A ;写数据长度 MOV A,VAR_CSW_BYTE_4 MOVX @DPTR,A MOV A,VAR_CSW_BYTE_5 MOVX @DPTR,A MOV A,VAR_CSW_BYTE_6 MOVX @DPTR,A |