单片机数字时钟实验报告

数字时钟实验报告

一、 实验目的

    1、熟悉单片机的结构和各引脚的的功能以及如何用程序控制。

2、学习用单片机对数字时钟控制、按键扫描及LED数码管显示的设计方法。

              3、了解键盘的结构以及工作原理,通过单片机的定义实现对数码管时钟的调整。

二、实验要求

       1、可以正常准确的显示时间.

2、可以通过键盘输入来对时间进行调整.

3、能够以两种时钟表示方式显示时间.

4、自由发挥其他功能.

三、实验基本原理

      利用单片机定时器完成计时功能,定时器0计时中断程序每隔0.05s中断一次并当作一个计数,设定定时1秒的中断计数初值为0,每中断一次中断计数初值加1,当加到20时,则表示1s到了,秒变量加1,同理再判断是否1min钟到了,再判断是否1h到了。采用动态显示法实现LED显示,通过对每位数码管的依次扫描,使对应数码管亮,同时向该数码管送对应的字码,使其显示数字。由于数码管扫描周期很短,由于人眼的视觉暂留效应,使数码管看起来总是亮的,从而实现了各种显示。

           利用键盘实现对时钟的调整,定义四个按键,按下第一个按键位置跳变到“分”,在按定义的第二个键每按一次数字加一,当数字到59时再按一次,直接跳变到00;

       用第三个键控制“时”的12小时制还是24小时制,对键盘扫描,如果发现该键被按下,则表示为12进制,每按一次第四个按键数字加一,当到达12时,再按一次直接跳到1,如果没有发现该按键,则默认为24小时制,当数字是23时,再按一次跳变到00,再按一下第一个键退出对事件的调整。

四、实验设计分析

       针对要实现的功能,采用AT89S51单片机进行设计,AT89S51 单片机是一款低功耗,高性能CMOS8位单片机,片内含4KB在线可编程(ISP)的可反复擦写1000次的Flash只读程序存储器,器件采用高密度、非易失性存储技术制造,兼容标准MCS- 51指令系统及80C51引脚结构。这样,既能做到经济合理又能实现预期的功能。

在程序方面,采用分块设计的方法,这样既减小了编程难度、使程序易于理解,又能便于添加各项功能。程序可分为闹钟的声音程序、时间显示程序、日期显示程序,秒表显示程序,时间调整程序、闹钟调整程序、定时调整程序,延时程序等。运用这种方法,关键在于各模块的兼容和配合,若各模块不匹配会出现意想不到的错误。

首先,在编程之前必须了解硬件结构尤其是各引脚的用法,以及内部寄存器、存储单元的用法,否则,编程无从下手,电路也无法设计。这是前期准备工作。第二部分是硬件部分:依据想要的功能分块设计设计,比如输入需要开关电路,输出需要显示驱动电路和数码管电路等。第三部分是软件部分:先学习理解汇编语言的编程方法再根据设计的硬件电路进行分块的编程调试,最终完成程序设计。第四部分是软件画图部分:设计好电路后进行画图,包括电路图和仿真图的绘制。第五部分是软件仿真部分:软硬件设计好后将软件载入芯片中进行仿真,仿真无法完成时检查软件程序和硬件电路并进行修改直到仿真成功。第六部分是硬件实现部分:连接电路并导入程序检查电路,若与设计的完全一样一般能实现想要的功能。最后进行功能扩展,在已经正确的设计基础上,添加额外的功能!

 

              

  

 

菱形: 是否有调    转?

 

以下为具体的数字时钟实现程序段:

         #include<reg52.h>

#define uchar unsigned char

#define uint unsigned int

uchar sec,sec1,min,min1,hou,hou1,t,num,temp;

uint num1,num2,num3,n,k=0;

uchar code table[]={

0x3f,0x06,0x5b,0x4f,

0x66,0x6d,0x7d,0x07,

0x7f,0x6f,0x40};

void init();

uchar keyscan();

void control();

void display(uchar ,uchar ,uchar ,uchar ,uchar ,uchar ,uchar ,uchar );

void delay(uint z)

{

   uint x,y;

   for(x=z;x>0;x--)

            for(y=110;y>0;y--);

}

void main(){

   init();

   while(1){

         TR0=0;

         keyscan();

         control();

         TR0=1;

    

     if(t==20){

        t=0;

        num1++;

        if(num1==60){

           num1=0;

                num2++;

            if(num2==60){

                     num2=0;

                     num3++;

                if(num3>23)

                     num3=0;

             }

       }

    }   

         sec1=num1/10;

        sec=num1%10;

         min1=num2/10;

             min=num2%10;

         hou1=num3/10;

         hou=num3%10;

         display(hou1,hou,10,min1,min,10,sec1,sec);

        

   }

}

 void init(){

    t=0; num1=0,num2=0,num3=0;

    TMOD=0x01;

   TH0=(65536-50000)/256;

   TL0=(65536-50000)%256;

    EA=1;

   ET0=1;

    TR0=1;

 }

void time0() interrupt 1

{

          TH0=(65536-50000)/256;

   TL0=(65536-50000)%256;

   t++;

}

void display(uchar a,uchar b,uchar c,uchar d,uchar e,uchar f,uchar k,uchar h)

  {

                P0=table[a];

            P2=0xfe;

            delay(1);

  

            P0=table[b];           

            P2=0xfd;  

            delay(1);

            P0=table[c];

            P2=0xfb;

            delay(1);

            P0=table[d];

            P2=0xf7;

            delay(1);

            P1=table[e];

            P2=0xef;

            delay(1);

            P0=table[f];

            P2=0xdf;

            delay(1);

            P0=table[k];

            P2=0xbf;

            delay(1);

            P0=table[h];

            P2=0x7f;

            delay(1);

}

uchar keyscan(){

             

              P3=0xfe;

              temp=P3;

              temp=temp&0xf0;

              while(temp!=0xf0){

                         delay(5);

                         temp=P3;

                         temp=temp&0xf0;

                   while(temp!=0xf0){

                             temp=P3;

                      switch(temp){

                            case 0xee: num=1;

                                                 break;

                                          case 0xde: num=2;

                                                 break;

                                          case 0xbe: num=3;

                                                 break;

                                          case 0x7e: num=4;k++;

                                                 break;

                     }

                      while(temp!=0xf0){

                      temp=P3;

                      temp=temp&0xf0;

                     }

                 }

            }

        return  num;   

}

void control(){

    if(num==1){

         num1++;

         P2=0xfe;

         n=num1%10;

         if(num1>59)

         num1=0;

         P0=table[n];//调整秒            

         num=0;

      }

    if(num==2){

        num2++;

        P2=0xf7;

        n=num2%10;

        if(num2>59)

        num2=0;

        P0=table[n];//调整分

        num=0;

   }

   if(num==3){

        num3++;

        P2=0xbf;

        n=num3%10;

        if(num3>23)

        num3=0;

        P0=table[n];//调整时

        num=0;

     }

   if(num==4)

      {

     if(k%2==0)

          {

           if(num3>12)num3=num3-12;

          }

     else {

           if(num3<12)

             {

               num3=num3+12;

             }

          }

     num=0;

     }

 }

五、实验总结及感想

           刚接触单片机这个词语,感觉是那么的深奥,它的功能是那么的强大,能让看上去似乎毫不相关的东西联系起来,能让静止的事物“活动起来”。刚开始和想象中的一样难,不知如何下手,前面几个课时都是一直在看视频,但是感觉光看不练,有些东西总是记不住,关掉写好的代码连最关键的语句都写不出来。当时看到大家学得是那么的得心应手,我的确很着急。但是单片机用途很多,在以后会变得很重要,虽然难,但是还得硬着头皮一点一点的摸下去。

           刚开始几个课时的确没什么收获,之后我想了一下,决定还是重头开始,边看边写,虽然进度比较慢,但是那些该记的的东西还是记住了,这样那些要记的源程序关键字也不那么陌生了,慢慢的能写一句,慢慢的开始能编好一个小程序了,我开始有点成就感,开始有点喜欢上这程序。

           后来我写数字时钟,虽然看似那么平常的东西,但是具体把程序完整的写出来还是得花些时间,编程要注意的细节很多,一个很小的标点符号都可能导致致命的错误,有的一个很小的错误,也需要你找上几个小时,很浪费时间。在编写过程中也遇到了不少问题。

       1、P0口开关问题。P0口比较特殊,它存在高阻态,要使其输入不是高电平就是低电平,就要接上拉电阻,给其高电平输入。

       2、按键编译检测比较繁琐,在编程中,我按下键一次数码管上的数字会跳动好多下,和预期的有很大差别,使后面调时间带来了极大的问题,但是后面经过小组讨论查找,最终还是把这个问题解决了,这让我深刻的意识到,一个团队是多么的重要,一个人几天都无法完成的任务,一个团队或许只需要几分钟。

       我感觉最高兴的时候,不是出现最终的结果那一刻,而是在调试出错误后,大家找各自错误,相互讨教,寻找结果的过程,因为结果是早就预知的,不过迟早而已。

       我一直相信只要付出努力,就一定会有收获。坚持不懈才是成功的秘诀。

      

 

第二篇:单片机数字时钟实验报告

数字时钟实验报告

一、实验目的:

通过实验进一步深刻理解单片机最小系统的工作原理。着重掌握中断和定时器的使用,以及读键盘和LED显示程序的设计(具体设计在后面会涉及到)。培养动手能力。

二、实验内容:

使用单片机最小系统设计一个12小时制自动报时的数字时钟。

三、功能描述:

★使用低六位数码管显示时、分、秒、使用第七位表示上午和下午。符号A表示上午;符号P表示下午。

★通过按键分别调整小时位和分钟位。

★到达整点时以第八位数码管闪烁的方式报时,使用8作为显示内容。 ★考虑整点报时功能。

四、设计整体思路以及个别重点部分的具体实现方式:

下面这幅图展示主函数的流程

单片机数字时钟实验报告

main

作者:caorui - 1 -

下面描述的是调用T0中断时所进行的动作

T0中断

intT0()

单片机数字时钟实验报告

显示更新的函数具体见下面这幅图

作者:caorui - 2 -

我们还一个对键盘进行扫描以获得有效键盘值,其具体的实现见下面这幅图

作者:caorui - 3 -

单片机数字时钟实验报告

单片机数字时钟实验报告

作者:caorui - 4 -

单片机数字时钟实验报告

● 要实现时钟的运行和时间的调整,我的设计思路是这样的:由于T0中断的

时间间隔是4ms,那么我可以设置一个计数器i,在每次进入中断时进行加一调 整,当i计满面250时就将时钟我秒的低位加一。然后根据进位规则,对其后 的各位依次进行调整。

● 要实现整点报时功能,则可以根据时位是否为0判断是否要闪烁显示字符8。

至于闪烁的具体实现方式,见源程序。

至此,本实验的设计思路己基本介绍完毕。下面就是本次实验的源程序代码。 /*********************************************************/

/** 数字时钟程序 **/

作者:caorui - 5 -

/** **/

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

#include <absacc.h>

#include <reg51.h>

#define uchar unsigned char

#define uint8 unsigned char

#define uint16 unsigned int

#define LED1 XBYTE [0xA000] //数码管地址

#define LED2 XBYTE [0xA001]

#define LED3 XBYTE [0xA002]

#define LED4 XBYTE [0xA003]

#define LED5 XBYTE [0xA004]

#define LED6 XBYTE [0xA005]

#define LED7 XBYTE [0xA006]

#define LED8 XBYTE [0xA007]

#define KEY XBYTE [0xA100] //键盘地址

bit ap=0;//上下午

int i=0;//计数器

uchar data clock[7]={0,0,0,0,0,0,0};

/*扫描键盘使用的变量 */

sbit first_row = P1^4; //键盘第一行控制

sbit second_row = P1^3; //键盘第二行控制

bit first_getkey = 0,control_readkey = 0; //读键盘过程中的标志位

bit getkey = 0; //获得有效键值标志位 等于1时代表得到一个有效键值 bit keyon = 0; //防止按键冲突标志位

uchar keynum = 0; //获得的有效按键值寄存器

/*数码管显示使用的变量和常量*/

uchar lednum = 0; //数码管显示位控制寄存器

uchar led[8] = {0,0,0,0,0,0,0,0}; //数码管显示内容寄存器

uchar code segtab[18] 作者:caorui - 6 - =

{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x8c,0xff}; //七段码段码表

// "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "P" ,"black"

void leddisp(void); //数码管显示函数

void readkey(void); //读键盘函数

void intT0() interrupt 1 //T0 定时中断处理函数

{

TH0 = -2720/256; //定时器中断时间间隔 4ms

TL0 = -2720%256;

if((clock[2]==0)&&(clock[3]==0)&&(i==125)&&(clock[5]<=5)&&(clock[4]==0))

led[7]=17;

if((clock[2]==0)&&(clock[3]==0)&&(i==0)&&(clock[5]<=5)&&(clock[4]==0))

led[7]=8;

i=i+1;

if(i==250)

{

if((clock[2]==0)&&(clock[3]==0)&&(clock[4]==0)&&(clock[5]==0)&&

(clock[6]==0))

{

ap=!ap;

if(ap==0)

led[6]=10;

if(ap==1)

led[6]=16;}

clock[5]=clock[5]+1;i=0;}

if(clock[5]==10)

{clock[5]=0;

clock[4]=clock[4]+1;}

if(clock[4]==6)

{clock[4]=0;

clock[3]=clock[3]+1;}

if(clock[3]==10)

{clock[3]=0;

clock[2]=clock[2]+1;}

if(clock[2]==6)

{clock[2]=0;

clock[6]=clock[6]+1;}

if(clock[6]==12)

作者:caorui - 7 -

{clock[6]=0;}

clock[0]=clock[6]/10;

clock[1]=clock[6]%10;

led[5]=clock[0];

led[4]=clock[1];

led[3]=clock[2];

led[2]=clock[3];

led[1]=clock[4];

led[0]=clock[5];

leddisp(); //每次定时中断显示更新一次

if(control_readkey == 1) //每两次定时中断扫描一次键盘

{

readkey();

}

ontrol_readkey = !control_readkey;

}

void main(void)

{

TMOD = 0x01; //

TH0 = -2720/256; //定时器中断时间间隔 4ms

TL0 = -2720%256;

TCON = 0x10;

ET0 = 1;

EA = 1;

while(1)

{

if(getkey == 1) //判断是否获得有效按键

{

getkey = 0;

switch(keynum) //判断键值,对不同键值采取相应的用户定义处理方式 {

case 0x01: //当按下第一行第二列键时,分加一

clock[3]=clock[3]+1;

break;

case 0x02: ////当按下第一行的第三列键时,分减一

作者:caorui - 8 - c

clock[3]=clock[3]-1;

break;

case 0x03: //当按下第一行的第四列时,时加一

clock[6]=clock[6]+1;

break;

case 0x04:

clock[6]=clock[6]-1; //当按下第一行的第五列时,时减一

break;

default:

break;

}

}

}

}

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

键盘扫描函数

原型: void readkey(void);

功能: 当获得有效按键时,令getkey=1,keynum为按键值

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

void readkey(void)

{

uchar M_key = 0;

second_row = 0; M_key = KEY;

if(M_key != 0xff) //如果有连续两次按键按下,认为有有效按键按下。消除按键抖动 {

if(first_getkey == 0)

{

first_getkey = 1; } else //当有有效按键按下时,进一步识别是哪一个按键

{

if(keyon == 0) //防止按键冲突,当还有未释放的按键时不对其它按键动作响应 {

first_row = 0; //扫描第一行按键

second_row = 1;

M_key = KEY;

if(M_key != 0xff)

{

switch(M_key)

{

case 0xfe:

作者:caorui - 9 -

keynum = 0x00;

break;

case 0xfd:

keynum = 0x01;

break;

case 0xfb:

keynum = 0x02;

break;

case 0xf7:

keynum = 0x03;

break;

case 0xef:

keynum = 0x04;

break;

case 0xdf:

keynum = 0x05;

break;

case 0xbf:

keynum = 0x06;

break;

case 0x7f:

keynum = 0x07;

break;

}

}

else

{

second_row = 0; //扫描第二行按键 first_row = 1;

M_key = KEY;

switch(M_key)

{

case 0xfe:

keynum = 0x08;

break;

case 0xfd:

keynum = 0x09;

break;

case 0xfb:

keynum = 0x0a;

break;

case 0xf7:

keynum = 0x0b;

break;

作者:caorui - 10 -

case 0xef:

keynum = 0x0c;

break;

case 0xdf:

keynum = 0x0d;

break;

case 0xbf:

keynum = 0x0e;

break;

case 0x7f:

keynum = 0x0f;

break;

}

getkey = 1; //获得有效按键数值

keyon = 1; //防止按键冲突,当获得有效按键时将其置1

else

{

first_getkey = 0;

keyon = 0; //防止按键冲突,当所有的按键都释放时将其清0 }

}

/*************************************************** 数码管显示函数

原型: void leddisp(void);

功能: 每次调用轮流显示一位数码管

****************************************************/ void leddisp(void)

{

switch(lednum) //选择需要显示的数码位

{

case 0:

LED1 = segtab[led[0]];

break;

case 1:

LED2 = segtab[led[1]];

break;

case 2:

LED3 = segtab[led[2]];

break;

作者:caorui - 11 - } } }

case 3:

LED4 = segtab[led[3]];

break;

case 4:

LED5 = segtab[led[4]];

break;

case 5:

LED6 = segtab[led[5]];

break;

case 6:

LED7 = segtab[led[6]];

break;

case 7:

LED8 = segtab[led[7]];

break;

}

if(lednum == 0) //更新需要现实的数码位{

lednum = 7;

}

Else

{

lednum = lednum-1;

}

}

作者:caorui - 12 -

相关推荐