单片机课程设计实验报告+基于单片机的数字时钟+含完整实验代码

单片机课程设计报告

基于单片机的数字时钟

                            姓名:

                            班级:

                            学号:


一、    前言

利用实验板上的4个LED数码管,设计带有闹铃、秒表功能的数字时钟。

功能要求:

a)         计时并显示(LED)。由于实验板上只有4位数码管,可设计成显示“时分”和显示“分秒”并可切换。

b)        时间调整功能。利用4个独立按钮,实现时钟调整功能。这4个按钮的功能为工作模式切换按钮(MODE),数字加(INC),数字减(DEC)和数字移位(SHITF)。

c)         定闹功能。利用4个独立按钮设定闹钟时间,时间到以蜂鸣器响、继电器动作作为闹铃。

d)        秒表功能。最小时间单位0.01秒。

二、    硬件原理分析

1.   电源部分

    电源部份采用两种输入接口(如上图)。

a)         外电源供电,采用2.1电源座,可接入电源DC5V,经单向保护D1接入开关S1。

b)        USB供电,USB供电口输入电源也经D1单向保护,送到开关S1。

    注:两路电源输入是并连的,因此只选择一路就可以了,以免出问题。

    S1为板子工作电源开关,按下后接通电源,提供VCC给板子各功能电路。电路采用两个滤波电容,给板子一个更加稳定的工作电源。LED为电源的指示灯,通电后LED灯亮。

2.   蜂鸣器

    蜂鸣器分为有源和无源两种,有源即两引脚有一个直流电源就可以长鸣,无源则需要一个1K左右的脉冲才可以蜂鸣,因此对于按键的提示音及报警蜂鸣使用有源来得方便。有源也可以当无源使用,而无源则不能当有源使用,当然用有源蜂鸣器作音乐发声会失真厉害。

如上图:单片机P15输出高低电平经R21连接三极管B极,控制三极管的导通与截止,从而控制蜂鸣器的工作。低电平时三极管导通,蜂鸣器得电蜂鸣,高电平时三极管截止,蜂鸣器失电关闭蜂鸣。


3.   数码管

    电路使用一个四位共阳型数码管,四个公共阳级由三极管放大电流来驱动,三极管由P10-P13控制开与关。数码管的阴级由P0口经过电阻限流连接。

   例如,要十位的数码管工作,P12输出0,使三极管Q12导通,8脚得电,当P0口相应位有输出0时,点亮相应的LED灯组合各种字符数字。

4.   独立键盘

如上图,由六个按键组成,每个按键的一端连接IO口,另一端直接连接GND(0电位)。六个按键分别接入P37-P32,只要按下按键,相应位的IO口位将被拉为低电平(0),程序可以判断相应位是否为0来确认按键己按下。

5.   动态显示

    动态显示驱动电路是单片机应用中最常用的显示方式。

    动态显示接口电路是把所有显示器的8个笔划段的同名段连在一起,而每一个显示器的公共极COM是各自独立地受I/O线控制。CPU向字段输出口送出字形码时,所有显示器接收到相同的字形码,但究竟是哪个显示器亮,则取决于COM端。

在轮流点亮扫描过程中,每位显示器的点亮时间是极为短暂的(约1ms),但由于人眼的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。

    动态扫描显示必须由CPU不断地调用显示程序,才能保证持续不断的显示。在实际工作中,当然不可能只显示,这样在两次调用显示程序之间的时间间隔就不一定了,如果时间间隔比较长,就会使显示不连续。


三、    软件设计

l  主程序

将按键1设定为功能选择键,每按一次按键1,进入相应的功能模式。

l  分秒显示

分秒显示模式下,数码管显示时钟时间的分和秒。当按键1按下后,进入下一模式。

l  时分显示

时分显示模式下,数码管显示时钟时间的时和分。当按键1按下后,进入下一模式。


l  时间调整

时间调整模式下,仅调整时钟的时、分。

数码管闪烁的为当前调整的位子,按键2为加,按键3为减,按键4为移位键。


l  闹钟

闹钟模式下,可设置闹钟的时、分。

数码管闪烁的为当前调整的位子,按键2为加,按键3为减,按键4为移位键。

当闹钟时间调整完毕之后,按下按键5,确认闹钟时间。随后等待时钟时间到达闹钟时间。时钟时间到达闹钟时间后,蜂鸣器工作。

l  秒表

秒表模式下,按下按键4,秒表开始计时;再次按下按键4,秒表暂停。

按下按键2或按键3,秒表清零。


四、    软件调试

在软件调试过程中碰到了很多问题。其中比较重要的几个问题如下:

1、时钟调整子程序中,时、分的十位数、个位数加减不符合常识。比如在加减时,小时可以达到27、28等数值,而这是不符合常识的。

在查看代码后发现,可以在加、减过程中设置if函数判断当前所处位置,调整当前数值所能达到的最大值或最小值。

修改后代码如下:

            else if(keyval==0xbf)     //按键2  加

                {

                    if(pos==0)

                    {

                        dispbuf[pos]++;

                        if(dispbuf[pos]==3)

                        {

                            dispbuf[pos]=0;

                        }

                    }

                    else if(pos==1)

                    {  

                        if(dispbuf[0]==0||dispbuf[0]==1)   

                        {

                            dispbuf[pos]++;

                            dispbuf[pos]=dispbuf[pos]%10;

                        }

                        if(dispbuf[0]==2)  

                        {

                            dispbuf[pos]++;

                            if(dispbuf[pos]>=4)

                            {

                                dispbuf[pos]=0;

                            }

                        }              

                    }

                    else if(pos==2)

                    {

                        dispbuf[pos]++;

                        if(dispbuf[pos]==6)

                        {

                            dispbuf[pos]=0;

                        }

                    }

                    else if(pos==3)

                    {                                                          

                        dispbuf[pos]++;

                        dispbuf[pos]=dispbuf[pos]%10;

                    }      

                }

                else if(keyval==0xdf)         //按键3   减 

                {

                    if(pos==0)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=2;

                        else dispbuf[pos]--;

                    }

                    else if(pos==1)

                    {

                        if(dispbuf[0]==0||dispbuf[0]==1)   

                        {

                            if(dispbuf[pos]==0)dispbuf[pos]=9;

                            else dispbuf[pos]--;

                        }

                        if(dispbuf[0]==2)  

                        {

                            if(dispbuf[pos]>=4&&dispbuf[pos]<=9)

                            {

                                dispbuf[pos]=3;

                            }

                            else if(dispbuf[pos]==0)

                            {

                                dispbuf[pos]=3;

                            }

                            else

                            {

                                dispbuf[pos]--;

                            }

                        }                  

                    }

                    else if(pos==2)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=5;

                        else dispbuf[pos]--;

                    }

                    else if(pos==3)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=9;

                        else dispbuf[pos]--;       

                    }

                }

2、秒表模式下,无法很好地区分当前计时所达到的具体数值。在秒的个位数位子设置一个小数点,使秒表的显示更加直观。

修改后代码如下:

在程序开始处,添加如下代码:

unsigned char code segtab_dot[]=

            {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};    //带小数点的数码管代码

    在void t0int(void) interrupt 1 using 1之中添加如下代码:

if(mode==4&&P1==0xfd)   //秒表处显示小数点的if语句

    {

        P0=segtab_dot[dispbuf[dispindex]];

    }

    else

    {

        P0=segtab[dispbuf[dispindex]];

    }


3、在秒表运行一遍后,进入其他模式操作完毕,再次进入秒表模式下发现秒表无法清零。检查代码后发现,在闹钟模式下,进入秒表模式时,未对秒表清零。

修改后代码如下:

                if(keyval==0x7f)//1号按键  模式  进入秒表模式

                {

                    mode=4;

                    old_ss=0;

                    old_ms=0;

                    miaobiao_ss=old_ss;

                    miaobiao_ms=old_ms;

                    dispbuf[0]=miaobiao_ss/10;

                    dispbuf[1]=miaobiao_ss%10;

                    dispbuf[2]=miaobiao_ms/10;

                    dispbuf[3]=miaobiao_ms%10;

                    clocksing=0;

                }

附完整实验代码:

/***********************************************************************

    基于单片机的数字时钟

    功能:时钟、闹钟、秒表

   

    模式0:时钟显示模式-分、秒

        模式0下,数码管显示时钟时间-分、秒。

       

        按键说明:

            按键1:选择下一模式。

    模式1:时钟显示模式-时、分

        模式1下,数码管显示时钟时间-时、分。

        按键说明:

            按键1:选择下一模式。

    模式2:时钟调整模式

        模式2下,调整时钟时间,仅调整时、分时间。

        按键说明:

            按键1:选择下一模式。

            按键2:增加选中数码管的数值。

            按键3:减少选中数码管的数值。

            按键4:移位键,按键选择下一数码管。

    模式3:闹钟模式

        模式3下,设置闹铃时间之后,等时钟到达相应时间,发出铃声。

        按键说明:

            按键1:选择下一模式。

            按键2:增加选中数码管的数值。

            按键3:减少选中数码管的数值。

            按键4:移位键,按键选择下一数码管。

            按键5:设置闹铃时间后,按键确认,数码管显示当前时钟时间。

    模式4:秒表模式

        模式4为秒表模式,可记录0-59秒。最小时间单位为0.01秒。

        按键说明:

            按键1:选择下一模式。

            按键2:清除秒表当前数值。

            按键3:清除秒表当前数值。

            按键4:开始、暂停秒表。

***********************************************************************/

#include <reg52.h>

unsigned char dispbuf[4]={0,0,0,0};

unsigned char code segtab[]=

            {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};    //不带小数点的数码管代码

unsigned char code segtab_dot[]=

            {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};    //带小数点的数码管代码

unsigned char code bittab[]={0xfe,0xfd,0xfb,0xf7};

unsigned char dispindex;

unsigned char mode;

unsigned char pos;

unsigned char keyval;

unsigned char hh,mm,ss;

unsigned char miaobiao_ms=0,miaobiao_ss=0;  //秒表

unsigned char old_ms,old_ss;    //保存秒表时间

unsigned char clock_hh,clock_mm;    //用于存放所设闹钟时间

unsigned int clock_shanshuo;    //用于控制闹钟调整时间时是否闪烁 0:闪烁  1:不闪烁

sbit clocksing=P1^5;    //闹钟所用蜂鸣器

unsigned int cnt;       //用于10ms转1s计数

unsigned char cnt1;     //用于10ms转0.5s处计数(用于闪烁)

unsigned char cnt2;     //用于秒表处计时

unsigned char keyscan(void);

void delay(unsigned int t);

void keypro(void);

bit flash;

/******************主函数******************/

void main(void)

{

    TMOD=0x11;

    TH0=(0-2000)/256;

    TL0=(0-2000)%256;

    ET0=1;

    TR0=1;

   

    TH1=(0-2000)/256;

    TL1=(0-2000)%256;

    ET1=1;

    TR1=0;      //秒表 先不开

    EA=1;

   

    mode=0;

    hh=02;

    mm=03;

    ss=15;

    while(1)

    {  

        if(mode==3)

        {

            if(hh==clock_hh)

            {

                if(mm==clock_mm)

                {

                    clocksing=0;

                }

            }

            else

            {

                clocksing=1;

            }

        }

        else

        {

            clocksing=1;

        }

        if(keyscan()==0xff) continue;

        keypro();  

    }

}

/******************键扫描程序******************/

unsigned char keyscan(void)

{

        P3=0xff;

        keyval=P3;

        if(keyval==0xff)

        {

            return 0xff;

        }

        delay(10000);

        if(keyval!=P3)

        {

            return 0xff;

        }

        else

        {

            while(P3!=0xff);

            return keyval;

        }  

}

/******************时钟中断及显示程序******************/

void t0int(void) interrupt 1 using 1

{

    TH0=(0-2000)/256;   //2ms

    TL0=(0-2000)%256;

    P0=0xff;

    P1=0xff;

   

    P1=bittab[dispindex];

    if(mode==4&&P1==0xfd)   //秒表处显示小数点的if语句

    {

        P0=segtab_dot[dispbuf[dispindex]];

    }

    else

    {

        P0=segtab[dispbuf[dispindex]];

    }

    if((dispindex==pos)&&(flash==1)&&(mode==2||(mode==3&&clock_shanshuo==0)))   //闪烁控制程序

    {

        P0=0xff;

        P1=0xf0;

    }

    dispindex++;

    dispindex=dispindex&0x03;

    cnt++;

    cnt1++;

    if(cnt1>=250)   //0.5ms闪烁

    {

        cnt1=0;

        flash=~flash;

    }

    if(cnt>=500)    //2ms*500=1s

    {

        cnt=0;

        ss++;

        if(ss>=60)

        {

            ss=0;

            mm++;

            if(mm>=60)

            {

                mm=0;

                hh++;

                if(hh>=24)

                {

                    hh=0;

                }

            }

        }

        if(mode==0)

        {

            dispbuf[0]=mm/10;

            dispbuf[1]=mm%10;

            dispbuf[2]=ss/10;

            dispbuf[3]=ss%10;

        }

        else if(mode==1||(mode==3&&clock_shanshuo==1))

        {

            dispbuf[0]=hh/10;

            dispbuf[1]=hh%10;

            dispbuf[2]=mm/10;

            dispbuf[3]=mm%10;

        }                            

    }

}

/******************按键选择,进入不同模式******************/

void keypro(void)

{

    switch(mode)

        {

            case 0://显示分秒

                if(keyval==0x7f)

                {

                    mode=1;

                    dispbuf[0]=hh/10;

                    dispbuf[1]=hh%10;

                    dispbuf[2]=mm/10;

                    dispbuf[3]=mm%10;

                   

                }

                break;

                           

            case 1://显示时分

                if(keyval==0x7f)        

                {

                    mode=2;

                    dispbuf[0]=hh/10;

                    dispbuf[1]=hh%10;

                    dispbuf[2]=mm/10;

                    dispbuf[3]=mm%10;

                }

                break;

       

            case 2://时间调整(只调整时分) 

               

                if(keyval==0x7f)         //按键1    调整模式    进入闹钟模式

                {                                                                                  

                    mode=3;

                    clock_hh=0;

                    clock_mm=0;

                    clock_shanshuo=0;

                    hh=dispbuf[0]*10+dispbuf[1];    //保存调整后的时间

                    mm=dispbuf[2]*10+dispbuf[3];

                    dispbuf[0]=clock_hh/10;         //显示闹钟的初始时间

                    dispbuf[1]=clock_hh%10;

                    dispbuf[2]=clock_mm/10;

                    dispbuf[3]=clock_mm%10;

                    TR0=1;                 

                }

                else if(keyval==0xbf)     //按键2  加

                {

                    if(pos==0)

                    {

                        dispbuf[pos]++;

                        if(dispbuf[pos]==3)

                        {

                            dispbuf[pos]=0;

                        }

                    }

                    else if(pos==1)

                    {  

                        if(dispbuf[0]==0||dispbuf[0]==1)   

                        {

                            dispbuf[pos]++;

                            dispbuf[pos]=dispbuf[pos]%10;

                        }

                        if(dispbuf[0]==2)  

                        {

                            dispbuf[pos]++;

                            if(dispbuf[pos]>=4)

                            {

                                dispbuf[pos]=0;

                            }

                        }              

                    }

                    else if(pos==2)

                    {

                        dispbuf[pos]++;

                        if(dispbuf[pos]==6)

                        {

                            dispbuf[pos]=0;

                        }

                    }

                    else if(pos==3)

                    {                                                          

                        dispbuf[pos]++;

                        dispbuf[pos]=dispbuf[pos]%10;

                    }      

                }

                else if(keyval==0xdf)         //按键3   减 

                {

                    if(pos==0)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=2;

                        else dispbuf[pos]--;

                    }

                    else if(pos==1)

                    {

                        if(dispbuf[0]==0||dispbuf[0]==1)   

                        {

                            if(dispbuf[pos]==0)dispbuf[pos]=9;

                            else dispbuf[pos]--;

                        }

                        if(dispbuf[0]==2)  

                        {

                            if(dispbuf[pos]>=4&&dispbuf[pos]<=9)

                            {

                                dispbuf[pos]=3;

                            }

                            else if(dispbuf[pos]==0)

                            {

                                dispbuf[pos]=3;

                            }

                            else

                            {

                                dispbuf[pos]--;

                            }

                        }                  

                    }

                    else if(pos==2)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=5;

                        else dispbuf[pos]--;

                    }

                    else if(pos==3)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=9;

                        else dispbuf[pos]--;       

                    }

                }

                else if(keyval==0xef)       //按键4  移位

                {

                    pos++;

                    pos=pos&03;

                }

                break;

   

            case 3: //闹钟模式                                                                                                                                                                                                                                                                     

               

                if(keyval==0x7f)    //按键1     调整模式    进入秒表模式

                {

                    mode=4;

                    old_ss=0;

                    old_ms=0;

                    miaobiao_ss=old_ss;

                    miaobiao_ms=old_ms;

                    dispbuf[0]=miaobiao_ss/10;

                    dispbuf[1]=miaobiao_ss%10;

                    dispbuf[2]=miaobiao_ms/10;

                    dispbuf[3]=miaobiao_ms%10;

                }

                else if(keyval==0xbf)   //按键2 加

                {

                    clock_shanshuo=0;

                    if(pos==0)

                    {

                        dispbuf[pos]++;

                        if(dispbuf[pos]==3)

                        {

                            dispbuf[pos]=0;

                        }

                    }

                    else if(pos==1)

                    {  

                        if(dispbuf[0]==0||dispbuf[0]==1)   

                        {

                            dispbuf[pos]++;

                            dispbuf[pos]=dispbuf[pos]%10;

                        }

                        if(dispbuf[0]==2)  

                        {

                            dispbuf[pos]++;

                            if(dispbuf[pos]>=4)

                            {

                                dispbuf[pos]=0;

                            }

                        }              

                    }

                    else if(pos==2)

                    {

                        dispbuf[pos]++;

                        if(dispbuf[pos]==6)

                        {

                            dispbuf[pos]=0;

                        }

                    }

                    else if(pos==3)

                    {                                                          

                        dispbuf[pos]++;

                        dispbuf[pos]=dispbuf[pos]%10;

                    }      

                }

                else if(keyval==0xdf)   //按键3 减

                {

                    clock_shanshuo=0;

                    if(pos==0)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=2;

                        else dispbuf[pos]--;

                    }

                    else if(pos==1)

                    {

                        if(dispbuf[0]==0||dispbuf[0]==1)   

                        {

                            if(dispbuf[pos]==0)dispbuf[pos]=9;

                            else dispbuf[pos]--;

                        }

                        if(dispbuf[0]==2)  

                        {

                            if(dispbuf[pos]>=4&&dispbuf[pos]<=9)

                            {

                                dispbuf[pos]=3;

                            }

                            else if(dispbuf[pos]==0)

                            {

                                dispbuf[pos]=3;

                            }

                            else

                            {

                                dispbuf[pos]--;

                            }

                        }                  

                    }

                    else if(pos==2)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=5;

                        else dispbuf[pos]--;

                    }

                    else if(pos==3)

                    {

                        if(dispbuf[pos]==0)dispbuf[pos]=9;

                        else dispbuf[pos]--;       

                    }

                }

                else if(keyval==0xef)   //按键4     移位

                {

                    clock_shanshuo=0;

                    pos++;

                    pos=pos&03;

                }

                else if(keyval==0xf7)   //按键5     设置闹钟时间

                {

                    clock_shanshuo=1;

                    clock_hh=dispbuf[0]*10+dispbuf[1];   //保存所设闹钟时间

                    clock_mm=dispbuf[2]*10+dispbuf[3];

                    dispbuf[0]=hh/10;

                    dispbuf[1]=hh%10;

                    dispbuf[2]=mm/10;

                    dispbuf[3]=mm%10;

                }                                                                                                                                              

                break;

           

            case 4: //秒表模式

               

                if(keyval==0x7f)    //按键1     调整模式    进入时钟显示模式               

                {

                    mode=0;

                    TR1=0;     //关闭秒表

                    dispbuf[0]=mm/10;

                    dispbuf[1]=mm%10;

                    dispbuf[2]=ss/10;

                    dispbuf[3]=ss%10;

                }

                else if(keyval==0xef)    //按键4    开始、暂停

                {

                    old_ss=miaobiao_ss;

                    old_ms=miaobiao_ms;

                    TR1=~TR1;

                }

                else if(keyval==0xbf||keyval==0xdf)    //按键2或按键3  清零

                {

                    miaobiao_ss=0;

                    miaobiao_ms=0;

                    dispbuf[0]=miaobiao_ss/10;

                    dispbuf[1]=miaobiao_ss%10;

                    dispbuf[2]=miaobiao_ms/10;

                    dispbuf[3]=miaobiao_ms%10;

                }

                break;

           

            default:

                break;

    }

}

/******************延时程序******************/

void delay(unsigned int t)

{

    while(t--);

}

/******************中断程序,用于秒表******************/

void t1int(void) interrupt 3 using 3

{

    TH1=(0-2000)/256;

    TL1=(0-2000)%256;

    cnt2++;

    if(cnt2>=5)

    {

        cnt2=0;

        miaobiao_ms++;

        if(miaobiao_ms>=100)

        {

            miaobiao_ms=0;

            miaobiao_ss++;

            if(miaobiao_ss>=60)

            {

                miaobiao_ss=0;

            }

        }

        dispbuf[0]=miaobiao_ss/10;

        dispbuf[1]=miaobiao_ss%10;

        dispbuf[2]=miaobiao_ms/10;

        dispbuf[3]=miaobiao_ms%10;

    }

}


五、    学习体会

通过本次单片机课程设计,不仅加深了我对单片机理论课程知识的认识,而且通过将理论与实践相结合,使我真正地全面理解单片机的功能。

在刚开始编程时,没有一点思路,通过ftp上所提供的例程,慢慢理清了思路、基本了解了程序大致需要那几部分,基本确定编程思想。

在课程设计的整个过程中也遇到了很多问题,但本着遇到问题解决问题的原则,通过查找资料和询问老师、同学的办法,基本解决了所遇到问题。整个课程设计过程学到了不少通过理论学习没法学到的东西,真正增强了自己的能力。

    课程设计的三天时间,虽然辛苦但是收获巨大。

相关推荐