使用51单片机C语言实现基础手表所有功能

忘了是用51单片机还是用国产的单片机做的了,模仿十来块钱的手表,功能几乎完全一样。

把当时实验的视频加上。

哈哈哈,其实是我大三上学习单片机的时候做的,好久没发了,完全复制分享下。

#include <reg52.h>
#include <intrins.h>    // 因为此文件中用到了延时函数_nop_(),所以要包含_nop_()的头文件


#define DEBUG
#ifdef DEBUG
int DebugNum=222;
#endif


#define uchar unsigned char


#define UP 80
#define DOWN 20
#define LEFT 40
#define RIGHT 60
#define NULL 0


uchar TimeDly=0; //定义闹钟延时单位秒
uchar key=0; //按键的键值
uchar flagm=0; //矩阵键盘扫描标志位
uchar count=0; //记菜单左键被按下的次数,相当于手边中左下一个键被按下
uchar ScanCount=0; //记录扫描的次数,输入时间用
//uchar MtCount=0; //定义矩阵键盘输入时间的次数
bit Flag=0; //按键是否按下标志
bit SecendFlag=1; //定义一个秒标志位,作用是没一秒,时和分之间的小数点闪一次
bit AlarmFlag=0; //闹钟时间到达标志位
bit RingFlag=0; //响铃标志位,让铃声可以是每秒响一次


//sbit ring=P1^0;
sbit ring=P3^4; //蜂鸣器


uchar year=16,month=1,day=3; //定义年月日变量


uchar ms10=0; //定义计时器变量,用定时器1
uchar chour=0;
uchar cmin=0;
uchar csecends=0;


uchar amin=1; //set alarm time and flag
uchar ahour=0;


uchar tm50=0; //定义时间寄存器,时分秒、50ms,250us
uchar tu250=0;
uchar secends=0;
uchar min=0;
uchar hour=0;


/* 定义数码管显示模块接口对应的端口 */
sbit DIG_DATA = P0^2;   // 74HC595的数据输入引脚
sbit DIG_SHCP = P0^4;   // 74HC595的移位脉冲引脚
sbit DIG_STCP = P2^3;   // 74HC595的锁存脉冲引脚


/* 变量定义 */
unsigned char DigBuf[4];    //定义数码管显示缓冲区


code unsigned char Segment[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; // 数字段码,Segment[0]~Segment[9]分别对应数字0~9的段码
code unsigned char Select[] = {0xff,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};       // 位选,Select[0]为全选,Select[1]~Select[8]分别对应第1位(最右侧)~第8位(最左侧)
code char change[17]={0,7,8,9,'1',4,5,6,'2',1,2,3,'3',0,'.','=','4'}; //按键键值检测转换表
code uchar loop[10][2]={0xfe,0x04,0xfe,0x02,0xfe,1,0xfd,1,0xfb,1,0xf7,1,0xf7,0x02,0xf7,0x04,0xef,0x04,0xdf,0x04}; //显示好看而已
uchar loops=0;


void DelayMs(int ms); //@12.000MHz
void Init(); //初始化定时器
void CheckLegal(); //检测各个时间值是否合法,如果不合法则复位
char ScanSg(); //扫描单按键 每次按键只返回一次1
char ScanMt(); //扫描矩阵按键 同上
void SetTime(uchar *hour,uchar *min);//设置时间
void ShowTime(); //显示时间
void ShowCount(); //显示计数器
void ShowAlarm(); //显示闹钟时间
void ShowData(); //显示日期
uchar ifleap(uchar years);//判断是否是瑞年
uchar maxday(uchar year,uchar month);/*每个月的天数*/
uchar week(uchar years,uchar month,uchar day); //输入年月日,返回星期几
void DigOutput(unsigned char SelectByte, unsigned char SegmentByte);//移出数据
void DigShowNumber(unsigned char DigSelect, unsigned char Number, bit Dp);//显示一个数


/*---------------------------------------------------------------------------------------------------------*/
/**********************************************
    主函数,读取并显示DS18B20的温度
**********************************************/
void main()
{
    Init();
    while(1) {
    CheckLegal(); // 时间数据监测修正
    if(ScanSg()) // 是否有按键事件
    {
        #ifdef DEBUG
        DigShowNumber(1,key/10,0);
        #endif
        if(AlarmFlag)
        AlarmFlag=0;
        
        if(count)
        {
            if(count==1) //计时器
            {
                if(key==UP)
                    TR1=~TR1;
                else if(key==DOWN || key==RIGHT) //清零
                {
                    TR1=0;
                    ms10=0;
                    chour=0;
                    cmin=0;
                    csecends=0;
                }
            }   
        else if(count==2) //如果LEFT被按下两次对应功能为设置闹钟
        {
            SetTime(&ahour,&amin);
            #ifdef DEBUG
            while(DebugNum)
            {
                ShowAlarm();
                DebugNum--;
                DigShowNumber(1,key/10,0);
            }
            DebugNum=222;
            #endif
        } else if(count==3) //如果LEFT被按下三次对应功能为设置时间
        {
            SetTime(&hour,&min);
        } else if(count==4) //如果按下四次,设置年月日
            SetTime(&month,&day);
        }
    } else if(count==1)
        ShowCount();
    else if(count==2) {
        ShowAlarm();
        DigShowNumber(2,ScanCount+1,0);
    } else if(count==3) {
        ShowTime();
        DigShowNumber(2,ScanCount+1,0);
    } else if(count==4) {
        ShowData();
        DigShowNumber(1,ScanCount+1,0);
    } else if(key==UP && Flag==1) //show the alarm clock
        ShowAlarm();
    else if(key==DOWN && Flag==1) //undetermined 可以是日期,农历,星期几
        ShowData();
    else
        ShowTime();
    }
}


/**********************************************
功能:
    中断计时
输入参数:
    无
输出参数:
    无
返回值:
**********************************************/
void intt0() interrupt 1
{
    //TH0=0x3C; //50ms
    //TL0=0xC0;
    tu250++;
    if(tu250==200) {
        // 如果是闹钟时间并且可以响铃,取反蜂鸣器
        if(AlarmFlag && RingFlag)
            ring = ~ring;
            
        tu250=0;
        tm50++;
        if(tm50==20)
        {
            if(AlarmFlag) //如果为闹钟时间,则闹钟间隔一秒响应
                RingFlag =~RingFlag;
            //否则全部清零,避免蜂鸣器在闹钟之后一直响
            else {
                ring=0;
                RingFlag = 0;
            }


            SecendFlag=~SecendFlag; //每秒小点闪烁一次

            tm50=0;
            secends++;
            loops++;
            if(loops==10)
            loops=0;
            //用一个时间每秒自减,直到运行时间剩余等于0,停止闹钟,消除标志位
            if(TimeDly) {
                TimeDly--;
                if(!TimeDly)
                AlarmFlag=0;
            }
            if(secends==60) { //计时
                min++;
                if(hour==ahour && min==amin) {
                    AlarmFlag=1;
                    TimeDly=30;
                }
                secends=0;
                if(min==60) {
                    hour++;
                    min=0;
                    if(hour==24) {
                        hour=0;
                        day++;
                        if(day > maxday(year,month)) {
                            day=1;
                            month++;
                            if(month > 12) {
                                month=1;
                                year++;
                            }
                        }
                    }
                }
            }
        }
    }
}


void intt1() interrupt 3
{
    TH1=55536/256; //10ms计时器,用来做钟表的定时器功能
    TL1=55536%256;
    TR1=1;
    ms10++;
    
    if(ms10>=100) {
        ms10=0;
        csecends++;
    }
    if(csecends>=60) {
        cmin++;
        csecends=0;
    }
    if(cmin>=60) {
        chour++;
        cmin=0;
    }
    if(chour>=24)
        chour=0;
}

// @12.000MHz
void DelayMs(int ms)
{
    uchar i,j;
    while(--ms)
    {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
    }
}

//检测各个时间值是否合法,如果不合法则复位
void CheckLegal() 
{
    if(hour>24)
        hour=0;
    if(min>60)
        min=0;
    if(secends>60)
        secends=0;
    if(ahour>24)
        hour=0;
    if(amin>60)
        min=0;
    if(day>31)
        day=0;
    if(month>12) {
        year++;
        month=0;
    }
}

//判断是否是瑞年
uchar ifleap(uchar years) 
{ 
    uchar leap;
    int year=2000;
    year += (int)years;
    if(year%4==0) { 
        if(year%100==0) { 
            if(year%400==0) 
                leap=1;
            else 
                leap=0; 
        }
    else 
        leap=1; 
    } else 
        leap=0; 
        
    return leap;
}

/*计算一个月的最大天数*/
uchar maxday(uchar year,uchar month) 
{ 
    uchar leap,max;
    leap=ifleap(year); 
    if(month==4 ||
       month==6 ||
       month==9 ||
       month==11)
        max=30; 
    else if(month==2 && leap==0) 
        max=28; 
    else if(month==2 && leap==1)
        max=29; 
    else 
        max=31;
    return max;
}

//输入年月日,返回星期几
uchar week(uchar years,uchar month,uchar day) 
{
    uchar c,y,w;
    int year=2000;
    year += years;


    if(month<3)
    {
        year=year-1;
        month=month+12;
    }


    c=year/1000*10+(year-(year/1000)*1000)/100;
    y=year-c*100;
    w=y+y/4+c/4-2*c+26*(month+1)/10+day-1;
    w=w%7;
    if(w==0)
        w=7;
  return w;
}


/*************************************
*
*function: scan singe key down
*
*input:  void
*
*out:  if keydown return back 1,else 0
*
**************************************/
char ScanSg()
{
    char temp=0x0F;
    P1 = 0x0F;
    if(Flag==1 && P1==0x0F)
        Flag=0;
    if(P1!=0x0F && Flag==0) {
        DelayMs(10);
    if(P1!=0x0F) {
        Flag=1;
        temp &= P1;
        //TimeDly=speed;
        switch(temp)
        {
        case(0X07): 
            key=RIGHT; 
            return 1;
        case(0X0b): 
            key=DOWN;  
            return 1;
        case(0X0d): 
            key=UP;    
            return 1;
        case(0X0e):
            key=LEFT;  
            count++; 
            if(count==5)
                count=0;
                return 1;
            }
        }
        return 0;
    } else
        return 0;
}


/*************************************
*
*function: scan muti key down
*
*input:  void
*
*out:  if keydown return back 1,else 0
*
**************************************/
char ScanMt()
{
    char i;
    char temp=0x0FE,count=0x0F0;
    P1 = 0x0FE;
    for(i=0; i<4; i++) {
        if(flagm==(1+i) && P1==temp)
            flagm=0;
        
        if(P1!=temp && flagm==0) {
            DelayMs(10);
            if(P1!=temp) {
                flagm=i+1;
                count &= P1;
                //TimeDly=speed;
                switch(count)
                {
                case(0X70): 
                    key=i*4+4; 
                    return 1;
                case(0X0B0):
                    key=i*4+3;  
                    return 1;
                case(0X0D0):
                    key=i*4+2;  
                    return 1;
                case(0X0E0):
                    key=i*4+1;  
                    return 1;
                }
            }
        }
        temp = _crol_(temp,1);
        P1=temp;
        count=0x0F0;
    }
    
    return 0;
}

void Init()
{
    ring=0;

    TMOD=0x12;
    TH1=55536/256;
    TL1=55536%256;
    TH0=6; //0x3C
    TL0=6; //0xC0
    ET0=1;
    ET1=1;
    EA=1;
    TR0=1;
}


/*---------------------------------------------------------------------------------------------------------*/
/* 数码管显示相关函数 */
/**********************************************
功能:
    输出位选字节和段码字节
输入参数:
    SelectByte:  位选字节
    SegmentByte: 段码字节
输出参数:
    无
返回值:
**********************************************/
void DigOutput(unsigned char SelectByte, unsigned char SegmentByte)
{
    unsigned char i;
    
    DIG_SHCP = 0;   // 74HC595的移位脉冲引脚输出低电平
    DIG_STCP = 0;   // 74HC595的锁存脉冲引脚输出低电平
    
    /* 将段码字节(共8位,高位在前)移入74HC595芯片 */
    for(i=0; i<8; i++)
    {
        /* 判断数据的最高位,如果最高位是1,数据引脚输出高电平;如果是0,输出低电平 */
        if(SegmentByte&0x80) {
            DIG_DATA = 1;
        } else {
            DIG_DATA = 0;
        }
        _nop_();
        /* 输出74HC595芯片的数据移位脉冲,每输出一个移位脉冲,74HC595内部的数据移动一位 */
        DIG_SHCP = 1;
        _nop_();
        DIG_SHCP = 0;
        _nop_();
        /* 要输出的数据左移一位,即为下一位数据的输出作准备 */
        SegmentByte <<= 1;
    }
    /* 将位选字节(共8位,高位在前)移入74HC595芯片 */
    for(i=0; i<8; i++)
    {
        if(SelectByte&0x80) {
            DIG_DATA = 1;
        } else {
            DIG_DATA = 0;
        }

        _nop_();
        DIG_SHCP = 1;
        _nop_();
        DIG_SHCP = 0;
        _nop_();
        SelectByte <<= 1;
    }
    
    /* 输出74HC595芯片的数据锁存脉冲,即将74HC595芯片接收到的最新数据输出到芯片的所有数据引脚 */
    DIG_STCP = 1;
    _nop_();
    DIG_STCP = 0;
    _nop_();
}


/**********************************************
功能:
    在某位显示数字
输入参数:
    DigSelect: 数码管位选择(1——8,即最右到最左)
    Number:    数字(0——9)
    Dp:        小数点(1:显示;0:不显示)
输出参数:
    无
返回值:
**********************************************/
void DigShowNumber(unsigned char DigSelect, unsigned char Number, bit Dp)
{
    // 同时判断位选的值是否为1——8,显示值是否为0——9。若超出范围,则不作任何操作
    if((0<DigSelect<9)&&(Number<10)) {
        if(Dp) {
            DigOutput(Select[DigSelect],(Segment[Number]&~0x80));   //点亮小数点
        } else {
            DigOutput(Select[DigSelect],(Segment[Number]|0x80));    //熄灭小数点
        }
    }
}


void ShowTime()
{
    DigShowNumber(8,hour/10,0);
    DigShowNumber(7,hour%10,SecendFlag);
    DigShowNumber(6,min/10,0);
    DigShowNumber(5,min%10,0);
    DigOutput(loop[loops][1],loop[loops][0]);
}

/*************************************
*
*function: show count time.min,secend,10ms
*
*input:  void
*
*out:  void
*
**************************************/
void ShowCount()
{
    DigShowNumber(8,chour/10,0);
    DigShowNumber(7,chour%10,1);
    DigShowNumber(6,cmin/10,0);
    DigShowNumber(5,cmin%10,1);
    DigShowNumber(4,csecends/10,0);
    DigShowNumber(3,csecends%10,1);
    DigShowNumber(2,ms10/10,0);
    DigShowNumber(1,ms10%10,0);
}

void ShowAlarm()
{
    DigShowNumber(8,ahour/10,0);
    DigShowNumber(7,ahour%10,1);
    DigShowNumber(6,amin/10,0);
    DigShowNumber(5,amin%10,0);
}


void ShowData()
{
    uchar weekday;
    weekday=week(year,month,day);

    DigShowNumber(8,year/10,0);
    DigShowNumber(7,year%10,1);
    DigShowNumber(6,month/10,0);
    DigShowNumber(5,month%10,1);
    DigShowNumber(4,day/10,0);
    DigShowNumber(3,day%10,0);
    if(Flag)
    DigShowNumber(1,weekday,0);
}


void SetTime(uchar *hour,uchar *min)
{
/***********加入的矩阵输入*****************
    uchar temp; //矩阵键盘用
    if(ScanMt()) {
        temp = change[key];
        if(0 <= temp <=9) {
            if(ScanCount==0)
                *hour += 10*temp;
            else if(ScanCount==1)
                *hour = temp;
            else if(ScanCount==2)
                *min += 10*temp;
            else
                *min=temp;
                
            ScanCount++;
            if(ScanCount==4)
                ScanCount=0;
        }
    }
*******************************************/
    if(key==UP) {
        switch(ScanCount)
        {
        case 0: (*min)++; break;
        case 1: (*min) += 10; break;
        case 2: (*hour)++; break;
        case 3: (*hour) += 10; break;
        }
    } else if(key==DOWN) {
        switch(ScanCount)
        {
        case 0: 
            (*min)--; 
            break;
        case 1: 
            (*min) -= 10; 
            break;
        case 2: 
            (*hour)--; 
            break;
        case 3: 
            (*hour) -= 10; 
            break;
        }
    } else if(key==RIGHT) {
        ScanCount++;
        if(ScanCount==4)
        ScanCount=0;
    }
}

  • 8
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值