打印

基于51定时器的时钟设计

[复制链接]
3496|31
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 雷影少年 于 2012-7-27 08:20 编辑

问题解决,把源码和电路图传上来给飞得比我慢的鸟儿们点虫吃,欢迎各位DX批评
0722.rar (67.81 KB)


//////////////////////////////////////////////////////////////////////////////////////
程序写了几个小时,能够正常走时,就是调时的时候有的小问题,调试了N久实在没辙了请各位大神看看吧~
情况是这样,可以正常的走时,调时间的时候可以正常的加1,无抖动现象,但是在调位的时候,出现了点问题,
设定的是F键调整时钟状态,按1次调秒状态
         按2次调分状态
         按3次调时状态
         按4次正常走时
但是现在按下F键不能确定被调的是时、分、秒还是正常走时,换了几个键都是这样,用proteus仿真也是如此,可以判定是我程序的问题
发源码
 
/***********************************************************
     基于51定时器的时钟设计
    利用LCD1602显示时间
    具有调时、调分、调秒功能
    F键调整时钟状态,按1次调秒状态
         按2次调分状态
         按3次调时状态
         按4次正常走时
    E键为add键,根据F的状态对应数值加1,
    正常走时时E键无效
************************************************************/
#include<reg51.h>
typedef unsigned char u8;
typedef unsigned char u16;
#define ms_50 46080   //定义中断初始值
u8 date[] ={"DATE 2012-07-22    "};    //装载日期初始值
u8 time[] ={"TIME 23-59-50    "};    //装载时间初始值
u8 count;
u8 key_num;         //时钟状态,
u8 key_add;           //E键值
u8 hour =23;
u8 min =59;
u8 sec =50;
sbit lcdrs =P1^0;
sbit lcden =P1^1;
//延时程序
void delay(u8 x)        
{
u8 a,b;
for (a =x;a>0;a--)
  for (b =100;b>0;b--);
}
//向1602写命令
void write_com(u8 com)      
{
lcdrs =0;
P2 =com;
delay(5);
lcden =1;
delay(5);
lcden =0;
}
//向1602内写数据
void write_date(u8 date)      
{
lcdrs =1;
P2 =date;
delay(5);
lcden =1;
delay(5);
lcden =0;
}
//lcd初始化
void init()        
{
lcden =0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
}
//显示初始时间
void display()      
{
u8 num;
init();
write_com(0x80);
for (num =0;num<16;num++)
{
  write_date(date[num]);
  delay(3);
}
write_com(0x80+0x40);
for (num =0;num<16;num++)
{
  write_date(time[num]);
  delay(3);
}
}
//向1602内写入两位数(时间变化位)
void timer_dis(x)        
{
write_com(0x80+0x40+x);
write_date(time[x]);
write_date(time[x+1]);
}
//键盘扫描程序
void key_saomiao()
{
u8 key,temp;
key_add =0;      //add清零
/*****************扫描键盘**************/
P3 =0xf0;
temp =P3;
if((temp&0xf0)!=0xf0)
{
  delay(100);
  temp =P3;
  if((temp&0xf0)!=0xf0)
  {
   P3 =0x0f;
   temp =P3|temp;
  }   
}
switch(temp)
{
/*  case 0xee: key =0;break;
  case 0xed: key =1;break;
  case 0xeb: key =2;break;
  case 0xe7: key =3;break;
  case 0xde: key =4;break;
  case 0xdd: key =5;break;
  case 0xdb: key =6;break;
  case 0xd7: key =7;break;
  case 0xbe: key =8;break;
  case 0xbd: key =9;break;
  case 0xbb: key =10;break;
  case 0xb7: key =11;break;
  case 0x7e: key =12;break;
  case 0x7d: key =13;break;*/
  case 0x7b: /*key =14;*/ key_add =1;break;     //若E键按下,key_add置1
  case 0x77: key =15;break;
  default: key =0;
}
if(15 ==key)     //判断F键按下
{
  while(key)     //等待按键松开
  {
   key_saomiao();
  }
  if(++key_num>=4 )   //key_num加1
  {
   key_num =0;
  }
}
}
//键盘处理程序
void key_chuli()
{
key_saomiao();     //扫描键盘
if(key_add)      //E键按下
{
  while(key_add)    //等待E键松开
  {
   key_saomiao();
  }
  switch(key_num)    //判断时钟状态
  {
   case 1:    //调秒
    if(++sec>=60)
    {
     sec =0;
    }
    time[11] ='0'+sec/10;  //更新数组数据
    time[12] ='0'+sec%10;
    timer_dis(11);  //更新显示
    break;
   case 2:     //调分
    if(++min>=60)
    {
     min =0;
    }
    time[8] ='0'+min/10;
    time[9] ='0'+min%10;
    timer_dis(8);
    break;     
   case 3:     //调时
    if(++hour>=24)
    {
     hour =0;
    }
    time[5] ='0'+hour/10;
    time[6] ='0'+hour%10;
    timer_dis(5);
    break;
   default: ;break;
  }
}
}
//正常走时,更新时间
void timer()      
{
/* static u8 hour =23;
static u8 min =59;
static u8 sec =50;*/
if (++sec>=60)     //更新秒
{
  sec =0;
  if (++min>=60)    //更新分
  {
   min =0;
   if (++hour>=24) hour =0; //更新时
   time[5] ='0'+hour/10;    //更新显示
   time[6] ='0'+hour%10;
   timer_dis(5);
  }
  time[8] ='0'+min/10;   //更新显示
  time[9] ='0'+min%10;
  timer_dis(8);
}
time[11] ='0'+sec/10;     //更新显示
time[12] ='0'+sec%10;
timer_dis(11);
}
/**********************************************************
      主函数
**********************************************************/  
void main()
{
EA  =1;      //开中断
ET0  =1;
TMOD  =0x01;
TH0  =-(ms_50/256);   //装载定时器初始值
TL0  =-(ms_50%256);
TR0  =1;
display();     //显示初始时间
while (1)
{
  key_saomiao();   //扫描键盘
  while(key_num)   //判断时钟是否处于调时状态状态
  {
   key_chuli();  //调用键盘处理程序
   count =0;
  }
  if (count>=20)   //正常走时,每秒更新一次时间
  {
   count =0;
   timer();
  }
}
}
//中断服务程序
void time50() interrupt 1       //每50ms中断一次
{
TH0 =-(ms_50/256);
TL0 =-(ms_50%256);
count++;
}
下面是图

相关帖子

沙发
xjwbh| | 2012-7-23 11:33 | 只看该作者
自已检查,太复杂了,真不想看,

使用特权

评论回复
板凳
雷影少年|  楼主 | 2012-7-23 11:44 | 只看该作者
:(我估计就是键盘处理程序的问题,但是看不出来是怎么错的
可能就是这几句代码的毛病
if(15 ==key)     //判断F键按下

{

  while(key)     //等待按键松开

  {

   key_saomiao();

  }

  if(++key_num>=4 )   //key_num加1

  {

   key_num =0;

  }

}

}

使用特权

评论回复
地板
wukunshan| | 2012-7-23 17:14 | 只看该作者
当F键按下时程序进入了这个死循环:
while(key)     //等待按键松开
{
      key_saomiao();
  }
你怎么退出这个死循环,让程序执行其下的语句呢?
问题就出在这里。

使用特权

评论回复
5
雷影少年|  楼主 | 2012-7-23 20:24 | 只看该作者
当F键按下时程序进入了这个死循环:
while(key)     //等待按键松开
{
      key_saomiao();
  }
你怎么退出这个死循环,让程序执行其下的语句呢?
问题就出在这里。 ...
wukunshan 发表于 2012-7-23 17:14

循环调用按键扫描程序,若F键松开,key=0,自动跳出循环,
加这个循环是因为单片机的频率较高,在按键按下的过程中程序可能就运行了n遍了,为了避免这种情况,就扫描等待按键松开后再执行下面的程序

使用特权

评论回复
6
wukunshan| | 2012-7-24 12:03 | 只看该作者
按键松开,键值为0,那在什么情况才能保存新的键值?把你的按键扫描函数再贴出来看一看。总感觉你的按键扫描有问题。

使用特权

评论回复
7
雷影少年|  楼主 | 2012-7-24 17:40 | 只看该作者
本帖最后由 雷影少年 于 2012-7-24 17:43 编辑
 

//键盘扫描程序

void key_saomiao()

{

u8 key,temp;

key_add =0; //add清零

/*****************扫描键盘**************/

P3 =0xf0;

temp =P3;

if((temp&0xf0)!=0xf0)

{

delay(100);

temp =P3;

if((temp&0xf0)!=0xf0)

{

P3 =0x0f;

temp =P3|temp;

}

}

switch(temp)

{

/* case 0xee: key =0;break;

case 0xed: key =1;break;

case 0xeb: key =2;break;

case 0xe7: key =3;break;

case 0xde: key =4;break;

case 0xdd: key =5;break;

case 0xdb: key =6;break;

case 0xd7: key =7;break;

case 0xbe: key =8;break;

case 0xbd: key =9;break;

case 0xbb: key =10;break;

case 0xb7: key =11;break;

case 0x7e: key =12;break;

case 0x7d: key =13;break;*/

case 0x7b: /*key =14;*/ key_add =1;break; //若E键按下,key_add置1

case 0x77: key =15;break;

default: key =0;

}

if(15 ==key) //判断F键按下

{

while(key) //等待按键松开

{

key_saomiao();

}

if(++key_num>=4 ) //key_num加1

{

key_num =0;

}

}

}

反转法扫描的键盘,如果是扫描的问题为什么E键就正常,F键就有问题呢?

使用特权

评论回复
8
sedatefire| | 2012-7-24 17:54 | 只看该作者
按键是有抖动的
你要考虑连续几次稳定状态的情况下,才认为是真正的按键
模块混乱哦
分层也不清晰

使用特权

评论回复
9
sedatefire| | 2012-7-24 17:55 | 只看该作者
哎,道理谁都懂,实现起来又是老毛病

使用特权

评论回复
10
雷影少年|  楼主 | 2012-7-24 19:05 | 只看该作者
if((temp&0xf0)!=0xf0)



{



delay(100);



temp =P3;

这里已经消除抖动了啊~
delay虽然毛病很多,但延时消抖还是没问题的,现在这个程序的问题是E键很正常,F键为什么会出现抖动现象?

使用特权

评论回复
11
651927693| | 2012-7-25 08:11 | 只看该作者
先不说你问的,15行中应该是unsigned int 吧

使用特权

评论回复
12
651927693| | 2012-7-25 08:18 | 只看该作者
27行下面的延时里的a的作用?
为什么不直接使用x?
这样效率会不会降低?

使用特权

评论回复
13
雷影少年|  楼主 | 2012-7-25 09:05 | 只看该作者
先不说你问的,15行中应该是unsigned int 吧
651927693 发表于 2012-7-25 08:11

  • typedef unsigned char u8;
    前面定义了的

使用特权

评论回复
14
雷影少年|  楼主 | 2012-7-25 09:07 | 只看该作者
27行下面的延时里的a的作用?
为什么不直接使用x?
这样效率会不会降低?
651927693 发表于 2012-7-25 08:18

这里倒是没注意~
谢谢啦~

使用特权

评论回复
15
sedatefire| | 2012-7-25 15:58 | 只看该作者
关于你另外一个贴的定时器不准问题,给你个提示
中断进来的时候,硬件的TH,TL其实还是在不停的往下计数
当程序跑到
TH = xx
TL = xx
的时候,
TH, TL的值并不是0

使用特权

评论回复
16
sedatefire| | 2012-7-25 15:59 | 只看该作者
计数器溢出,产生中断,压栈,跳转,这些都是需要时间的
这些就是定时器的误差

使用特权

评论回复
17
雷影少年|  楼主 | 2012-7-25 19:11 | 只看该作者
计数器溢出,产生中断,压栈,跳转,这些都是需要时间的
这些就是定时器的误差
sedatefire 发表于 2012-7-25 15:59

嗯~
单片机定时器不够精确啊~
所以我的下一步就是利用DS1302做时钟,但是这个按键的究竟是错在哪啊?肯定是程序问题,我就是找不出来,麻烦各位大侠讲明白点,不要绕圈子,菜鸟表示亚历山大啊~

使用特权

评论回复
18
sedatefire| | 2012-7-25 21:22 | 只看该作者
我是想提示你一下误差产生的原因,让你自己想一下解决方法

使用特权

评论回复
19
sedatefire| | 2012-7-25 21:23 | 只看该作者
还是直接给你答案吧
用下面这种写法就可以避免误差
TCNT -= xx
原因你自己想想看

使用特权

评论回复
20
雷影少年|  楼主 | 2012-7-26 08:28 | 只看该作者
TCNT51里没有啊~
百度了一下,没找到具体说明TCNT的,觉得好像是装载定时器初始值的。
我这个程序现在不是定时器的误差,而是出现了键盘抖动,是个错误。
程序又改了一下,再发现F键被按下后就初始化定时器,下面是修改后的代码
/**************************************
                        定时器T0初始化
**************************************/
void        init_T0()
{
        EA  =1;                                                //开中断
        ET0  =1;
        TMOD  =0x01;
        TH0  =-(ms_50/256);                        //装载定时器初始值
        TL0  =-(ms_50%256);
        TR0  =1;
}

/**********************************************************
                                                主函数
**********************************************************/               
void main()
{
        init_T0();
        display();                                        //显示初始时间

        while (1)
        {
                key_saomiao();                        //扫描键盘
                while(key_num)                        //判断时钟是否处于调时状态状态
                {
                        key_chuli();                //调用键盘处理程序
                        count =0;
                                                init_T0();
                }

                if (count>=20)                 //正常走时,每秒更新一次时间
                {
                        count =0;
                        timer();
                }
        }
}
这样可以减小定时器误差,但是F键的抖动依然存在

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:高明的设计不在于是否用了最新的技术,在于是否能用最小的成本做出可靠的东西来。

11

主题

714

帖子

0

粉丝