基于51定时器的时钟设计

[复制链接]
 楼主| 雷影少年 发表于 2012-7-23 11:13 | 显示全部楼层 |阅读模式
本帖最后由 雷影少年 于 2012-7-27 08:20 编辑

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



//////////////////////////////////////////////////////////////////////////////////////
程序写了几个小时,能够正常走时,就是调时的时候有的小问题,调试了N久实在没辙了请各位大神看看吧~
情况是这样,可以正常的走时,调时间的时候可以正常的加1,无抖动现象,但是在调位的时候,出现了点问题,
设定的是F键调整时钟状态,按1次调秒状态
         按2次调分状态
         按3次调时状态
         按4次正常走时
但是现在按下F键不能确定被调的是时、分、秒还是正常走时,换了几个键都是这样,用proteus仿真也是如此,可以判定是我程序的问题
发源码

  1. /***********************************************************
  2.      基于51定时器的时钟设计
  3.     利用LCD1602显示时间
  4.     具有调时、调分、调秒功能
  5.     F键调整时钟状态,按1次调秒状态
  6.          按2次调分状态
  7.          按3次调时状态
  8.          按4次正常走时
  9.     E键为add键,根据F的状态对应数值加1,
  10.     正常走时时E键无效
  11. ************************************************************/
  12. #include<reg51.h>
  13. typedef unsigned char u8;
  14. typedef unsigned char u16;
  15. #define ms_50 46080   //定义中断初始值
  16. u8 date[] ={"DATE 2012-07-22    "};    //装载日期初始值
  17. u8 time[] ={"TIME 23-59-50    "};    //装载时间初始值
  18. u8 count;
  19. u8 key_num;         //时钟状态,
  20. u8 key_add;           //E键值
  21. u8 hour =23;
  22. u8 min =59;
  23. u8 sec =50;
  24. sbit lcdrs =P1^0;
  25. sbit lcden =P1^1;
  26. //延时程序
  27. void delay(u8 x)        
  28. {
  29. u8 a,b;
  30. for (a =x;a>0;a--)
  31.   for (b =100;b>0;b--);
  32. }
  33. //向1602写命令
  34. void write_com(u8 com)      
  35. {
  36. lcdrs =0;
  37. P2 =com;
  38. delay(5);
  39. lcden =1;
  40. delay(5);
  41. lcden =0;
  42. }
  43. //向1602内写数据
  44. void write_date(u8 date)      
  45. {
  46. lcdrs =1;
  47. P2 =date;
  48. delay(5);
  49. lcden =1;
  50. delay(5);
  51. lcden =0;
  52. }
  53. //lcd初始化
  54. void init()        
  55. {
  56. lcden =0;
  57. write_com(0x38);
  58. write_com(0x0c);
  59. write_com(0x06);
  60. write_com(0x01);
  61. }
  62. //显示初始时间
  63. void display()      
  64. {
  65. u8 num;
  66. init();
  67. write_com(0x80);
  68. for (num =0;num<16;num++)
  69. {
  70.   write_date(date[num]);
  71.   delay(3);
  72. }
  73. write_com(0x80+0x40);
  74. for (num =0;num<16;num++)
  75. {
  76.   write_date(time[num]);
  77.   delay(3);
  78. }
  79. }
  80. //向1602内写入两位数(时间变化位)
  81. void timer_dis(x)        
  82. {
  83. write_com(0x80+0x40+x);
  84. write_date(time[x]);
  85. write_date(time[x+1]);
  86. }
  87. //键盘扫描程序
  88. void key_saomiao()
  89. {
  90. u8 key,temp;
  91. key_add =0;      //add清零
  92. /*****************扫描键盘**************/
  93. P3 =0xf0;
  94. temp =P3;
  95. if((temp&0xf0)!=0xf0)
  96. {
  97.   delay(100);
  98.   temp =P3;
  99.   if((temp&0xf0)!=0xf0)
  100.   {
  101.    P3 =0x0f;
  102.    temp =P3|temp;
  103.   }   
  104. }
  105. switch(temp)
  106. {
  107. /*  case 0xee: key =0;break;
  108.   case 0xed: key =1;break;
  109.   case 0xeb: key =2;break;
  110.   case 0xe7: key =3;break;
  111.   case 0xde: key =4;break;
  112.   case 0xdd: key =5;break;
  113.   case 0xdb: key =6;break;
  114.   case 0xd7: key =7;break;
  115.   case 0xbe: key =8;break;
  116.   case 0xbd: key =9;break;
  117.   case 0xbb: key =10;break;
  118.   case 0xb7: key =11;break;
  119.   case 0x7e: key =12;break;
  120.   case 0x7d: key =13;break;*/
  121.   case 0x7b: /*key =14;*/ key_add =1;break;     //若E键按下,key_add置1
  122.   case 0x77: key =15;break;
  123.   default: key =0;
  124. }
  125. if(15 ==key)     //判断F键按下
  126. {
  127.   while(key)     //等待按键松开
  128.   {
  129.    key_saomiao();
  130.   }
  131.   if(++key_num>=4 )   //key_num加1
  132.   {
  133.    key_num =0;
  134.   }
  135. }
  136. }
  137. //键盘处理程序
  138. void key_chuli()
  139. {
  140. key_saomiao();     //扫描键盘
  141. if(key_add)      //E键按下
  142. {
  143.   while(key_add)    //等待E键松开
  144.   {
  145.    key_saomiao();
  146.   }
  147.   switch(key_num)    //判断时钟状态
  148.   {
  149.    case 1:    //调秒
  150.     if(++sec>=60)
  151.     {
  152.      sec =0;
  153.     }
  154.     time[11] ='0'+sec/10;  //更新数组数据
  155.     time[12] ='0'+sec%10;
  156.     timer_dis(11);  //更新显示
  157.     break;
  158.    case 2:     //调分
  159.     if(++min>=60)
  160.     {
  161.      min =0;
  162.     }
  163.     time[8] ='0'+min/10;
  164.     time[9] ='0'+min%10;
  165.     timer_dis(8);
  166.     break;     
  167.    case 3:     //调时
  168.     if(++hour>=24)
  169.     {
  170.      hour =0;
  171.     }
  172.     time[5] ='0'+hour/10;
  173.     time[6] ='0'+hour%10;
  174.     timer_dis(5);
  175.     break;
  176.    default: ;break;
  177.   }
  178. }
  179. }
  180. //正常走时,更新时间
  181. void timer()      
  182. {
  183. /* static u8 hour =23;
  184. static u8 min =59;
  185. static u8 sec =50;*/
  186. if (++sec>=60)     //更新秒
  187. {
  188.   sec =0;
  189.   if (++min>=60)    //更新分
  190.   {
  191.    min =0;
  192.    if (++hour>=24) hour =0; //更新时
  193.    time[5] ='0'+hour/10;    //更新显示
  194.    time[6] ='0'+hour%10;
  195.    timer_dis(5);
  196.   }
  197.   time[8] ='0'+min/10;   //更新显示
  198.   time[9] ='0'+min%10;
  199.   timer_dis(8);
  200. }
  201. time[11] ='0'+sec/10;     //更新显示
  202. time[12] ='0'+sec%10;
  203. timer_dis(11);
  204. }
  205. /**********************************************************
  206.       主函数
  207. **********************************************************/  
  208. void main()
  209. {
  210. EA  =1;      //开中断
  211. ET0  =1;
  212. TMOD  =0x01;
  213. TH0  =-(ms_50/256);   //装载定时器初始值
  214. TL0  =-(ms_50%256);
  215. TR0  =1;
  216. display();     //显示初始时间
  217. while (1)
  218. {
  219.   key_saomiao();   //扫描键盘
  220.   while(key_num)   //判断时钟是否处于调时状态状态
  221.   {
  222.    key_chuli();  //调用键盘处理程序
  223.    count =0;
  224.   }
  225.   if (count>=20)   //正常走时,每秒更新一次时间
  226.   {
  227.    count =0;
  228.    timer();
  229.   }
  230. }
  231. }
  232. //中断服务程序
  233. void time50() interrupt 1       //每50ms中断一次
  234. {
  235. TH0 =-(ms_50/256);
  236. TL0 =-(ms_50%256);
  237. count++;
  238. }
下面是图

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
xjwbh 发表于 2012-7-23 11:33 | 显示全部楼层
自已检查,太复杂了,真不想看,
 楼主| 雷影少年 发表于 2012-7-23 11:44 | 显示全部楼层
:(我估计就是键盘处理程序的问题,但是看不出来是怎么错的
可能就是这几句代码的毛病
  1. if(15 ==key)     //判断F键按下

  2. {

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

  4.   {

  5.    key_saomiao();

  6.   }

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

  8.   {

  9.    key_num =0;

  10.   }

  11. }

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

循环调用按键扫描程序,若F键松开,key=0,自动跳出循环,
加这个循环是因为单片机的频率较高,在按键按下的过程中程序可能就运行了n遍了,为了避免这种情况,就扫描等待按键松开后再执行下面的程序
wukunshan 发表于 2012-7-24 12:03 | 显示全部楼层
按键松开,键值为0,那在什么情况才能保存新的键值?把你的按键扫描函数再贴出来看一看。总感觉你的按键扫描有问题。
 楼主| 雷影少年 发表于 2012-7-24 17:40 | 显示全部楼层
本帖最后由 雷影少年 于 2012-7-24 17:43 编辑


  1. //键盘扫描程序

  2. void key_saomiao()

  3. {

  4. u8 key,temp;

  5. key_add =0; //add清零

  6. /*****************扫描键盘**************/

  7. P3 =0xf0;

  8. temp =P3;

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

  10. {

  11. delay(100);

  12. temp =P3;

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

  14. {

  15. P3 =0x0f;

  16. temp =P3|temp;

  17. }

  18. }

  19. switch(temp)

  20. {

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

  22. case 0xed: key =1;break;

  23. case 0xeb: key =2;break;

  24. case 0xe7: key =3;break;

  25. case 0xde: key =4;break;

  26. case 0xdd: key =5;break;

  27. case 0xdb: key =6;break;

  28. case 0xd7: key =7;break;

  29. case 0xbe: key =8;break;

  30. case 0xbd: key =9;break;

  31. case 0xbb: key =10;break;

  32. case 0xb7: key =11;break;

  33. case 0x7e: key =12;break;

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

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

  36. case 0x77: key =15;break;

  37. default: key =0;

  38. }

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

  40. {

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

  42. {

  43. key_saomiao();

  44. }

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

  46. {

  47. key_num =0;

  48. }

  49. }

  50. }

反转法扫描的键盘,如果是扫描的问题为什么E键就正常,F键就有问题呢?
sedatefire 发表于 2012-7-24 17:54 | 显示全部楼层
按键是有抖动的
你要考虑连续几次稳定状态的情况下,才认为是真正的按键
模块混乱哦
分层也不清晰
sedatefire 发表于 2012-7-24 17:55 | 显示全部楼层
哎,道理谁都懂,实现起来又是老毛病
 楼主| 雷影少年 发表于 2012-7-24 19:05 | 显示全部楼层
if((temp&0xf0)!=0xf0)



{



delay(100);



temp =P3;

这里已经消除抖动了啊~
delay虽然毛病很多,但延时消抖还是没问题的,现在这个程序的问题是E键很正常,F键为什么会出现抖动现象?
651927693 发表于 2012-7-25 08:11 | 显示全部楼层
先不说你问的,15行中应该是unsigned int 吧
651927693 发表于 2012-7-25 08:18 | 显示全部楼层
27行下面的延时里的a的作用?
为什么不直接使用x?
这样效率会不会降低?
 楼主| 雷影少年 发表于 2012-7-25 09:05 | 显示全部楼层
先不说你问的,15行中应该是unsigned int 吧
651927693 发表于 2012-7-25 08:11

  • typedef unsigned char u8;
    前面定义了的
 楼主| 雷影少年 发表于 2012-7-25 09:07 | 显示全部楼层
27行下面的延时里的a的作用?
为什么不直接使用x?
这样效率会不会降低?
651927693 发表于 2012-7-25 08:18

这里倒是没注意~
谢谢啦~
sedatefire 发表于 2012-7-25 15:58 | 显示全部楼层
关于你另外一个贴的定时器不准问题,给你个提示
中断进来的时候,硬件的TH,TL其实还是在不停的往下计数
当程序跑到
TH = xx
TL = xx
的时候,
TH, TL的值并不是0
sedatefire 发表于 2012-7-25 15:59 | 显示全部楼层
计数器溢出,产生中断,压栈,跳转,这些都是需要时间的
这些就是定时器的误差
 楼主| 雷影少年 发表于 2012-7-25 19:11 | 显示全部楼层
计数器溢出,产生中断,压栈,跳转,这些都是需要时间的
这些就是定时器的误差
sedatefire 发表于 2012-7-25 15:59

嗯~
单片机定时器不够精确啊~
所以我的下一步就是利用DS1302做时钟,但是这个按键的究竟是错在哪啊?肯定是程序问题,我就是找不出来,麻烦各位大侠讲明白点,不要绕圈子,菜鸟表示亚历山大啊~
sedatefire 发表于 2012-7-25 21:22 | 显示全部楼层
我是想提示你一下误差产生的原因,让你自己想一下解决方法
sedatefire 发表于 2012-7-25 21:23 | 显示全部楼层
还是直接给你答案吧
用下面这种写法就可以避免误差
TCNT -= xx
原因你自己想想看
 楼主| 雷影少年 发表于 2012-7-26 08:28 | 显示全部楼层
TCNT51里没有啊~
百度了一下,没找到具体说明TCNT的,觉得好像是装载定时器初始值的。
我这个程序现在不是定时器的误差,而是出现了键盘抖动,是个错误。
程序又改了一下,再发现F键被按下后就初始化定时器,下面是修改后的代码
  1. /**************************************
  2.                         定时器T0初始化
  3. **************************************/
  4. void        init_T0()
  5. {
  6.         EA  =1;                                                //开中断
  7.         ET0  =1;
  8.         TMOD  =0x01;
  9.         TH0  =-(ms_50/256);                        //装载定时器初始值
  10.         TL0  =-(ms_50%256);
  11.         TR0  =1;
  12. }

  13. /**********************************************************
  14.                                                 主函数
  15. **********************************************************/               
  16. void main()
  17. {
  18.         init_T0();
  19.         display();                                        //显示初始时间

  20.         while (1)
  21.         {
  22.                 key_saomiao();                        //扫描键盘
  23.                 while(key_num)                        //判断时钟是否处于调时状态状态
  24.                 {
  25.                         key_chuli();                //调用键盘处理程序
  26.                         count =0;
  27.                                                 init_T0();
  28.                 }

  29.                 if (count>=20)                 //正常走时,每秒更新一次时间
  30.                 {
  31.                         count =0;
  32.                         timer();
  33.                 }
  34.         }
  35. }
这样可以减小定时器误差,但是F键的抖动依然存在
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

11

主题

714

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
个人签名:高明的设计不在于是否用了最新的技术,在于是否能用最小的成本做出可靠的东西来。

11

主题

714

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部