蓝桥杯单片机第十一届模拟题“智能门锁”
题目内的功能基本实现了,但是我自己发现一个BUG就是在修改密码时,如果输入密码,按下的次数超过6时,就会卡死在修改密码的界面,应该是我修改密码索引值的问题,我没有加限制条件。如果有其他bug可以在评论区留言,一起互相学习。
·
从一开始毫无头绪,到一点一点功能实现,今天总算基本实现了所有的功能。我个人是用的B站西风大佬的底层模版,按键 数码管 LED灯。我把代码放出来,大家相互学习一下,检查一下bug。我个人也是小白。
我的代码有4个界面:
一、密码输入界面
二、密码清除界面
三、初始状态界面
四、开锁界面
我耗时间比较久就是写那个密码输入界面之后进入的开门状态,在那个状态下的功能逻辑的实现还是有点点复杂的。
原理图

题目


代码
关闭外设
#include <STC15F2K60S2.H>
void Close_ZYZ(void)
{
P0 = 0xff;
P2 = (P2 & 0x1f) | 0x80;
P2 &= 0x1f;
P0 = 0;
P2 = (P2 & 0x1f) | 0xa0;
P2 &= 0x1f;
}
LED和继电器
#include <STC15F2K60S2.H>
void Led_Disp_ZYZ(unsigned char Addr,Enable)
{
unsigned char Data = 0x00;
unsigned char Data_old =0xff;
if(Enable)
{
Data |= 0x01 << Addr;
}
else
{
Data &= ~(0x01 << Addr);
}
if(Data != Data_old)
{
P0 = ~Data;
P2 = (P2 & 0x1f) | 0x80;
P2 &= 0x1f;
Data_old = Data;
}
}
void Relay(unsigned char Enable)
{
unsigned char Data=0x00;
static unsigned char Data_old =0xff;
//读取当前P0的状态
// Data = ~P0;//
if(Enable)
{
Data |= 0x40;
}
else
{
Data &= ~0x40;
}
if(Data != Data_old)
{
P0 = ~Data;
P2 = (P2 & 0x1f) | 0xa0;
P2 &= 0x1f;
Data_old = Data;//保存当前状态
}
}
按键
#include <STC15F2K60S2.H>
//左
sbit L1 = P4^4;
sbit L2 = P4^2;
sbit L3 = P3^5;
sbit L4 = P3^4;
//右
sbit R1 = P3^3;
sbit R2 = P3^2;
sbit R3 = P3^1;
sbit R4 = P3^0;
unsigned char Key_Scan_ZYZ(void)
{
unsigned char Key_Num = 0;//按键值
L1 = 0;
L2 = L3 = L4=1;//第一列
if(R1 == 0)
{
Key_Num = 4;
}
if(R2 == 0)
{
Key_Num = 5;
}
if(R3 == 0)
{
Key_Num = 6;
}
if(R4 == 0)
{
Key_Num = 7;
}
L2 = 0;
L1 = L3 = L4=1;//第二列
if(R1 == 0)
{
Key_Num = 8;
}
if(R2 == 0)
{
Key_Num = 9;
}
if(R3 == 0)
{
Key_Num = 10;
}
if(R4 == 0)
{
Key_Num = 11;
}
L3 = 0;
L2 = L1 = L4=1;//第三列
if(R1 == 0)
{
Key_Num = 12;
}
if(R2 == 0)
{
Key_Num = 13;
}
if(R3 == 0)
{
Key_Num = 14;
}
if(R4 == 0)
{
Key_Num = 15;
}
L4 = 0;
L1 = L3 = L2=1;//第四列
if(R1 == 0)
{
Key_Num = 16;
}
if(R2 == 0)
{
Key_Num = 17;
}
if(R3 == 0)
{
Key_Num = 18;
}
if(R4 == 0)
{
Key_Num = 19;
}
return Key_Num;
}
数码管
#include <STC15F2K60S2.H>
unsigned char Smg_Data[16] ={
0xc0, // 0
0xf9, // 1
0xa4, // 2
0xb0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xf8, // 7
0x80, // 8
0x90, // 9
0xff, //关闭
0xbf, //-
0xc6, //c
0x86, //E
0xc8, //n
0x8c //p
};
unsigned char Smg_Pos[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void Smg_Disp_ZYZ(unsigned char Pos,Value,Point)
{
//消影
P0 = 0xff;
P2 = (P2 & 0x1f) | 0xe0;
P2 &= 0x1f;
P0 = Smg_Pos[Pos];//位选
P2 = (P2 & 0x1f) | 0xc0;
P2 &= 0x1f;
P0 = Smg_Data[Value];//位选
if(Point) //选择小数点
P0 &= 0x7f;
P2 = (P2 & 0x1f) | 0xe0;
P2 &= 0x1f;
}
I2c
#include "I2c.h"
#include "intrins.h"
#define DELAY_TIME 5
#define AT24C02_ADDR 0xA0
/** 定义I2C总线时钟线和数据线 */
sbit scl = P2^0;
sbit sda = P2^1;
/**
* @brief I2C总线中一些必要的延时
*
* @param[in] i - 延时时间调整.
* @return none
*/
void i2c_delay(unsigned char i)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(i--);
}
/**
* @brief 产生I2C总线启动条件.
*
* @param[in] none
* @param[out] none
* @return none
*/
void i2c_start(void)
{
sda = 1;
scl = 1;
i2c_delay(DELAY_TIME);
sda = 0;
i2c_delay(DELAY_TIME);
scl = 0;
}
/**
* @brief 产生I2C总线停止条件
*
* @param[in] none
* @param[out] none.
* @return none
*/
void i2c_stop(void)
{
sda = 0;
scl = 1;
i2c_delay(DELAY_TIME);
sda = 1;
i2c_delay(DELAY_TIME);
}
/**
* @brief I2C发送一个字节的数据
*
* @param[in] byt - 待发送的字节
* @return none
*/
void i2c_sendbyte(unsigned char byt)
{
unsigned char i;
//
EA = 0; //关闭中断,避免因为中断而影响总写读写的时序,导致读写失败。
for(i=0; i<8; i++){
scl = 0;
i2c_delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
i2c_delay(DELAY_TIME);
scl = 1;
byt <<= 1;
i2c_delay(DELAY_TIME);
}
EA = 1;
//
scl = 0;
}
/**
* @brief 等待应答
*
* @param[in] none
* @param[out] none
* @return none
*/
unsigned char i2c_waitack(void)
{
unsigned char ackbit;
scl = 1;
i2c_delay(DELAY_TIME);
ackbit = sda; //while(sda); //wait ack
scl = 0;
i2c_delay(DELAY_TIME);
return ackbit;
}
/**
* @brief I2C接收一个字节数据
*
* @param[in] none
* @param[out] da
* @return da - 从I2C总线上接收到得数据
*/
unsigned char i2c_receivebyte(void)
{
unsigned char da;
unsigned char i;
//
EA = 0;
for(i=0;i<8;i++){
scl = 1;
i2c_delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
i2c_delay(DELAY_TIME);
}
EA = 1;
//
return da;
}
/**
* @brief 发送应答
*
* @param[in] ackbit - 设定是否发送应答
* @return - none
*/
void i2c_sendack(unsigned char ackbit)
{
scl = 0;
sda = ackbit; //0:发送应答信号;1:发送非应答信号
i2c_delay(DELAY_TIME);
scl = 1;
i2c_delay(DELAY_TIME);
scl = 0;
sda = 1;
i2c_delay(DELAY_TIME);
}
/**
* @brief 读写操作过程中一些必要的延时
*
* @param[in] i - 指定延时时间
* @return - none
*/
void operate_delay(unsigned char t)
{
unsigned char i;
while(t--){
for(i=0; i<112; i++);
}
}
/**
* @brief 向AT24c02多字节写入数据
*
* @param[in] Addr内存单元地址 Data 数据 num 数据长度
* @return - none
*/
void Wirte_E2prom(unsigned char Addr,unsigned char *Data,unsigned char num)
{
i2c_start();//起始信号
i2c_sendbyte(AT24C02_ADDR);//发送设备地址
i2c_waitack();//等待从机应答
i2c_sendbyte(Addr);//发送内存单元地址
i2c_waitack();//等待从机应答
while(num--)
{
i2c_sendbyte(*Data++);//发送写入的数据
i2c_waitack();//等待从机应答
operate_delay(200);
}
i2c_stop();//停止信号
}
/**
* @brief 向AT24c02多字节读取
*
* @param[in] Addr内存单元地址 Data 数据 num 数据长度
* @return - none
*/
void Read_E2Prom(unsigned char Addr,unsigned char *Data,unsigned char num)
{
i2c_start();//起始信号
i2c_sendbyte(0xa0);//发送写设备地址
i2c_waitack();//等待从机应答
i2c_sendbyte(Addr);//发送内存单元地址
i2c_waitack();//等待从机应答
i2c_start();//起始信号
i2c_sendbyte(AT24C02_ADDR | 0x01);//发送读设备地址
i2c_waitack();//等待从机应答
while(num--)
{
*Data++ = i2c_receivebyte();//读取内存中的数据
if(num)
{
i2c_sendack(0);//产生应答信号
}
else
{
i2c_sendack(1);//产生非应答信号
}
}
i2c_stop();//停止信号
}
/**
* @brief 向AT24c02单字节读取
*
* @param[in] Addr内存单元地址
* @return - none
*/
unsigned char Read_E2prom_Byte(unsigned char Addr)
{
unsigned char Temp;
i2c_start();//起始信号
i2c_sendbyte(AT24C02_ADDR);//发送写设备地址
i2c_waitack();//等待从机应答
i2c_sendbyte(Addr);//发送内存单元地址
i2c_waitack();//等待从机应答
i2c_start();//起始信号
i2c_sendbyte(AT24C02_ADDR | 0x01);//发送读设备地址
i2c_waitack();//等待从机应答
Temp = i2c_receivebyte();//读取内存中的数据
i2c_sendack(1);//产生非应答信号
i2c_stop();//停止信号
return Temp;
}
主函数 Main
#include <STC15F2K60S2.H>
#include <string.h>
#include "ClosePher.h"//关闭外设
#include "Key.h"//按键
#include "Led.h"//LED灯
#include "Smg.h"//数码管
#include "I2c.h"//AT24C02
#define uint8_t unsigned char //255
#define uint16_t unsigned short int //65535
#define Password_Length 6 //密码长度
#define Door_Open_Timer 5000//开门时间
#define AT24C02_Frist_Run_Flag 0x00 //初次上电地址
#define AT24C02_Password_Addr 0x01 //数据存储的地址
uint8_t Key_Value,Key_Down,Key_Up,Key_Old;
uint16_t Key_Delay;
uint8_t Smg_Record[8]={10,10,10,10,10,10,10,10};
uint8_t Smg_Delay;
uint8_t Smg_Place;//数码管扫描位
uint8_t Smg_Point[8]={0,0,0,0,0,0,0,0};
uint8_t Led_Disp[8]={0,0,0,0,0,0,0,0};
uint8_t Smg_Mode;//初始界面标志位
uint8_t Mima = 10;//初始密码值
uint8_t PassWord[Password_Length]={10,10,10,10,10,10};//定义一个密码数组存放按键密码
uint8_t Init_Password[Password_Length]={8,8,8,8,8,8};//初始化的密码
uint8_t Chang_Password[Password_Length]={10,10,10,10,10,10};//修改密码存储数组
uint8_t Open_Data[8] ={0,10,10,10,0,15,13,14};//开门数组
uint8_t Password_Index;//密码索引
uint8_t Change_Password_Index;//修改密码索引值
uint16_t Open_Door_Timer;//开门时间
uint16_t Error_Timer;//输入密码错误时间
bit Key_Press;//按键按下标志位
bit Open_Door=0;//门状态标志位
bit Error_Flag;//密码错误标志位
void Key_Deal_ZYZ(void)//按键处理函数
{
uint8_t i;
if(Key_Delay)
return;
Key_Delay =1;//消抖 防止重复扫描
Key_Value = Key_Scan_ZYZ();//获取按键值
Key_Down = Key_Value & (Key_Old ^Key_Value);//检测下降沿
Key_Up = ~Key_Value & (Key_Old ^Key_Value);//检测上升沿
Key_Old = Key_Value;//按键旧事件
switch(Key_Down)
{
case 5:
Mima = 8;
break;
case 6:
Mima = 4;
break;
case 7:
Mima = 0;
break;
case 8://清除按键
Smg_Mode =2;
break;
case 9:
Mima = 9;
break;
case 10:
Mima = 5;
break;
case 11:
Mima = 1;
break;
case 12://修改密码
if(Open_Door==1)//只有门开启才能修改密码
{
if(Smg_Mode !=4)//第一次按下S12 进入密码修改模式
{
Smg_Mode = 4;//进入密码修改界面
memset(Chang_Password,10,sizeof(Chang_Password));//清除修改密码存储数组 方便下一次修改密码
}
else if(Smg_Mode == 4 && Change_Password_Index == Password_Length)//再次按下S12 密码修改完成 退出密码修改界面
{
Smg_Mode = 5;//开门界面
memcpy(Init_Password,Chang_Password,Password_Length);//将密码存入初始密码
Open_Door_Timer=0;//重置计数
Change_Password_Index=0;//修改密码索引值清0
}
}
break;
case 14:
Mima = 6;
break;
case 15:
Mima = 2;
break;
case 16://输入
Smg_Mode = 1;
break;
case 18:
Mima = 7;
break;
case 19:
Mima = 3;
break;
}
//输入模式:将密码存储到数组中
if(Smg_Mode == 1 && Key_Down>=5 && Key_Down <=19 && Key_Down !=8 && Key_Down !=12 && Key_Down !=16 && Key_Down !=13 && Key_Down !=17)
{
if(Password_Index < Password_Length)//索引值小于密码长度
{
for(i=0;i<Password_Length;i++)
{
PassWord[i] = PassWord[i+1];//数字向左存储 {10,10,10,10,10,1} {10,10,10,10,1,2}
}
PassWord[Password_Length-1] = Mima;//将新按的数字存储在最右边
}
//按键按下
Key_Press =1;
//更新按键索引值
Password_Index++;
}
//修改密码模式
if(Smg_Mode==4 && Key_Down>=5 && Key_Down <=19 && Key_Down !=8 && Key_Down !=12 && Key_Down !=16 && Key_Down !=13 && Key_Down !=17)
{
if(Change_Password_Index<Password_Length)
{
for(i=0;i<Password_Length;i++)
{
Chang_Password[i] = Chang_Password[i+1];
}
Chang_Password[Password_Length-1] = Mima;//将密码值存储在最右边
}
//更新索引值
if(++Change_Password_Index==6)
{
Change_Password_Index =6;
}
//按键按下
Key_Press =1;
}
}
void Other_Deal_ZYZ(void)//其他处理函数
{
//如果门打开 需要再按一下按键才能5S后关闭所有
if(Open_Door==1)
{
if(Open_Door_Timer<=Door_Open_Timer)//5S
{
//打开继电器
Relay(1);
if(Key_Press)//如果在开门状态下有按键按下
{
Open_Door_Timer=0;//重置计数
Key_Press =0;
}
}
else //无按键操作 全部关闭 进入关门状态
{
//密码索引清0
Password_Index = 0;
//关闭
Open_Door = 0;//关门
Relay(0);//关闭继电器
Close_ZYZ();//关闭所有外设
Smg_Mode =3;//关门状态
}
}
//密码错误 设备进入初始状态 等待新密码输入
if(Error_Flag==1)
{
if(Error_Timer<=Door_Open_Timer)//5S
{
Led_Disp[0] =1;//L1点亮
}
else
{
Led_Disp[0] =0;
Error_Flag = 0;
memset(PassWord,10,sizeof(PassWord));//清除密码
}
}
//密码输入时点亮L7
if(Smg_Mode==1 && Password_Index < 6)
{
Led_Disp[6] =1;
}
else
{
Led_Disp[6] =0;
}
//修改密码时点亮L8
if(Smg_Mode == 4 && Change_Password_Index<=6)
{
Led_Disp[7] =1;
}
else
{
Led_Disp[7] =0;
}
}
void Smg_Show_ZYZ(void)//数码管显示函数
{
uint8_t i;
if(Smg_Delay)
return;
Smg_Delay=1;//防止重复扫描
switch(Smg_Mode)
{
case 1://输入模式
if(Password_Index < Password_Length+1)
{
Smg_Record[0] = 11;//-
for(i=0;i<Password_Length;i++)
{
Smg_Record[7-i] = PassWord[Password_Length-1-i];//显示密码
}
if(Password_Index == Password_Length)//Password_Index = 6;//密码索引
{
if(memcmp(PassWord,Init_Password,sizeof(PassWord))==0)//比对密码
{
//开启就置标志位开始计时
Open_Door=1;
//定时器值重置
Open_Door_Timer=0;
Smg_Mode =5;//进入开门界面
Password_Index=7;//跳出显示 进入开门状态
}
else//密码错误 设备进入初始化初始化状态
{
Error_Timer =0;//开始计时
Error_Flag =1;
Password_Index=0;
}
}
}
break;
case 2://密码清除
for(i=0;i<6;i++)
{
PassWord[i]=10;// 10 10 ....
Smg_Record[i+2] = PassWord[i];//显示数组
}
Password_Index=0;//索引值清0
break;
case 3://初始状态 LED 数码管全部关闭
memset(PassWord,10,sizeof(PassWord));//清除密码
memset(Smg_Record,10,sizeof(Smg_Record));//清空显示
memset(Led_Disp,0,sizeof(Led_Disp));//关闭LED
Wirte_E2prom( AT24C02_Password_Addr,Init_Password,6);//修改的密码写入存储器
break;
case 4://修改密码界面
Smg_Record[0] = 12;//C
for(i=0;i<Password_Length;i++)
{
Smg_Record[7-i] = Chang_Password[Password_Length-1-i];
}
break;
case 5://开门界面
for(i=0;i<8;i++)
{
Smg_Record[i] = Open_Data[i];
}
break;
}
}
void Timer0_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA = 1; //开启总中断
}
//检测是否为初次上电
void Check_Frist_Run(void)
{
uint8_t Frist_Run_Flag;
uint8_t Defalut_Password[Password_Length] = {8,8,8,8,8,8};//默认密码
Frist_Run_Flag = Read_E2prom_Byte(AT24C02_Frist_Run_Flag);//首次上电的标志位
if(Frist_Run_Flag == 0xFF)//首次上电
{
Wirte_E2prom(AT24C02_Password_Addr,Defalut_Password,6);//存储默认密码
Wirte_E2prom(AT24C02_Frist_Run_Flag,0x00,1);//设置首次上电标志位
}
}
//读取存储器内的密码
void Read_AT24C02_Password(void)
{
Read_E2Prom(AT24C02_Password_Addr,Init_Password,6);
}
void main(void)
{
// uint8_t i=0;
Check_Frist_Run();//检测是否为第一次上电
Read_AT24C02_Password();//读取密码
//验证是否正确读取存储的密码
// for(i=0;i<8;i++)
// {
// Smg_Record[i]=Init_Password[i];
// }
Close_ZYZ();//关闭外设
Timer0_Init();
while(1)
{
Key_Deal_ZYZ();
Smg_Show_ZYZ();
Other_Deal_ZYZ();
}
}
void Timer0_Isr(void) interrupt 1
{
if(++Key_Delay == 10)//按键延时消抖
{
Key_Delay = 0;
}
if(++Smg_Delay == 500)//数码管动态扫描消影延迟500ms
{
Smg_Delay = 0;
}
if(++Smg_Place == 8)
{
Smg_Place = 0;
}
Smg_Disp_ZYZ(Smg_Place,Smg_Record[Smg_Place],Smg_Point[Smg_Place]);//动态扫描数码管
Led_Disp_ZYZ(Smg_Place,Led_Disp[Smg_Place]);//动态扫描LED
if(Open_Door==1)//开始计时
{
Open_Door_Timer++;
}
if(Error_Flag==1)
{
Error_Timer++;
}
}
题目内的功能基本实现了,但是我自己发现一个BUG就是在修改密码时,如果输入密码,按下的次数超过6时,就会卡死在修改密码的界面,应该是我修改密码索引值的问题,我没有加限制条件。如果有其他bug可以在评论区留言,一起互相学习。
更多推荐



所有评论(0)