『壹』 求51單片機C語言編的簡易密碼鎖程序
首先得說明我這個可是自己原創手打的,但是沒去模擬了,程序可能有錯誤,你自己修改下吧
#include<reg52.h>
typedef unsigned char uchar;
typedef unsigned int uint;
sbit key1=P0^0;
sbit key2=P0^1;
sbit key3=P0^2;
sbit key4=P0^3;
sbit wela=P2^0;//位鎖存端
#define SMG P1
sbit LED=P3^0;//低電平亮
uchar code table[]={0x8d,0x86};//共陽數碼管 P,E
uchar chushi_mima[]={2,1,3};
uchar shuru_mima[3];
uchar index;//控制輸入密碼的位數
uchar flag_3s=0;//3s標志位
uchar keydown;//確定按鍵變數
#define times 15//去抖時間15Ms
uchar key1_count,key2_count,key3_count,key4_count;
void init()
{
wela=0;
SMG=0xff;
TMOD=0x01;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
ET0=1;
EA=1;
TR0=1;
LED=1;
}
void main()
{
init();
while(1)
{
switch(keydown)
{
if(index>2)index=0;
case 1:
shuru_mima[index]=0;
index++;
break;
case 2:
shuru_mima[index]=1;
index++;
break;
case 3:
shuru_mima[index]=2;
index++;
break;
case 4:
shuru_mima[index]=3;
index++;
break;
}
flag_3s=0;
for(i=0;i<3;i++)
{
if(shuru_mima[i]==chushi_mima[i])
{
LED=0;
wela=1;
SMG=table[0];
if(flag_3s)
{
flag_3s=0;
wela=0;
}
}
else
{
LED=1;
wela=1;
SMG=table[1];
if(flag_3s)
{
flag_3s=0;
wela=0;
}
}
}
}
}
void timer0() interrupt 1
{
uchar count;
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
if(++count>=600)
{
count=0;
flag_3s=1;
}
/*********1ms中斷掃描按鍵(包含去抖程序)********/
if(!key1&&key1_count!=0)
{
key1_count--;
if(key1_count==0)
{
keydown=1;
}
}
else if(!key1) key1_count=times;
// key2,key3,key4你自己寫吧
}
『貳』 51單片機密碼鎖原理
其實沒什麼演算法,不要想太復雜了。就是把幾個按鍵的順序存入2404中,修改的時候存入,鍵入密碼的時候對比。正確了就做相應處理,錯誤做另外相應處理。管理員也是有密碼,原理一樣,可以在C代碼中為管理員編一個固定的密碼,也可以是根據後期輸入改變的密碼qq461292125不明白的再問
怎麼不給分QQ也給你講明白了!!!!!!!!!!!!!!!!!!!!!
『叄』 單片機的密碼鎖
TLOW EQU 00H
THIGH EQU 0EEH
COUN0 EQU 200 ;5ms * 200 = 1S
COUN1 EQU 3FH ;密碼的位數
D_TIME EQU 3EH ;設置密碼時的時間限制
SEC EQU 3DH ;秒單元
PASS_OLD EQU 30H
PASS_NEW EQU 40H
SDA EQU P3.4 ;24C01的串列數據線
SCLK24 EQU P3.3 ;24C01的串列時鍾線
;20H.0 為時間限制標記
;20H.1 為按錯鍵標記
;20H.2 為比較對錯標記
;20H.3 為3秒時間標記
RS EQU P2.0
RW EQU P2.1
EN EQU P2.2
X EQU 2FH ;LCD 地址變數
BEEP EQU P3.7
;--------------------------------------------------
ORG 0000H
JMP MAIN
ORG 0BH
JMP T0_INT
;--------------------------------------------------
MAIN:
MOV SP,#60H
MOV A,#00H
MOV D_TIME,A
MOV SEC,A
MOV COUN1,A
MOV R5,#06H
MOV R0,#PASS_OLD
CLR_01: MOV @R0,A
INC R0
DJNZ R5,CLR_01
CLR EN
CLR 20H.0
CLR 20H.1
CLR 20H.2
CLR 20H.3
CALL INIT_TIMER
CALL SET_LCD
CALL MENU1
CALL PASS_READ ;讀出預定密碼。
LOOP1:
CALL SCAN_KEY
CALL DELAY2
CJNE A,#0fH,LOOP1 ;按F鍵啟動進入輸入密碼程序
SETB TR0
LP0: CALL SCAN_KEY ;按住F鍵3秒以上蜂鳴器不響為止
CALL DELAY2
CJNE A,#0fH,LP3
JB 20H.3,LP1 ;3秒到,20H.3置1。
JMP LP0
LP1: CLR TR0 ;停止中斷
MOV SEC,#00H ;秒單元清零
CLR 20H.3 ;清3秒標記位
LP2: MOV P1,#0F0H ;等待鍵釋放
MOV A,P1
CJNE A,#0F0H,LP2
CALL PASS_IN
JB 20H.2,LOOP2 ;密碼正確後方可轉下一步
JMP LP4
LP3: CLR TR0 ;停止中斷
CLR 20H.3 ;清3秒標記位
MOV SEC,#00H ;秒單元清零
LP4: CALL SYS_RST ;系統復位
RET
NOP
NOP
NOP
LJMP MAIN
LOOP2: MOV R4,#06H ;模擬輸出蜂鳴器響六聲
LOOP3: CALL BZ
DJNZ R4,LOOP3
MOV A,#00H
LOOP4: CALL SCAN_KEY
CJNE A,#0AH,LOOP6 ;按A鍵進入PASS_LOOK
CALL PASS_LOOK
LOOP5: CALL SCAN_KEY
CJNE A,#0EH,LOOP5 ;按E鍵退出PASS_LOOK
CALL MENU1
CALL MENU2
LOOP6: CJNE A,#0BH,LOOP8 ;按B鍵進入PASS_SET
CALL PASS_SET
LOOP7: CALL SCAN_KEY
CJNE A,#0EH,LOOP7 ;按E鍵退出PASS_SET
CALL MENU1
CALL MENU2
LOOP8: CJNE A,#0DH,LOOP4 ;按D鍵退出
ACALL MAIN ;PASS_LOOK和PASS_SET狀態
JMP LOOP4
SYS_RST:
RST1: CALL SCAN_KEY
CJNE A,#0DH,RST1 ;"D" 鍵
LJMP MAIN
RET
NOP
NOP
NOP
LJMP MAIN
PASS_IN:
MOV 21H,#03H ;允許輸入三次密碼
P_IN1: MOV DPTR,#IN_PASS0 ;
MOV A,#1 ;
CALL LCD_SHOW
MOV DPTR,#INFO2 ;指針指到顯示信息2
MOV A,#2 ;顯示在第二行
CALL LCD_SHOW
MOV R0,#PASS_OLD
MOV R3,#00H
MOV R2,#09H ;設置LCD地址
MOV COUN1,#06H ;6位密碼
SETB TR0
P_IN2: JB 20H.0,P_IN4
MOV P1,#0F0H
MOV A,P1
CJNE A,#0F0H,P_IN3
jmp P_IN2
P_IN3:
CALL KEY_IN
CALL SETDATA0
CALL BZ
JB 20H.1,P_IN4
DJNZ COUN1,P_IN2
CLR TR0
CLR 20H.0
CALL PASS_COMP
JNB 20H.2,P_IN4 ;比較對錯標記
RET
P_IN4: CLR TR0
CLR 20H.0 ;
CLR 20H.1 ;
CALL INPUT_ERR
DJNZ 21H,P_IN1 ;
RET
IN_PASS0:
DB " INPUT PASSWORD ",0
PASS_COMP:
MOV COUN1,#06H ;比較6位數值
MOV R0,#PASS_OLD
MOV R1,#PASS_NEW
P_COMP0: MOV A,@R0
MOV B,@R1
CJNE A,B,P_COMP1
INC R0
INC R1
DJNZ COUN1,P_COMP0
CALL INPUT_OK
SETB 20H.2
RET
P_COMP1: CLR 20H.2
;CALL INPUT_ERR
RET
PASS_SET:
MOV 21H,#05H ;允許輸入三次密碼
P_SET1: MOV DPTR,#SET_PASS0 ;
MOV A,#1 ;
CALL LCD_SHOW
MOV DPTR,#INFO2 ;指針指到顯示信息2
MOV A,#2 ;顯示在第二行
CALL LCD_SHOW
MOV R3,#00H
MOV R2,#09H ;設置LCD地址
MOV R1,#PASS_NEW
MOV COUN1,#06H ;6位密碼
SETB TR0
P_SET2: JB 20H.0,P_SET4
MOV P1,#0F0H
MOV A,P1
CJNE A,#0F0H,P_SET3
jmp P_SET2
P_SET3:
CALL KEY_IN
CALL SETDATA1
CALL BZ
JB 20H.1,P_SET4
;MOV R5,#04H
;CALL DELAY
DJNZ COUN1,P_SET2
CLR TR0
CLR 20H.0
CALL RESET_OK
CALL EEPW
RET
P_SET4: CLR TR0
CLR 20H.0 ;
CLR 20H.1 ;
CALL RESET_ERR
DJNZ 21H,P_SET1 ;
RET
SET_PASS0:
DB " RESET PASSWORD ",0
MENU1: ;LCD 顯示工作菜單信息
MOV DPTR,#MENU01
MOV A,#1 ;在第一行顯示信息
CALL LCD_SHOW
RET
MENU01: DB "PASSWORD CONTROL",0
MENU2: ;LCD 顯示工作菜單信息
MOV DPTR,#MENU02
MOV A,#2 ;在第一行顯示信息
CALL LCD_SHOW
RET
SETDATA1:
MOV A,R3
ANL A,#0FH ;取出低四位二進制數
PUSH ACC
CLR C
SUBB A,#0AH ;減10
POP ACC
JC ASCII3 ;該數小於10,轉ASCII0
SETB 20H.1
RET
ADD A,#07H ;大於10的數加上37H
ASCII3: ADD A,#30H ;小於10的數加上30H
MOV @R1,A
MOV B,R2
CALL LCDP2
INC R2
INC R1
RET
SETDATA0:
MOV A,R3
ANL A,#0FH ;取出低四位二進制數
PUSH ACC
CLR C
SUBB A,#0AH ;減10
POP ACC
JC ASCII4 ;該數小於10,轉ASCII4
SETB 20H.1
RET
ASCII4: ADD A,#30H ;小於10的數加上30H
MOV @R0,A ;保存密碼值
MOV A,#2AH ;顯示" * "
MOV B,R2
CALL LCDP2
INC R2
INC R0
RET
PASS_LOOK:
MOV DPTR,#LOOK1 ;指針指到顯示信息1
MOV A,#1 ;顯示在第一行
CALL LCD_SHOW
MOV DPTR,#LOOK2 ;指針指到顯示信息2
MOV A,#2 ;顯示在第二行
CALL LCD_SHOW
MOV R1,#PASS_NEW
MOV R2,#09
MOV COUN1,#06
LOOK0: MOV A,@R1
MOV B,R2
CALL LCDP2
INC R2
INC R1
DJNZ COUN1,LOOK0
RET
LOOK1: DB " LOOK PASSWORD ",0 ;LCD 第一行顯示信息
LOOK2: DB "PASSWORD ------ ",0 ;LCD 第二行顯示信息
SCAN_KEY:
SCAN_K: MOV P1,#0F0H
MOV A,P1
CJNE A,#0F0H,KEY_NUM0 ;有鍵按下轉
JMP KEY_END
KEY_NUM0: CALL KEY_IN
CALL BZ
MOV R5,#04H
CALL DELAY
KEY_END: RET
KEY_IN: MOV P1,#0F0H ;置列線為0,行線為1
MOV A,P1
ANL A,#0F0H
MOV B,A
MOV P1,#0FH ;置列線為1,行線為0
MOV A,P1
ANL A,#0FH
ORL A,B ;高四位與低四位重新組合
CJNE A,#0FFH,KEY_IN1 ;0FFH為末按鍵
RET
KEY_IN1: MOV B,A
MOV DPTR,#KEYTABLE
MOV R3,#0FFH
KEY_IN2: INC R3
MOV A,R3
MOVC A,@A+DPTR
CJNE A,B,KEY_IN3
MOV A,R3 ;找到,取順序碼
RET
KEY_IN3: CJNE A,#0FFH,KEY_IN2 ;末完,繼續查
RET ;0FFH為結束碼
SET_LCD:
CLR EN
CALL INIT_LCD ;初始化 LCD
CALL DELAY1
MOV DPTR,#INFO1 ;指針指到顯示信息1
MOV A,#1 ;顯示在第一行
CALL LCD_SHOW
MOV DPTR,#INFO2 ;指針指到顯示信息2
MOV A,#2 ;顯示在第二行
CALL LCD_SHOW
RET
INFO1: DB " ",0 ;LCD 第一行顯示信息
INFO2: DB "PASSWORD ------ ",0 ;LCD 第二行顯示信息INIT_LCD: ;8位I/O控制 LCD 介面初始化
MOV A,#38H ;雙列顯示,字形5*7點陣
CALL WCOM
CALL DELAY1
MOV A,#38H ;雙列顯示,字形5*7點陣
CALL WCOM
CALL DELAY1
MOV A,#38H ;雙列顯示,字形5*7點陣
CALL WCOM
CALL DELAY1
MOV A,#0CH ;開顯示,關游標,
CALL WCOM
CALL DELAY1
MOV A,#01H ;清除 LCD 顯示屏
CALL WCOM
CALL DELAY1
RET
LCD_SHOW: ;在LCD的第一行或第二行顯示信息字元
CJNE A,#1,LINE2 ;判斷是否為第一行
LINE1: MOV A,#80H ;設置 LCD 的第一行地址
CALL WCOM ;寫入命令
CALL CLR_LINE ;清除該行字元數據
MOV A,#80H ;設置 LCD 的第一行地址
CALL WCOM ;寫入命令
JMP FILL
LINE2: MOV A,#0C0H ;設置 LCD 的第二行地址
CALL WCOM ;寫入命令
CALL CLR_LINE ;清除該行字元數據
MOV A,#0C0H ;設置 LCD 的第二行地址
CALL WCOM
FILL: CLR A ;填入字元
MOVC A,@A+DPTR ;由消息區取出字元
CJNE A,#0,LC1 ;判斷是否為結束碼
RET
LC1: CALL WDATA ;寫入數據
INC DPTR ;指針加1
JMP FILL ;繼續填入字元
RET
CLR_LINE: ;清除該行 LCD 的字元
MOV R0,#24
CL1: MOV A,#' '
CALL WDATA
DJNZ R0,CL1
RET
ENABLE: ;寫指令使能
CLR RS ;RS=L,RW=L,D0-D7=指令碼,E=高脈沖
CLR RW
SETB EN
CALL DELAY0
CLR EN
RET
ENABLE1: ;寫數據使能
SETB RS ;RS=H,RW=L,D0-D7=數據,E=高脈沖
CLR RW
SETB EN
CALL DELAY0
CLR EN
RET
DELAY0: MOV R7,#250 ;延時500微秒
DJNZ R7,$
RET
WCOM: ;以8位控制方式將命令寫至LCD
MOV P0,A ;寫入命令
CALL ENABLE
RET
WDATA: ;以8位控制方式將數據寫至LCD
MOV P0,A ;寫入數據
CALL ENABLE1
RET
LCDP2: ;在LCD的第二行顯示字元
PUSH ACC ;
MOV A,B ;設置顯示地址
ADD A,#0C0H ;設置LCD的第二行地址
CALL WCOM ;寫入命令
POP ACC ;由堆棧取出A
CALL WDATA ;寫入數據
RET
CONV:
MOV X,#9 ;設置顯示起始位置
MOV A,R3
ANL A,#0F0H ;取出高四位二進制數
SWAP A ;高四位與低四位互換
PUSH ACC ;壓入堆棧
CLR C ;C=0
SUBB A,#0AH ;減10
POP ACC ;彈出堆棧
JC ASCII0 ;該數小於10,轉ASCII0
ADD A,#07H ;大於10的數加上37H
ASCII0: ADD A,#30H ;小於10的數加上30H
MOV B,X
CALL LCDP2
MOV A,R3
ANL A,#0FH ;取出低四位二進制數
PUSH ACC
CLR C
SUBB A,#0AH ;減10
POP ACC
JC ASCII1 ;該數小於10,轉ASCII1
ADD A,#07H ;大於10的數加上37H
ASCII1: ADD A,#30H ;小於10的數加上30H
INC X
MOV B,X
CALL LCDP2
RET
DELAY2: MOV R5,#15H
DELAY: ;延時R5×10MS
MOV R6,#50
D1: MOV R7,#100
DJNZ R7,$
DJNZ R6,D1
DJNZ R5,DELAY
RET
DELAY1: ;延時5MS
MOV R6,#25
D2: MOV R7,#100
DJNZ R7,$
DJNZ R6,D2
RET
KEYTABLE:
DB 0EEH,0EDH,0EBH,0E7H,0DEH ;0,1,2,3,4, 順序碼
DB 0DDH,0DBH,0D7H,0BEH,0BDH ;5,6,7,8,9,
DB 0BBH,0B7H,07EH,07DH,07BH ;A,B,C,D,E,
DB 077H,0FFH ;F 0FF為結束碼
DB 0DBH,0EEH,0DEH,0BEH,07EH ;0,1,2,3,4, 順序碼
DB 0EDH,0DDH,0BDH,07DH,0EBH ;5,6,7,8,9,
DB 0BBH,07BH,0E7H,0D7H,0B7H ;A,B,C,D,E,
DB 077H,067H,066H,065H,0FFH ;F,C+F,1+F,8+C
INIT_TIMER: ;初始化定時器
MOV TMOD,#01H ;設置定時器0 工作模式為模式1
MOV IE, #82H ;啟用定時器0 中斷產生
MOV TL0,#TLOW
MOV TH0,#THIGH
RET
T0_INT:
PUSH ACC
MOV TL0,#TLOW
MOV TH0,#THIGH
INC D_TIME
MOV A,D_TIME ;5ms 計數值加1
CJNE A,#COUN0,T0_T
MOV D_TIME,#0
INC SEC ;秒加1
MOV A,SEC
CJNE A,#03H,TO_INT0
SETB 20H.3
TO_INT0: CJNE A,#8,T0_T ;是否到8秒?
MOV SEC,#0 ;秒單元清0
SETB 20H.0
T0_T: POP ACC
RETI
PASS_READ:
CALL EEPR
RET
MOV COUN1,#06H ;6位密碼數值
MOV R7,#00H
MOV R1,#PASS_NEW
P_READ: MOV A,R7
MOV DPTR,#R_TABLE
MOVC A,@A+DPTR
MOV @R1,A
INC R1
INC R7
DJNZ COUN1,P_READ
RET
R_TABLE:
DB 35H,36H,34H,34H,38H,38H
COMP_ERR:
MOV DPTR,#COMP_ERR1 ;
MOV A,#2 ;
CALL LCD_SHOW
RET
COMP_ERR1:
DB " PASSWORD ERROR ",0
COMP_OK:
MOV DPTR,#COMP_OK0 ;
MOV A,#2 ;
CALL LCD_SHOW
RET
COMP_OK0:
DB " PASSWORD RIGHT ",0
RESET_ERR:
MOV DPTR,#RESET_ERR0 ;
MOV A,#2 ;
CALL LCD_SHOW
RET
RESET_ERR0:
DB " RESET ERROR ",0
RESET_OK:
MOV DPTR,#RESET_OK0 ;
MOV A,#2 ;
CALL LCD_SHOW
RET
RESET_OK0:
DB " RESET RIGHT ",0
INPUT_ERR:
MOV DPTR,#INPUT_ERR0 ;
MOV A,#2 ;
CALL LCD_SHOW
RET
INPUT_ERR0:
DB " INPUT ERROR ",0
INPUT_OK:
MOV DPTR,#INPUT_OK0 ;
MOV A,#2 ;
CALL LCD_SHOW
RET
INPUT_OK0:
DB " INPUT RIGHT ",0
EEPW: PUSH ACC
PUSH PSW
CLR PSW.3
SETB PSW.4
MOV R1,#PASS_NEW
MOV R7,#06H ;連續寫8個位元組
LCALL START
MOV A,#0A0H ;送器件地址
ACALL SUBS
MOV A,#00H ;送片內位元組地址
ACALL SUBS
AGAIN1:
MOV A,@R1
ACALL SUBS ;調發送單位元組子程序
INC R1
DJNZ R7,AGAIN1 ;連續寫8個位元組
LCALL STOP ;發停止信號
POP PSW
POP ACC
RET
SUBS:
MOV R0,#08H ;發送單位元組子程序
LOOP: CLR SCLK24
RLC A
MOV SDA,C
NOP
SETB SCLK24
ACALL DELAY24
DJNZ R0,LOOP ;循環8次送8個bit
CLR SCLK24
ACALL DELAY24
SETB SCLK24
REP: MOV C,SDA
JC REP ;判應答到否,未到則等待
CLR SCLK24
RET
DELAY24:
NOP
NOP
RET
EEPR: PUSH ACC
PUSH PSW
CLR PSW.3
SETB PSW.4
MOV R7,#06H
MOV R1,#PASS_NEW
LCALL START ;發開始信號
MOV A,#0A0H ;送器件地址
ACALL SUBS ;調發送單位元組子程序
MOV A,#00H ;送片內位元組地址
ACALL SUBS
LCALL START ;再發開始信號
MOV A,#0A1H
ACALL SUBS
MORE: ACALL SUBR
MOV @R1,A
MOV A,#00H
INC R1
DJNZ R7,MORE
LCALL STOP ;送停止信號
POP PSW
POP ACC
RET
SUBR: MOV R0,#08H ;接受單位元組子程序
SUBR2: SETB SCLK24
ACALL DELAY24
MOV C,SDA
RLC A
CLR SCLK24
ACALL DELAY24
DJNZ R0,SUBR2
CJNE R7,#01H,ALOW
SETB SDA ;若是最後一個位元組置SDA=1
AJMP SETOK
ALOW:
CLR SDA ;否則置SDA=0
SETOK: ACALL DELAY24
SETB SCLK24
ACALL DELAY24
CLR SCLK24
ACALL DELAY24
SETB SDA ;應答畢,SDA置1
RET
START:
CLR SDA
ACALL DELAY24
SETB SDA
SETB SCLK24
ACALL DELAY24
CLR SDA
SETB SCLK24
RET
STOP:
CLR SDA
SETB SCLK24
ACALL DELAY24
SETB SDA
ACALL DELAY24
RET
BZ: ;蜂鳴器
MOV R6,#100
B1: CALL DEX
CPL BEEP
DJNZ R6,B1
MOV R5,#20
CALL DELAY
RET
DEX: MOV R7,#180
DE1: NOP
DJNZ R7,DE1
RET
END
『肆』 高分求完整的單片機密碼鎖程序...
/*變數的定義:
show_val[6]: 顯示的值
init_val[6]: 密碼初始值
key_val: 返回按鍵的值 255-表示無按鍵按下
key_index: 當前按鍵是哪一位密碼
T1_cnt: 定時器計數溢出數
cnt_val_15s: 報警計時的數值
cnt_val_5s: 待機時間計時
cnt_val_4s: 輸入正確,等待4秒清除開鎖信號
cnt_state: 計時狀態
error_num: 錯誤次數
led_seg_code:數碼管7段碼
*/
#include "reg51.h"
/*說明key0=P1^0; key1=P1^1;key2=P1^2; key3=P1^3;key4=P1^4;key5=P1^5;enter=P1^6;esc=P1^7;*/
sbit relay_open=P3^0; //電磁鎖開鎖驅動
sbit pw_error=P3^1; //密碼錯誤信號
sbit alarm_out=P3^2; //報警輸出
sbit open_lock=P3^3; //已開鎖指示信號
sbit audio_out=P3^4; //有源蜂鳴器
unsigned char data cnt_val_15s,cnt_val_5s,cnt_val_4s,cnt_state;
unsigned int data T1_cnt;
unsigned char data key_val,key_index,key_val_old;
unsigned char data state_val,error_num;
unsigned char data show_val[6];
char code init_val[6]={1,2,3,4,5,0};
char code led_seg_code[11]={0x3f,0x06,0x05b,0x04f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
//led_seg_code[0-9]代表0-9 led_seg_code[10]=0x00數碼管不顯示任何內容
//--------延時程序----------------
void delay(unsigned int i)//延時
{ while(--i); }
//--------清除輸入內容----------
void init_variant()
{unsigned char i;
for(i=0;i<6;i++)
show_val[i]=10; //led_seg_code[10]=0x00表示數碼管不顯示任何內容
key_index=0; //沒有任何輸入或清除所有輸入時,保存當前鍵的位置
}
//---------按鍵掃描---------------
unsigned char scan_key()
{ unsigned char i,k;
i=P1;
if (i==0xff && cnt_state!=2)
{ k=255; } //無鍵按下
else //有鍵按下
{ delay(500); //延時去抖動
if(i!=P1)
{k=255;}
else
{ TR1=1; //有鍵按下則開定時器,啟動待機計時
cnt_val_5s=0;
switch (i)
{ case 0xfe: k=0; break;
case 0xfd: k=1; break;
case 0xfb: k=2; break;
case 0xf7: k=3; break;
case 0xef: k=4; break;
case 0xdf: k=5; break;
case 0xbf: k=6; break;
case 0x7f: k=7; break;
}
}
}
return k;
}
//---------數碼管顯示---------------
void led_show()
{P0=led_seg_code[show_val[0]];
P2=0xdf;
delay(500);
P0=led_seg_code[show_val[1]];
P2=0xef;
delay(500);
P0=led_seg_code[show_val[2]];
P2=0xf7;
delay(500);
P0=led_seg_code[show_val[3]];
P2=0xfb;
delay(500);
P0=led_seg_code[show_val[4]];
P2=0xfd;
delay(500);
P0=led_seg_code[show_val[5]];
P2=0xfe;
delay(500);
}
//--------定時器T1中斷服務程序-----------------
void timer1() interrupt 3 //T1中斷
{ T1_cnt++;
if(T1_cnt>3999) //如果計數>3999, 計時1s
{ T1_cnt=0;
switch (cnt_state)
{ case 0: //待機,需要計時5s
if(cnt_val_5s<5)
{ cnt_val_5s++;}
else
{ cnt_val_5s=0;
init_variant();//待機計時到5秒時,清除輸入的內容
TR1=0; //停止計時
}
break;
case 1://密碼輸入正確,需要計時4s
if(cnt_val_4s<4)
{ cnt_val_4s++;}
else
{ cnt_val_4s=0;
init_variant();//密碼輸入正確,計時到4秒時,清除輸入的內容
open_lock=1; //已開鎖信號清零
relay_open=1; //開鎖信號清零
cnt_state=0;
TR1=0; //停止計時
}
break;
case 2: //密碼輸入錯誤3次,計時15s
if(cnt_val_15s<15)
{ cnt_val_15s++;}
else
{ cnt_val_15s=0;
init_variant();//三次密碼錯誤時,計時15秒,清除輸入的內容
open_lock=1; // 清除所有指示和報警
relay_open=1;
alarm_out=1;
pw_error=1;
cnt_state=0;
TR1=0; //停止計時
}
break;
}
}
}
//--------判斷鍵盤輸入內容與密碼是否一致------
unsigned char check_input_pw()
{ unsigned char i,k;
k=1;
for(i=0;i<6;i++)
{ k=k && (show_val[i]==init_val[i]); }
return k;
}
//---------主程序----------------
main()
{ //初始化各變數
audio_out=1;
P3=0xff;
cnt_val_15s=0;
cnt_val_5s=0;
cnt_val_4s=0;
cnt_state=0;
//0-待機計時5s狀態;1-密碼正確,計時4s狀態 ;2-三次密碼錯誤,處於計時15秒狀態。
T1_cnt=0;
error_num=0;
key_val_old=255;
init_variant();
//初始化51的寄存器
TMOD=0x20; //用T1計時 8位自動裝載定時模式
TH1=0x19; //500微秒溢出一次; 250=(256-x)*12/11.0592 -> x=19
TL1=0x19;
EA=1; //開中斷
ET1=1;
TR1=0; //開定時器T1
while(1)
{ key_val=scan_key(); //按鍵輸入,有鍵按下key_val為0-7,無鍵按下key_val為255。
if (key_val!=key_val_old)
{ key_val_old=key_val;
if (key_val!=255&& cnt_state!=2)
{ audio_out=0;
delay(100); //延時去抖動
audio_out=1;
switch (key_val)
{ case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
if(key_index<6) //密碼為6位,超過6位視為輸入無效
{ show_val[key_index]=key_val;
key_index++; }
break;
case 6: //確認鍵
if(check_input_pw())
{//密碼正確
error_num=0; //密碼輸入錯誤次數清零
//---------
pw_error=1; //密碼錯誤指示燈滅
relay_open=0; //開鎖驅動信號燈亮
open_lock=0; //已開鎖信號燈亮
//---------
delay(50000); //兩聲短「滴」聲
audio_out=0;
delay(50000);
audio_out=1;
delay(50000);
audio_out=0;
delay(50000);
audio_out=1;
//---------
cnt_state=1; //下一狀態處於4秒計時的狀態
TR0=1; //啟動定時
}
else
{ if (error_num<2)
{error_num++; //輸入錯誤次數小於3次時,沒錯一次error_num增一
pw_error=0; //密碼錯誤指示燈亮
//-----------
delay(20000);//一聲長「滴」聲,提示錯誤
audio_out=0;
delay(60000);
audio_out=1;
//-----------
init_variant();//清除所有輸入,等待下一次輸入
}
else //輸入錯誤次數超過3次
{ alarm_out=0; //報警燈亮
pw_error=0; //密碼錯誤指示燈亮
error_num=0; //密碼輸入錯誤次數清零
//----------
audio_out=0; //長鳴聲報警
delay(60000);
delay(60000);
delay(60000);
delay(60000);
delay(60000);
delay(60000);
delay(60000);
delay(60000);
delay(60000);
audio_out=1;
//-------------
TR1=1; //打開定時器計時
cnt_state=2; //下一狀態處於15秒計時的狀態
}
}
break;
case 7://取消鍵
init_variant();
break;
}
}
}
led_show();
}
}
//-----程序結束-----------------
『伍』 求51單片機C語言編的密碼鎖程序
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
#define BIN(a,b,c,d,e,f,g,h) ((a<<7)+(b<<6)+(c<<5)+(d<<4)+(e<<3)+(f<<2)+(g<<1)+(h<<0))
//下面的code表示數組存放在ROM中,因為這個數組的值不需要改寫
uchar code KeyCode[16]={15,14,12,8,30,28,24,16,60,56,48,32,120,112,96,64};//值為m*(n+1)的乘積,用於Key()
uchar dis[6];
msdelay(uint x)//延時子函數
{uchar j;
while(x--)
{for(j=0;j<125;j++){;}
}
}
//鍵盤子程序一,鍵盤值與數組值對比得到
uchar Key(void)
{uchar temp,m,n,i,j,matrix,k;
P1=0xF0; /*行線電平為高,列線為低*/
temp=P1&0xf0;
if (temp==0xf0) return(16); /*行仍為高,無按健,退出*/
else msdelay(10);
for(i=1;i<16;i=i*2)
{m=i;
for(j=1;j<16;j=j*2)
{n=(~j)&0x0f;
P1=(m<<4)|n; /*m為P1的行值由i循環得到,n為列值,由j循環並取反得到*/
temp=P1&0xf0;
if (!temp)
{do{temp=P1&0xf0;}while(!temp);
matrix=m*(n+1);/*為避免乘積重復,n+1*/
for(k=0;k<16;k++){if (matrix==KeyCode[k]) return(k);} //KeyCode:見前
return(16);
} //if loop
}//j loop
}//i loop
}//Key end
//用Switch...case語句得到鍵盤值*/
uchar Key1(void)
{uchar temp,m,n,i,j,matrix;
P1=0xF0; /*行線電平為高,列線為低*/
temp=P1&0xf0;
if (temp==0xf0) return(16); /*行仍為高,無按健,退出*/
else msdelay(10);
for(i=1;i<16;i=i*2)
{m=i;
for(j=1;j<16;j=j*2)
{n=(~j)&0x0f;
P1=(m<<4)|n;/*m為P1的行值由i循環得到,n為列值,由j循環並取反得到*/
temp=P1&0xf0;
if (!temp)
{do{temp=P1&0xf0;}while(!temp);
matrix=m*(n+1);
switch(matrix) //此方法的基本思路:
{case 15:return(1); break; //由循環得到的m,n值賦於P1埠實現逐個鍵掃描
case 14:return(2); break; //同時由m,n+1的值相乘得到對應鍵點de的積
case 12:return(3); break; //m*(n+1)值掃描鍵點對應而得出鍵值
case 8:return(4); break; //
case 30:return(5); break; //
case 28:return(6); break; //
case 24:return(7); break; //
case 16:return(8); break;
case 60:return(9); break;
case 56:return(0); break;
case 48:return(10); break;
case 32:return(11); break;
case 120:return(12); break;
case 112:return(13); break;
case 96:return(14); break;
case 64:return(15); break;
default:return(16);
} //switch end
} //if loop
}//j loop
}//i loop
}//Key end
//依次掃描16個按鍵
uchar Key2(void)
{uchar temp;
P1=0xF0; /*使P1=1111 0000,行線電平為高,列線為低*/
temp=P1&0xf0;
if (temp==0xf0) return(16); /*讀P1=1111 xxxx,表示行仍為高,無按健,退出(x表示不關心)?/
else msdelay(10);
P1=0x1e; /*P1=0001 1110,行一為高,列一為低,掃描第一個按鍵*/
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(1);}
P1=0x1d; /*P1=0001 1101,行一為高,列二為低,掃描第二個按鍵,下面掃描其餘按鍵*/
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(2);}
P1=0x1b;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(3);}
P1=0x17;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(4);}
P1=0x2e;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(5);}
P1=0x2d;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(6);}
P1=0x2b;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(7);}
P1=0x27;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(8);}
P1=0x4e;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(9);}
P1=0x4d;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(0);}
P1=0x4b;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(10);}
P1=0x47;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(11);}
P1=0x8e;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(12);}
P1=0x8d;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(13);}
P1=0x8b;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(14);}
P1=0x87;
temp=P1&0xf0;
if (!temp) {do{temp=P1&0xf0;}while(!temp);
return(15);}
return(16); //掃描all按鍵都未按下,則輸出16
}//Key2 end.
////////時鍾中斷顯示子程序
void T0_int() interrupt 1
{static uchar i;
if (i==6){i=0;}
P0=5-i;
P0=P0|(dis[i]<<4);
i++;
TL0=0;
TH0=252;}
void distri(uint disnum)
{uint temp;
dis[0]=0;
dis[1]=disnum/10000;
temp=disnum%10000;
dis[2]=temp/1000;
temp=temp%1000;
dis[3]=temp/100;
temp=temp%100;
dis[4]=temp/10;
dis[5]=temp%10;
}
Main()
{uchar KeyVal,i=0;
TMOD=0x01;
IE=0x82;
TH0=252;
TL0=0;
TR0=1;
distri(0);
do{
KeyVal=Key();
if (KeyVal!=16) dis[1]=KeyVal; //注意:當有按鍵時才賦於顯示位dis[1],否則出錯,請分析!
}while(1);
}
『陸』 單片機c語言密碼鎖程序
include<reg52.h> //包含頭文件,一般情況不需要改動,頭文件包含特殊功能寄存器的定義
#define DataPort P0 //定義數據埠 程序中遇到DataPort 則用P0 替換
#define KeyPort P1
sbit LATCH1=P2^2;//定義鎖存使能埠 段鎖存
sbit LATCH2=P2^3;// 位鎖存
unsigned char code dofly_DuanMa[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71};// 顯示段碼值0~F
unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//分別對應相應的數碼管點亮,即位碼
unsigned char TempData[10]; //存儲顯示值的全局變數
unsigned char code password[8]={1,2,3,4,5,6,7,8};
//可以更改此密碼做多組測試
void DelayUs2x(unsigned char t);//us級延時函數聲明
void DelayMs(unsigned char t); //ms級延時
void Display(unsigned char FirstBit,unsigned char Num);//數碼管顯示函數
unsigned char KeyScan(void);//鍵盤掃描
unsigned char KeyPro(void);
void Init_Timer0(void);//定時器初始化
/*------------------------------------------------
主函數
------------------------------------------------*/
void main (void)
{
unsigned char num,i,j;
unsigned char temp[8];
bit Flag;
Init_Timer0();
while (1) //主循環
{
num=KeyPro();
if(num!=0xff)
{
if(i==0)
{
for(j=0;j<8;j++)//清屏
TempData[j]=0;
}
if(i<8)
{
temp[i]=dofly_DuanMa[num];//把按鍵值輸入到臨時數組中
for(j=0;j<=i;j++) //通過一定順序把臨時數組中
//的值賦值到顯示緩沖區,從右往左輸入
TempData[7-i+j]=temp[j];
}
i++; //輸入數值累加
if(i==9)//正常等於8即可,由於我們需要空一個用於清屏,
//清屏時的按鍵不做輸入值
{
i=0;
Flag=1;//先把比較位置1
for(j=0;j<8;j++)//循環比較8個數值,
//如果有一個不等 則最終Flag值為0
Flag=Flag&&(temp[j]==dofly_DuanMa[password[j]]);
//比較輸入值和已有密碼
for(j=0;j<8;j++)//清屏
TempData[j]=0;
if(Flag)//如果比較全部相同,標志位置1
{
TempData[0]=0x3f; // "o"
TempData[1]=0x73; // "p"
TempData[2]=0x79; // "E"
TempData[3]=0x54; // "n"
//說明密碼正確,輸入對應操作 顯示"open"
}
else
{
TempData[0]=0x79; // "E"
TempData[1]=0x50; // "r"
TempData[2]=0x50; // "r"
//否則顯示"Err"
}
}
}
}
}
/*------------------------------------------------
uS延時函數,含有輸入參數 unsigned char t,無返回值
unsigned char 是定義無符號字元變數,其值的范圍是
0~255 這里使用晶振12M,精確延時請使用匯編,大致延時
長度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延時函數,含有輸入參數 unsigned char t,無返回值
unsigned char 是定義無符號字元變數,其值的范圍是
0~255 這里使用晶振12M,精確延時請使用匯編
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延時1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
顯示函數,用於動態掃描數碼管
輸入參數 FirstBit 表示需要顯示的第一位,如賦值2表示從第三個數碼管開始顯示
如輸入0表示從第一個顯示。
Num表示需要顯示的位數,如需要顯示99兩位數值則該值輸入2
------------------------------------------------*/
void Display(unsigned char FirstBit,unsigned char Num)
{
static unsigned char i=0;
DataPort=0; //清空數據,防止有交替重影
LATCH1=1; //段鎖存
LATCH1=0;
DataPort=dofly_WeiMa[i+FirstBit]; //取位碼
LATCH2=1; //位鎖存
LATCH2=0;
DataPort=TempData[i]; //取顯示數據,段碼
LATCH1=1; //段鎖存
LATCH1=0;
i++;
if(i==Num)
i=0;
}
/*------------------------------------------------
定時器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定時器,使用"|"符號可以在使用多個定時器時不受影響
//TH0=0x00; //給定初值
//TL0=0x00;
EA=1; //總中斷打開
ET0=1; //定時器中斷打開
TR0=1; //定時器開關打開
}
/*------------------------------------------------
定時器中斷子程序
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
TH0=(65536-2000)/256; //重新賦值 2ms
TL0=(65536-2000)%256;
Display(0,8); // 調用數碼管掃描
}
/*------------------------------------------------
按鍵掃描函數,返回掃描鍵值
------------------------------------------------*/
unsigned char KeyScan(void) //鍵盤掃描函數,使用行列反轉掃描法
{
unsigned char cord_h,cord_l;//行列值中間變數
KeyPort=0x0f; //行線輸出全為0
cord_h=KeyPort&0x0f; //讀入列線值
if(cord_h!=0x0f) //先檢測有無按鍵按下
{
DelayMs(10); //去抖
if((KeyPort&0x0f)!=0x0f)
{
cord_h=KeyPort&0x0f; //讀入列線值
KeyPort=cord_h|0xf0; //輸出當前列線值
cord_l=KeyPort&0xf0; //讀入行線值
while((KeyPort&0xf0)!=0xf0);//等待松開並輸出
return(cord_h+cord_l);//鍵盤最後組合碼值
}
}return(0xff); //返回該值
}
/*------------------------------------------------
按鍵值處理函數,返回掃鍵值
------------------------------------------------*/
unsigned char KeyPro(void)
{
switch(KeyScan())
{
case 0x7e:return 0;break;//0 按下相應的鍵顯示相對應的碼值
case 0x7d:return 1;break;//1
case 0x7b:return 2;break;//2
case 0x77:return 3;break;//3
case 0xbe:return 4;break;//4
case 0xbd:return 5;break;//5
case 0xbb:return 6;break;//6
case 0xb7:return 7;break;//7
case 0xde:return 8;break;//8
case 0xdd:return 9;break;//9
case 0xdb:return 10;break;//a
case 0xd7:return 11;break;//b
case 0xee:return 12;break;//c
case 0xed:return 13;break;//d
case 0xeb:return 14;break;//e
case 0xe7:return 15;break;//f
default:return 0xff;break;
}
}
『柒』 c51單片機密碼鎖
STC89C52RC 之類的也可以
都是內置EEPROM的
不附加存儲器是不行的,因為一般AT89C51內只有可燒寫ROM和掉電丟失的RAM 而ROM是不能在單片機運行時更改的,所以你必須用24C02之類的外存儲晶元
一般51實驗班上都有I2C
『捌』 單片機密碼鎖
網上一搜一大把,常式很多,小夥子會知道不會網路啊
『玖』 51單片機 密碼鎖
不行,8位的數你知道是多少嗎?9999 9999這么大了。51單位機是8位的。你想單片機能存放得這么大的值嗎?所以還是分為兩個4位的值來存放了。要不然就出錯了。當然你也可以每一位每一位的來做,那麼每一位數定義一個8位的變數,那樣也行了。
『拾』 電子密碼鎖 用單片機實現
我博客有相關程序,包含兩個單片機的通信,一個為呼叫,另一單片機為應答,當然,如果知道密碼也不需要呼叫的密碼鎖。
說明:
1.基本部分為單片機的串口通信,包含串口通信,鍵盤掃描
2.程序部分有詳細的注釋。
/*-------------------------------------------
Project: mimasuo program (V0.1)
Filename: mimasuo.c
Prozessor: 80C51 family
Compiler: Keil Version 6.14
Autor: ********
Copyrigth: 041151**
date: 2008.3.17
------------------------------------------ */
#include<reg51.h>
#define uchar unsigned char
sbit ADCS =P3^6;
sbit ADC =P3^7;
sbit AD =P1^0;
int fafu=0;
uchar key,key1,i,count1=1,yidong=256;
uchar jgh[9]={0xff,0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00};//輸出指示
uchar jgh1[9]; //輸入鍵盤緩存
uchar mima[9]={0,1,2,3,4,5,6,7,8};//初始8位密碼 : 12345678 第0位未用
uchar fangjian[4]={0,2,5,2}; //初始門牌號 252 第0位未用
char count=0;
void init_serialcomm(void) //串口波特率設置
{
SCON=0x50;
TMOD=0x20;
PCON=0x80;
TH1=0x40;
TL1=0x40;//300
TR1=1;
EA=1;
TI=0;
RI=0;
}
void delay10ms(void) //10毫秒延時程序
{
unsigned char i,j,k;
for(i=5;i>0;i--)
for(j=4;j>0;j--)
for(k=248;k>0;k--);
}
uchar kbscan(void) // 鍵盤掃描程序
{
uchar sccode,recode;
P1=0xf0; //置所有行為低電平,行掃描,列線輸入(此時)
if((P1&0xf0)!=0xf0) //判斷是否有有鍵按下(讀取列的真實狀態,若第4列有鍵按下則P1的值會變成0111 0000),有往下執行
{
delays(); //延時去抖動(10ms)
if((P1&0xf0)!=0xf0) //再次判斷列中是否是干擾信號,不是則向下執行
{
sccode=0xFE; //逐行掃描初值(即先掃描第1行)
while((sccode&0x10)!=0)//行掃描完成時(即4行已經全部掃描完成)sccode為1110 1111 停止while程序
{
P1=sccode; //輸出行掃描碼
if ((P1&0xf0)!=0xf0) //本行有鍵按下(即P1(真實的狀態)的高四位不全為1)
{
recode=(P1&0xf0)|0x0f; //列
return(sccode&recode); //返回行和列
}
else //所掃描的行沒有鍵按下,則掃描下一行,直到4行都掃描
{
sccode=(sccode<<1)|0x01;//行掃描碼左移一位
}
}
}
}
else
{
return 0; //無鍵按下,返回0
}
}
uchar readnumber(uchar tmp) //按鍵掃描的結果,轉換為數字,便於程序對按鍵數據處理
{
switch(tmp)
{
case 0x28:return 0 ;break;
case 0x14:return 1 ;break;
case 0x24:return 2 ;break;
case 0x44:return 3 ;break;
case 0x12:return 4 ;break;
case 0x22:return 5 ;break;
case 0x42:return 6 ;break;
case 0x11:return 7 ;break;
case 0x21:return 8 ;break;
case 0x41:return 9 ;break;
case 0x88:return 10 ;break;
case 0x82:return 11 ;break;
default:break;
}
}
void main(void) //主程序
{
P2=0xff;
init_serialcomm();
while(1)
{
key=kbscan();
// P2=key;
fafu++;
if(fafu==10000){
fafu=0;
ADCS = 1;
ADC = 1;}
if(RI) //呼叫應答
{
RI=0;
ADCS = 0;
// P2=~P2;
}
if(key!=0){
do{
key1=kbscan();
AD = 0;
}while(key1!=0);//等待按鍵釋放
AD = 1;
if(readnumber(key)==10) // 密碼比較
{ count1=1;
for(i=1;i<=8;i++)
{
if(mima[i]==jgh1[i])
count1++;
}
if(count1==9)
{
// P2=~P2;
ADCS = 0;
}
else ADC=0;
}
if(readnumber(key)==11) // 呼叫房間
{ count1=1;
for(i=1;i<=3;i++)
{
if(fangjian[i]==jgh1[i])
count1++;
}
if(count1==4) //發送傳送碼
{
SBUF=0xf0;
while(TI==0);
TI=0;
P2=~P2;
}
}
if((key!=0x88)&&(key!=0x84)&&(key!=0x82)) //數字鍵輸入,並把輸入的數據存到數組中
{ count++;
P2=jgh[count];
jgh1[count]=readnumber(key);
if(count==8)
count=0;
}
if(key==0x84)
{ //取消功能鍵
count--;
if(count<=0)count=0;
P2=jgh[count];
}
}
}
}
詳細代碼可以到我博客下載:
http://www.shenzhenwangzhanyouhua.com/seo/danpianji-key-machine.html