您的位置:首页 > 其它

蓝桥杯单片机第三届国赛题目-门禁系统

2021-05-24 21:42 99 查看

文章目录

  • 要点
  • 题目

    文件树

    代码

    init.c

    #include "init.h"
    
    code uchar table[][4] = {0, 1, 1, 1,
    1, 0, 1, 1,
    1, 1, 0, 1,
    1, 1, 1, 0};
    extern uchar buffer[8];
    extern uint distance;
    void SL(uchar _device, uchar _data){
    P0 = _data; SEL(_device);
    }
    
    void Timer1Init(void)		//2毫秒@12.000MHz
    {
    AUXR |= 0x40;		//定时器时钟1T模式
    TMOD &= 0x0F;		//设置定时器模式
    TL1 = 0x40;		//设置定时初值
    TH1 = 0xA2;		//设置定时初值
    TF1 = 0;		//清除TF1标志
    TR1 = 1;		//定时器1开始计时
    ET1 = 1;
    EA = 1;
    }
    
    uchar GetKey(){
    uchar i;
    C1 = 1; C2 = 1; C3 = 1; C4 = 1;
    for(i = 0; i < 4; i++){
    R1 = table[i][0]; R2 = table[i][1];
    R3 = table[i][2]; R4 = table[i][3];
    if(C1 == 0) return 1 + i*4;
    if(C2 == 0) return 2 + i*4;
    if(C3 == 0) return 3 + i*4;
    if(C4 == 0) return 4 + i*4;
    }
    return 0;
    }
    uchar FR(float _data, uchar _dig){
    uint m = 1;
    while(_dig--){
    m = m * 10;
    }
    return (uint)_data/m%10;
    }
    void Bf(uchar _0, uchar _1, uchar _2, uchar _3,
    uchar _4, uchar _5, uchar _6, uchar _7){
    buffer[0] = _0; buffer[1] = _1; buffer[2] = _2; buffer[3] = _3;
    buffer[4] = _4; buffer[5] = _5; buffer[6] = _6; buffer[7] = _7;
    }
    
    void TimeRun(t_delay* time){
    if(time->cnt++ < time->max);
    else{
    time->cnt = 0;
    time->ok = 1;
    }
    }
    
    void Delay14us()		//@12.000MHz
    {
    unsigned char i;
    
    _nop_();
    _nop_();
    i = 39;
    while (--i);
    }
    
    void Sonic_Start(){
    uchar cnt = 10;
    while(cnt--){
    ST = 1;
    Delay14us();
    ST = 0;
    Delay14us();
    }
    }
    void Sonic(){
    uint time;
    CH = 0; CL = 0; CR = 1;
    Sonic_Start();
    while((SR) && (CF == 0));
    CR = 0;
    if(CF){CF = 0; distance = 9999;}
    else{
    time = ((uint)CH<<8) | CL;
    distance = time*0.017/12;
    }
    }
    uchar Check(uchar* p1, uchar* p2){
    uchar i;
    for(i = 0; i < 6; i++){
    if(*p1 != *p2){return 0;}
    else{
    p1++; p2++;
    }
    }
    return 1;
    }
    void Copy(uchar* p1, uchar* p2){
    uchar i;
    for(i = 0; i < 6; i++){
    *p2 = *p1;
    p1++; p2++;
    }
    }

    main.c

    #include "init.h"
    code uchar CA[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99,\
    0x92, 0x82, 0xf8, 0x80, 0x90,\
    0xff, 0xbf
    };
    uchar buffer[] = {F_SEP, F_SEP, F_C, F_C, F_C, F_C, F_C, F_C};
    uchar curDig = 0;
    code enum{LED = 4, EXT, SEL, CODE};
    code uchar clear[] = {F_C, F_C, F_C, F_C, F_C, F_C};
    enum{KS_GT, KS_AS, KS_WA}keyState = KS_GT;
    uchar key, tmpKey, keyCnt;
    t_delay delay_200 = {200 ,0, 0};
    t_delay delay_500 = {500 ,0, 0};
    t_delay delay_Beep = {1500 ,0, 0};
    t_delay delay_Relay = {2500 ,0, 0};
    uint distance;
    uchar time[3];
    enum{S_AUTO, S_CODE}gloSta = S_CODE;
    uchar sonicEnable = 0;
    uchar codeCnt = 0;
    uchar errorCnt = 0;
    bit codeOk = 0;
    bit beepEnable = 0;
    bit relayEnable = 0;
    uchar codedata[] = {6,5,4,3,2,1};
    
    void Timer1Hanle() interrupt 3{
    SL(CODE, 0xff); SL(SEL, 1 << curDig); SL(CODE, CA[buffer[curDig]]);
    curDig = (curDig + 1)%8;
    switch(keyState){
    case KS_GT:
    keyCnt = 0;tmpKey = GetKey();keyState = KS_AS;
    break;
    
    case KS_AS:
    if(keyCnt++ < 10);
    else if(tmpKey == GetKey()){
    if(tmpKey != key){
    key = tmpKey;keyState = KS_WA;
    }
    else{
    keyState = KS_GT;
    }
    }
    else{
    keyState = KS_GT;
    }
    break;
    }
    TimeRun(&delay_200);
    TimeRun(&delay_500);
    if(delay_Beep.ok == -1)
    TimeRun(&delay_Beep);
    if(delay_Relay.ok == -1)
    TimeRun(&delay_Relay);
    }
    
    void Sensor(){
    if(delay_200.ok){
    delay_200.ok = 0;
    if(sonicEnable)
    Sonic();
    //如果小于30cm并且继电器没有开启并且在自动门模式则开启继电器
    if((distance < 30) && (delay_Relay.ok != -1) && (gloSta == S_AUTO)){
    RLON;
    delay_Relay.ok = -1;
    }
    DS1302_Read();
    //通过时间判断处于哪个模式
    if((time[0] == 0) && (time[1] == 0) && time[2] == 7){
    gloSta = S_AUTO;
    sonicEnable = 1;
    }
    if((time[0] == 0) && (time[1] == 0) && time[2] == 22){
    gloSta = S_CODE;
    sonicEnable = 0;
    }
    //如果是自动模式,则显示时间
    if(gloSta == S_AUTO){
    Bf(FR(time[2],1),FR(time[2],0),F_SEP,
    FR(time[1],1),FR(time[1],0),F_SEP,
    FR(time[0],1),FR(time[0],0));
    }
    }
    if(delay_Beep.ok == 1){
    delay_Beep.ok = 0;
    BPOFF;RLOFF;
    }
    if(delay_Relay.ok == 1){
    delay_Relay.ok = 0;
    RLOFF;
    }
    }
    void main(){
    BPOFF; RLOFF;
    SL(LED, 0xff);
    Timer1Init();
    CMOD |= 0x08;
    DS1302_init(06, 59, 00);
    CODE_Read(0, &codedata[0]);
    while(1){
    Sensor();
    if(keyState == KS_WA){
    //如果是手动密码模式
    if(gloSta == S_CODE){
    //如果按键处于0-9之间
    if(key < 11 && key > 0){
    //如果小于六位,则每次将按键值显示,并且按键计数+1
    if(codeCnt < 6){
    buffer[codeCnt+2] = key - 1;
    codeCnt++;
    }
    }
    //设置按键
    else if(key == 11){
    if(WAITINPUT){
    //进入输入旧密码界面
    Copy(&clear[0],&buffer[2]);
    buffer[0] = F_C;
    codeCnt = 0;
    errorCnt = 0;
    }
    }
    //如果是确认按键
    else if(key == 15){
    //如果是等待输入,则确认按键是确认当前输入密码
    if(WAITINPUT){
    //检查密码是否正确
    if(Check(&buffer[2], &codedata[0])){
    //如果正确,打开继电器
    RLON;
    delay_Relay.ok = -1;
    errorCnt = 0;
    }
    //如果错误
    else{
    //如果错误次数小于3,错误次数+1
    if(errorCnt < 3){
    errorCnt++;
    }
    //否则响起蜂鸣器
    else{
    errorCnt = 0;
    BPON;RLOFF;
    delay_Beep.ok = 1;
    }
    }
    }
    //如果是输入旧密码界面
    else if(OLDCODE){
    //检查密码是否正确
    if(Check(&buffer[2], &codedata[0])){
    //如果正确,进入输入新密码状态
    buffer[0] = F_SEP;buffer[1] = F_C;
    codeCnt = 0;
    errorCnt = 0;
    }
    //如果错误
    else{
    //如果错误次数小于3,错误次数+1
    if(errorCnt < 3){
    errorCnt++;
    }
    //否则响起蜂鸣器,!并且退出修改密码功能
    else{
    errorCnt = 0;
    BPON;RLOFF;
    delay_Beep.ok = 1;
    buffer[0] = F_SEP;buffer[1] = F_SEP;
    codeCnt = 0;
    }
    }
    }
    //如果是输入新密码界面
    else if(NEWCODE){
    //检查密码是否为6位
    if(codeCnt == 6){
    Copy(&buffer[2], &codedata[0]);
    buffer[0] = F_SEP;buffer[1] = F_SEP;
    codeCnt = 0;
    errorCnt = 0;
    //这里我发现写一次有问题,我就重复写三次了
    CODE_Write(0,&codedata[0]);
    CODE_Write(0,&codedata[0]);
    CODE_Write(0,&codedata[0]);
    }
    }
    //不管哪种情况按下确认按键后,都会清空密码
    Copy(&clear[0], &buffer[2]);
    codeCnt = 0;
    }
    //复位按键
    else if(key == 12){
    uchar defaultcode[] = {6,5,4,3,2,1};
    Copy(&defaultcode[0], &codedata[0]);
    //这里我发现写一次有问题,我就重复写三次了
    CODE_Write(0,&codedata[0]);
    CODE_Write(0,&codedata[0]);
    CODE_Write(0,&codedata[0]);
    }
    //退出按键
    else if(key == 16){
    if((OLDCODE) || (NEWCODE)){
    errorCnt = 0;
    buffer[0] = F_SEP;buffer[1] = F_SEP;
    codeCnt = 0;
    Copy(&clear[0],&buffer[2]);
    }
    }
    }
    keyState = KS_GT;
    }
    }
    }

    ds1302.c

    /*
    程序说明: DS1302驱动程序
    软件环境: Keil uVision 4.10
    硬件环境: CT107单片机综合实训平台 8051,12MHz
    日    期: 2011-8-9
    */
    
    #include <reg52.h>
    #include <intrins.h>
    #include "type.h"
    sbit SCK=P1^7;
    sbit SDA=P2^3;
    sbit RST = P1^3;   // DS1302复位
    extern uchar time[3];
    void Write_Ds1302(unsigned  char temp)
    {
    unsigned char i;
    for (i=0;i<8;i++)
    {
    SCK=0;
    SDA=temp&0x01;
    temp>>=1;
    SCK=1;
    }
    }
    
    void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
    {
    RST=0;	_nop_();
    SCK=0;	_nop_();
    RST=1; 	_nop_();
    Write_Ds1302(address);
    Write_Ds1302(dat);
    RST=0;
    }
    uchar Read(){
    unsigned char i,temp=0x00;
    for (i=0;i<8;i++){
    SCK=0;
    temp>>=1;
    if(SDA)
    temp|=0x80;
    SCK=1;
    }
    return (temp);
    }
    void DS1302_init(uchar _h, uchar _m, uchar _s){
    uchar i;
    _h = (_h/10%10 << 4) | _h%10;
    _m = (_m/10%10 << 4) | _m%10;
    _s = (_s/10%10 << 4) | _s%10;
    Write_Ds1302_Byte(0x8e, 0);
    RST=0;	_nop_();
    SCK=0;	_nop_();
    RST=1; 	_nop_();
    Write_Ds1302(0xbe);
    Write_Ds1302(_s);
    Write_Ds1302(_m);
    Write_Ds1302(_h);
    for(i = 0; i < 5; i++){
    Write_Ds1302(0);
    }
    RST=0;
    Write_Ds1302_Byte(0x8e, 0x80);
    }
    void DS1302_Read(){
    Write_Ds1302_Byte(0x8e, 0);
    RST=0;	_nop_();
    SCK=0;	_nop_();
    RST=1;	_nop_();
    Write_Ds1302(0xbf);
    time[0] = Read();
    time[1] = Read();
    time[2] = Read();
    time[0] = (time[0] >> 4)*10 + (time[0] & 0x0f);
    time[1] = (time[1] >> 4)*10 + (time[1] & 0x0f);
    time[2] = (time[2] >> 4)*10 + (time[2] & 0x0f);
    RST=0;	_nop_();
    SCK=0;	_nop_();
    SCK=1;	_nop_();
    SDA=0;	_nop_();
    SDA=1;	_nop_();
    Write_Ds1302_Byte(0x8e, 0x80);
    }

    iic.c

    /*
    程序说明: IIC总线驱动程序
    软件环境: Keil uVision 4.10
    硬件环境: CT107单片机综合实训平台 8051,12MHz
    日    期: 2011-8-9
    */
    
    #include "reg52.h"
    #include "intrins.h"
    #include "type.h"
    #define DELAY_TIME 5
    
    #define SlaveAddrW 0xA0
    #define SlaveAddrR 0xA1
    uint IICVal;
    //总线引脚定义
    sbit SDA = P2^1;  /* 数据线 */
    sbit SCL = P2^0;  /* 时钟线 */
    
    void IIC_Delay(unsigned char i)
    {
    do{_nop_();}
    while(i--);
    }
    //总线启动条件
    void IIC_Start(void)
    {
    SDA = 1;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 0;
    }
    
    //总线停止条件
    void IIC_Stop(void)
    {
    SDA = 0;
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SDA = 1;
    IIC_Delay(DELAY_TIME);
    }
    
    //发送应答
    void IIC_SendAck(bit ackbit)
    {
    SCL = 0;
    SDA = ackbit;  					// 0:应答,1:非应答
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    SCL = 0;
    SDA = 1;
    IIC_Delay(DELAY_TIME);
    }
    
    //等待应答
    bit IIC_WaitAck(void)
    {
    bit ackbit;
    SCL  = 1;
    IIC_Delay(DELAY_TIME);
    ackbit = SDA;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    return ackbit;
    }
    
    //通过I2C总线发送数据
    void IIC_SendByte(unsigned char byt)
    {
    unsigned char i;
    
    for(i=0; i<8; i++)
    {
    SCL  = 0;
    IIC_Delay(DELAY_TIME);
    if(byt & 0x80) SDA  = 1;
    else SDA  = 0;
    IIC_Delay(DELAY_TIME);
    SCL = 1;
    byt <<= 1;
    IIC_Delay(DELAY_TIME);
    }
    SCL  = 0;
    }
    
    //从I2C总线上接收数据
    unsigned char IIC_RecByte(void)
    {
    unsigned char i, da;
    for(i=0; i<8; i++)
    {
    SCL = 1;
    IIC_Delay(DELAY_TIME);
    da <<= 1;
    if(SDA) da |= 1;
    SCL = 0;
    IIC_Delay(DELAY_TIME);
    }
    return da;
    }
    void Delay1ms()		//@12.000MHz
    {
    unsigned char i, j;
    
    i = 47;
    j = 174;
    do
    {
    while (--j);
    } while (--i);
    }
    
    void CODE_Write(uchar _addr, uchar* _data){
    uchar i = 6;
    IIC_Start();
    IIC_SendByte(0xA0);
    IIC_WaitAck();
    IIC_SendByte(_addr*6);
    IIC_WaitAck();
    while(i--){
    IIC_SendByte(*_data);
    _data++;
    IIC_WaitAck();
    }
    IIC_Stop();
    //加个延时很重要
    Delay1ms();
    }
    void CODE_Read(uchar _addr, uchar *_des){
    uchar i = 6;
    IIC_Start();
    IIC_SendByte(0xA0);
    IIC_WaitAck();
    IIC_SendByte(_addr*6);
    IIC_WaitAck();
    IIC_Start();
    IIC_SendByte(0xA1);
    IIC_WaitAck();
    while(i--){
    *_des = IIC_RecByte();
    _des++;
    IIC_SendAck(0);
    }
    IIC_Stop();
    }

    type.h

    #ifndef _TYPE_H
    #define _TYPE_H
    #define uchar unsigned char
    #define uint unsigned int
    #endif

    ds1302.h

    #ifndef __DS1302_H
    #define __DS1302_H
    
    void Write_Ds1302(unsigned char temp);
    void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
    unsigned char Read_Ds1302_Byte( unsigned char address );
    void DS1302_Read();
    void DS1302_init(uchar _h, uchar _m, uchar _s);
    #endif

    iic.h

    #ifndef _IIC_H
    #define _IIC_H
    extern uint IICVal;
    void IIC_Start(void);
    void IIC_Stop(void);
    bit IIC_WaitAck(void);
    void IIC_SendAck(bit ackbit);
    void IIC_SendByte(unsigned char byt);
    unsigned char IIC_RecByte(void);
    void IIC_Write(uchar _addr, uint _data);
    void IIC_Read(uchar _addr);
    void CODE_Write(uchar _addr, uchar* _data);
    void CODE_Read(uchar _addr, uchar *_des);
    #endif

    要点

    1. 在于io打开后定时关闭,这个问题需要自己总结一个写法,在比赛中很多时候需要你,打开蜂鸣器后,延时3s然后关闭蜂鸣器;
    2. 第二个是一个技巧,节省变量,比如这里我使用数码管的显示来判断当前输入是什么状态
    3. 使用指针来判断数据相同
    4. 使用此函数来返回数据的位数

      比如:
    uchar a = 123;
    FR(a,2)就等于1
    FR(a,1)就等于2
    FR(a,0)就等于3
    //这里输入类型_data标识为float是为了适应uchar , uint以及float三种变量,它们都可以通过这一个函数来进行位数的返回
    float a = 456.22;
    FR(a,2)就等于4
    FR(a,1)就等于5
    FR(a,0)就等于6
    1. 连续写和连续读iic
    void CODE_Write(uchar _addr, uchar* _data){
    uchar i = 6;
    IIC_Start();
    IIC_SendByte(0xA0);
    IIC_WaitAck();
    IIC_SendByte(_addr*6);
    IIC_WaitAck();
    while(i--){
    IIC_SendByte(*_data);
    _data++;
    IIC_WaitAck();
    }
    IIC_Stop();
    //加个延时很重要
    Delay1ms();
    }
    void CODE_Read(uchar _addr, uchar *_des){
    uchar i = 6;
    IIC_Start();
    IIC_SendByte(0xA0);
    IIC_WaitAck();
    IIC_SendByte(_addr*6);
    IIC_WaitAck();
    IIC_Start();
    IIC_SendByte(0xA1);
    IIC_WaitAck();
    while(i--){
    *_des = IIC_RecByte();
    _des++;
    IIC_SendAck(0);
    }
    IIC_Stop();
    }

    注意

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: