学校单片机结课课设,课题为电子密码锁,平台为AT89C52
琢磨明白github后会把代码全部上传到GitHub,有问题可以留言或私信,看到就回。(2023.4)
系统结构
主要外设有4X4矩阵键盘、EEPROM存储、数码管
硬件原理图
用了嘉立创EDA
代码
主程序
#include "main.h"
#include "string.h"
#include "seg.h"
#include "key.h"
#include "at24c02.h"
sbit AUXR = 0x8e;
//变量
u8 key_scan_flag;
u8 key_val = 0; //读取按键标志
u8 mode = 0; //当前模式,0 - 输入/管理/登陆 1 - 提示信息界面 2 - 自锁
u8 menu_index = 0; //当前模式下的菜单模式,0 - 输入 1 - 登陆 2 - 管理
u8 error_times = 0; //错误次数
u8 fin_counter = 0; //当前已输入密码位数
u8 infor_type = 0; //0无提示消息 1-open 2-error 3-change
u8 self_down = 60;
u16 counter = 0;
code u8 char_Code[] = {
// 0 1 2 3 4 5 6 7 8 9
~0x3F,~0x06,~0x5B,~0x4F,~0x66,~0x6D,~0x7D,~0x07,~0x7F,~0x6F,
};
code u8 menu_item[] = {~0x3F,~0x31,~0x37};
code u8 information[3][8] = {
{~0x3F,~0x73,~0x79,~0x37,0xff,0xff,0xff,0xff}, //开锁
{~0x79,~0x31,~0x31,~0x3F,~0x31,0xff,0xff,0xff}, //密码错误
{~0x39,~0x76,~0x77,~0x37,~0x3d,~0x79,0xff,0xff} //更改成功
};
u8 disp_num[] = {0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff
};
u8 disp_com[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 //高到低,数码管位选
};
u8 input_code[4] = {0xff,0xff,0xff,0xff}; //用于记录已输入密码
u8 save_psw[4] = {~0x3f,~0x3f,~0x3f,~0x3f}; //用于保存正确密码,低位到高位
//函数
void Timer0Init(void); //定时器初始化
void key_switch(void); //按键选择
u8 judge_password(void); //判断密码是否正确,0 - 错误,1 - 正确
void seg_show(void); //数码管显示
void work_function(void);
void main(){
u8 i;
Timer0Init();
for(i = 0;i < 4;i++){ //第一次上电时存入初始密码,下载完后可以注释掉重新编译
AT24C02_WriteData(i,char_Code[i]);
}
for(i = 0;i < 4;i++){
save_psw[i] = AT24C02_ReadData(i);
}
while(1){
if(key_scan_flag){ //读取按键值
key_scan_flag = 0;
key_val = key_read();
}
key_switch();
work_function();
}
}
//按键模块
void key_switch(void){
if(key_val == 0) return;
if(mode == 2) return;
if(key_val == 0x77){ //S1 输入数字1
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[1];
}
}
else if(key_val == 0xb7){ //S2 输入数字2
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[2];
}
}
else if(key_val == 0xd7){ //S3 输入数字3
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[3];
}
}
else if(key_val == 0xe7){ //S4 进入管理员界面
menu_index = 1; //登陆
fin_counter = 0; //已输入的密码位数清零
error_times = 0;
}
else if(key_val == 0x7b){ //S5 输入数字4
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[4];
}
}
else if(key_val == 0xbb){ //S6 输入数字5
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[5];
}
}
else if(key_val == 0xdb){ //S7 输入数字6
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[6];
}
}
else if(key_val == 0xeb){ //S8 退出登陆
if(menu_index == 2){
menu_index = 1;
fin_counter = 0;
error_times = 0;
}
}
else if(key_val == 0x7d){ //S9 输入数字7
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[7];
}
}
else if(key_val == 0xbd){ //S10 输入数字8
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[8];
}
}
else if(key_val == 0xdd){ //S11 输入数字9
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[9];
}
}
else if(key_val == 0xed){ //S12 返回输入界面
menu_index = 0;
fin_counter = 0;
}
else if(key_val == 0x7e){ //S13 确定
if(menu_index == 0 || menu_index == 1){
if(fin_counter < 4){
fin_counter = 0;
if(++error_times == 3){
error_times = 0;
mode = 2;
counter = 0;
self_down = 60;
}else{
mode = 1;
infor_type = 2;
counter = 0;
}
}else{
fin_counter = 0;
if(judge_password()){ //密码正确
if(menu_index == 0){
mode = 1;
infor_type = 1;//开锁
counter = 0;
}else{
mode = 0;
menu_index = 2;//进入管理员界面
}
}else{
if(++error_times == 3){ //错误次数达到3次,进入自锁模式
error_times = 0;
mode = 2;
self_down = 60;
counter = 0;
}else{
mode = 1;
infor_type = 2; //错误
counter = 0;
}
}
}
}
else if(menu_index == 2){
u8 i;
if(fin_counter < 4){
mode = 1;
infor_type = 2;
counter = 0;
return;
}
//管理员界面,存储密码
for(i = 0;i < 4;i++){
save_psw[i] = input_code[i];
AT24C02_WriteData(i,save_psw[i]);
}
mode = 1; //进入提示信息
infor_type = 3;
fin_counter = 0;
counter = 0;
}
}
else if(key_val == 0xbe){ //S14 输入数字0
if(fin_counter < 4){
input_code[fin_counter++] = char_Code[0];
}
}
else if(key_val == 0xde){ //S15 回退
input_code[--fin_counter] = 0xff;
}
else if(key_val == 0xee){ //S16 清空
u8 i;
for(i = 0;i < fin_counter;i++){
input_code[i] = 0xff;
}
fin_counter = 0;
}
key_val = 0;
}
//数码管显示模块
void seg_show(void){
u8 i;
for(i = 0;i < 8;i++){
disp_seg(disp_com[i],disp_num[i]);
}
}
//判断密码
u8 judge_password(void){
u8 rtval = 0;
u8 i;
for(i = 0;i < 4;i++){
if(save_psw[i] != input_code[i]){
rtval = 0;
break;
}
rtval = 1;
}
return rtval;
}
//工作模块
void work_function(void){
//1.先判断处于哪个模式
if(mode == 0){ //正常模式
u8 i,j;
memset(disp_num,0xff,sizeof(disp_num));
disp_num[0] = menu_item[menu_index];
for(i = 4,j = 0;i < 8;i++){
if(i < 8 - fin_counter) disp_num[i] = 0xff;
else disp_num[i] = input_code[j++];
}
}
else if(mode == 1){ //提示信息界面
u8 i;
memset(disp_num,0xff,sizeof(disp_num));
for(i = 0;i < 8;i++){
disp_num[i] = information[infor_type - 1][i];
}
}
else if(mode == 2){ //自锁界面
if(self_down == 0){
mode = 0;
return;
}
memset(disp_num,0xff,sizeof(disp_num));
if(self_down > 10) disp_num[6] = char_Code[self_down / 10];
disp_num[7] = char_Code[self_down % 10];
}
seg_show();
}
//定时器模块
void Timer0Init(void) //10毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA = 1;
ET0 = 1;
}
void Timer0Function(void) interrupt 1{ //定时器0中断服务函数
static u8 key_counter = 0;
if(++key_counter == 1){
key_scan_flag = 1;
key_counter = 0;
}
if(mode == 1){
if(++counter == 200){
mode = 0;
}
}
else if(mode == 2){
if(++counter == 100){
self_down--;
counter = 0;
}
}
}
数码管底层代码
数码管用两片74HC595
构成的位移缓存器连接,可以减少引脚的使用。
#include "seg.h"
sbit SEG_DIO = P1^0;
sbit SEG_SCLK = P1^1;
sbit SEG_RCLK = P1^2;
//写数据
void write_data_595(u8 dat);
void disp_seg(u8 addr,u8 dat){
write_data_595(addr); //输出位码
write_data_595(dat); //输出段码
SEG_RCLK = 1; //锁存数据
SEG_RCLK = 0;
}
void write_data_595(u8 dat){
u8 i;
for(i = 0;i < 8;i++){
if(dat & 0x80) SEG_DIO = 1;
else SEG_DIO = 0;
dat <<= 1;
SEG_SCLK = 0;
SEG_SCLK = 1;
}
}
矩阵按键底层代码
运用了状态机的思想
#include "key.h"
u8 key_read(void){
static u8 key_state;
u8 key_rtval = 0,key_press = 0;
R4 = R3 = R2 = R1 = 0;C4 = C3 = C2 = C1 = 1;
if(C1 == 0) key_press = 0x70;
else if(C2 == 0) key_press = 0xb0;
else if(C3 == 0) key_press = 0xd0;
else if(C4 == 0) key_press = 0xe0;
else key_press = 0;
R4 = R3 = R2 = R1 = 1;C4 = C3 = C2 = C1 = 0;
if(R1 == 0) key_press |= 0x07;
else if(R2 == 0) key_press |= 0x0b;
else if(R3 == 0) key_press |= 0x0d;
else if(R4 == 0) key_press |= 0x0e;
else key_press = 0;
switch(key_state){
case 0:
if(key_press != 0) key_state = 1;
break;
case 1:
if(key_press != 0){
key_rtval = key_press;
key_state = 2;
}
else key_state = 0;
break;
case 2:
if(key_press == 0) key_state = 0;
break;
}
return key_rtval;
}