;===================================================
;首先显示器提示输入密码,密码在12.5S内输入有效,
;         输入三次错误报警10S,输入正确开门,7秒后返回初态
;         以上参数均可在主程序里设定,修改系统密码在程序的结尾
;===================================================
        RS BIT P2.7 
        RW BIT P2.6
        E BIT P2.5
        ENTER BIT 22H.0
        M4 BIT P2.3			   ;步进电机
		M3 BIT P2.2
		M2 BIT P2.1
		M1 BIT P2.0
        SPEAK BIT P3.7
        PASSBUF EQU 40H
        KEYNUMBER EQU 41H       ;存放识别后的按键号
	ORG 0000H
        AJMP MAIN
        ORG 0003H
        JMP KEY_INT
        ORG 000BH
        AJMP INTT0
        ORG 001BH
        AJMP INTT1
        ORG 0030H
;========== 主程序开始 ==============
MAIN:   MOV 30H,#06H          ;输入密码为6位  最高只可设15位
        MOV 42H,#200          ;设定报警的时间,42H单元里的数*50MS
		MOV 43H,#250          ;设定开门时间，14的倍数(实际上液晶显示器执行指令占用了时间,开门时间会比14*250MS大)
		;44H号单元存放输入错误的次数
        ;45H为定时器0中断次数
		;46H为定时器1中断次数
        MOV 47H,#250          ;输入密码时间设定,50MS倍数
		MOV SP,#60H
        CLR SPEAK              ;初始化输出设备
        CLR ENTER              ;标志位初始化
	
        MOV TMOD,#11H          ;定时器工作方式2
        MOV TH0,#3CH           ;初值50MS
        MOV TL0,#0BH 
		MOV TH1,#3CH           
        MOV TL1,#0BH 
        SETB EA
		SETB ET0
		SETB ET1
        
        MOV TCON,#00000000B     ;电平触发
START:  MOV R0,30H
        MOV R1,#31H
SS1:	MOV @R1,#00H
		INC R1
        DJNZ R0,SS1            ;将输入密码缓冲单元清零
        MOV P1,#0FH            ;初始化按键口
		MOV R1,#31H             ;从31号单元开始存放按建输入的密码          
        CLR 20H                 ;报警标志清零
		CLR 21H                 ;密码超时标志
		MOV 46H,#00H           ;超时后对定时器中断次数清零,便于下一轮重新计时
INIT:   ACALL SELLCD            ;设定液晶工作方式
        ACALL LINE1             ;在第一行显示
        MOV DPTR,#MES_P         ;显示请输入密码
        ACALL DISPLAY
		setb EX0               ;开中断,允许键盘输入
KEYIN:  JB  21H,ERR             ;超时转到错误程序
        JNB ENTER,KEYIN         ;等待ENTER按下
        CLR ENTER
		clr EX0                ;密码输入完后锁定键盘
;
;========== 密码判断程序 ============
COMP:            
        MOV DPTR,#PASS
        MOV R4,30H             ;密码的长度
        MOV A,#0
        MOV R1,#31H            
LOOP:   MOV R5,A
        MOVC A,@A+DPTR
        MOV PASSBUF,A          ;取出系统设定的密码
        MOV A,@R1              ;从31号单元开始取出输入的密码
        CJNE A,PASSBUF,ERR     ;比较
        MOV A,R5
        INC A
        INC R1                 ;取下一位
        DJNZ R4,LOOP

;正确
MATCH:  MOV A,#01H
        ACALL ENABLE           ;清屏,光标复位
        ACALL LINE1            ;从第一行开始显示
        MOV DPTR,#MES_C
        ACALL DISPLAY          ;显示"请进"
		ACALL LINE2
		MOV DPTR,#TAB_1
		ACALL DISPLAY

        CLR  M2              ;电机正转
		CLR  M1
		SETB M3
		SETB M4
        acall delay10ms     ;延时
		CLR  M3
        acall delay10ms     ;延时
		SETB M1
        acall delay10ms     ;延时
		CLR  M4
        acall delay10ms     ;延时
		SETB M2
        acall delay10ms     ;延时
		CLR  M1
        acall delay10ms     ;延时
		SETB M3
        acall delay10ms     ;延时

		MOV 44H,#00H        ;输入错误次数清零
        MOV R6,43H                
        ACALL DELAY

		CLR  M3		    ;关锁，电机反转
        acall delay10ms     ;延时
		SETB M1
        acall delay10ms     ;延时
		CLR  M2
        acall delay10ms     ;延时
		CLR  M4
        acall delay10ms     ;延时
		SETB M4
        acall delay10ms     ;延时
		CLR  M1
        acall delay10ms     ;延时
		SETB M3
        acall delay10ms     ;延时
												             
        JMP START            
;错误
ERR:    ACALL BACK
        MOV R6,#03H
        ACALL DELAY
		ACALL BACK          ;输错提示音,鸣两下
        MOV A,#01H
        ACALL ENABLE
        ACALL LINE1
        MOV DPTR,#MES_E
        ACALL DISPLAY       ;显示错误
        MOV R6,#40H
        ACALL DELAY         
		INC  44H
		MOV A,44H
        CJNE A,#03H,EE0
EE0:    JC  ERROUT        ;输错不到三次返回
		MOV 44H,#00H        ;清零
		ACALL WRANING       ;报警
		CLR SPEAK
ERROUT: JMP START

;=========== 按键中断服务程序 ==========
KEY_INT:push acc
        push psw
		clr ex0
        acall delay10ms     ;延时消抖
        SETB TR1            ;启动定时器
        ACALL SMKEY         ;按键识别
		MOV A,KEYNUMBER     ;返回按键号
        ;ACALL BACK          ;按键发声
        MOV R3,A
        CJNE R3,#0BH,K1     ;ENTER是否按下
        SETB ENTER
		clr tr1             ;按下确认后关毕定时器
		mov 46h,#00h        ;对中断次数清零
        AJMP KEY_RTN
K1:     ADD A,#30H          ;将按键号转换为ASCⅡ码
        MOV @R1,A           ;将输入的密码存入缓冲单元
        INC R1              ;修改地址
        
        ACALL WRDATA        ;输入一位显示一个"*"
        
KEY_RTN:pop psw
        pop acc
		setb ex0
        RETI


;=========== 按键识别程序 ==========
SMKEY:   
         MOV A,P1
	 	ANL A,#0FH
WKEY:    MOV B,A               ;反转法查询  
         MOV P1,#0F0H
	 	NOP
	 	MOV A,P1
	 	ANL A,#0F0H
	 	ORL A,B
	 	MOV P1,#0FH         ;还原P1口初态
		CJNE A,#0EEH,WKEY1
	 	MOV KEYNUMBER,#00H       
	 	AJMP RETURN
WKEY1:  CJNE A,#0EDH,WKEY2
		MOV KEYNUMBER,#01H
	 	AJMP RETURN
WKEY2:  CJNE A,#0EBH,WKEY3
	 	MOV KEYNUMBER,#02H
		AJMP RETURN
WKEY3:  CJNE A,#0E7H,WKEY4
		MOV KEYNUMBER,#03H
		AJMP RETURN
WKEY4:  CJNE A,#0DEH,WKEY5
	 	MOV KEYNUMBER,#04H
	 	AJMP RETURN
WKEY5:  CJNE A,#0DDH,WKEY6
	 	MOV KEYNUMBER,#05H
	 	AJMP RETURN
WKEY6:  CJNE A,#0DBH,WKEY7
	 	MOV KEYNUMBER,#06H
	 	AJMP RETURN
WKEY7:  CJNE A,#0D7H,WKEY8
	 	MOV KEYNUMBER,#07H
	 	AJMP RETURN
WKEY8:  CJNE A,#0BEH,WKEY9
	 	MOV KEYNUMBER,#08H
	 	AJMP RETURN
WKEY9:  CJNE A,#0BdH,WKEY10
	 	MOV KEYNUMBER,#09H
	 	AJMP RETURN
WKEY10: CJNE A,#0BbH,WKEY11
	 	MOV KEYNUMBER,#0AH
	 	AJMP RETURN
WKEY11: CJNE A,#0B7H,RETURN
	 	MOV KEYNUMBER,#0BH       ;判断哪个键按下,返回相应按键号码
RETURN: MOV P1,#0FH
        NOP
        NOP 
        MOV A,P1
        CPL A
	 	ANL A,#0FH
	 	JNZ RETURN              ;等待按键松开
        RET



;========== 液晶工作方式设置 ============
SELLCD: MOV A,#01H          ;清屏,光标复位    
        ACALL ENABLE
        MOV A,#38H          ;设置显示模式,8位两行,5*7点阵
        ACALL ENABLE
        MOV A,#0DH          ;显示器开,允许内烁
        ACALL ENABLE      
        MOV A,#06H          ;文字不动,光标右移
        ACALL ENABLE
        RET

;========== 液晶执行指令 ============
ENABLE: CLR RS
        CLR RW              ;RS,RW同为低电平时写入指令
        MOV P0,A
		CLR E               ;E由高变低时执行指令
        ACALL WAIT
        SETB E
        RET

;========== WAIT ============
;判断液晶是否忙的子程序
WAIT:   MOV P0,#0FFH
        CLR RS
		SETB RW             ;RW为高,RS为低时可读忙信号
		CLR E
		NOP
		SETB E
		JB P0.7,WAIT
		RET


        
;========== 显示的起始地址 ============
;在第一行
LINE1:  MOV A,#80H
        ACALL ENABLE
        RET
;在第二行
LINE2:  MOV A,#0C0H
        ACALL ENABLE
        RET


;=========== 显示程序 ============
DISPLAY:MOV R7,#00H
NEXT:   MOV A,R7
        MOVC A,@A+DPTR
        CJNE A,#10H,DSP
        RET
DSP:    ACALL WRDATA
        INC R7
        AJMP NEXT

;========== 将A里面的数据写入液晶 ============
WRDATA: SETB RS
        CLR RW
		MOV P0,A
		CLR E
		ACALL WAIT
		SETB E
		RET

;=========== 报警程序 ============ 
WRANING: SETB TR0
		 ACALL BACK
		 MOV R4,#00H
	 	 ACALL D2
		 JNB 20H, WRANING
         RET
;=========== 发声程序 ============ 
BACK:    MOV R0,#100
KK1:     CPL  SPEAK
         LCALL DELAY2MS																								 
		 RET                      ;延时的长短可以改变输出的频率 

		 DJNZ  R0,KK1             ;不断取反P2.0使其产生一定频率的方波推动扬声器
;=========== INTT0 ============ 
;报警时间设定
INTT0:   PUSH ACC
         PUSH PSW                  
         MOV TH0,#3CH           
         MOV TL0,#0BH
         INC 45H
         MOV A,45H
         CJNE A,42H,INTT0OUT 
         MOV 45H,#00H
         SETB 20H                 ;置标志位
         CLR TR0                  ;关闭定时器
INTT0OUT:POP PSW
         POP ACC
         RETI
;=========== INTT1 ============ 
;输入超时
INTT1:  PUSH ACC
        PUSH PSW
	 	INC 46H
	 	MOV A,46H
        CJNE A,47H,INTT1OUT
	 	CLR TR1                 ;关闭定时器
	 	SETB 21H                ;时间到置标志位	
		 MOV 47H,#00H            ;中断次数清零
INTT1OUT:POP PSW
         POP ACC
         RETI  
;=========== 延时程序 ============
DELAY:    MOV R4,#38H
D1:       MOV R5,#0F9H
          DJNZ R5,$
          DJNZ R4,D1
          DJNZ R6,DELAY        ;14R6毫秒
          RET
DELAY2MS: MOV R4,#02H
D2:       MOV R5,#0F0H
          DJNZ R5,$
 	  	  DJNZ R4,D2
	  	  RET 
DELAY10ms:  MOV R4,#20
D3:         MOV R5,#248
            DJNZ R5,$
            DJNZ R4,D3
            RET

PASS:   DB "123456"
MES_P:  DB "Password?",10H
MES_C:  DB "   welcome!",10H
MES_E:  DB "Error!",10H
TAB_1:  DB "     OK",10h     

        END


