找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3410|回复: 7
打印 上一主题 下一主题
收起左侧

51单片机+ds1302电子钟能正常走时,按键有问题

[复制链接]
跳转到指定楼层
楼主
ID:67519 发表于 2015-1-14 01:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
时钟目前能正常走时,大概1天快12秒
  鄙人愚昧,部分问题未能解决,
  还望各位高手出手相救,出谋划策
【按键扫描问题】:
调时间时:1,调小时分钟  加键不是按一次加一,是乱加,
                   小时能调到24时,退出设置显示0时
               2,调小时分钟  减键不是按一次减一,是乱减
[size=13.63636302948px]               3,调秒时,加键减键乱加减,秒能调到8*,9*秒
[size=13.63636302948px]               4,退出设置秒清零,设置的秒未能保存,时和分能保存
[size=13.63636302948px]               5,时间能调到24时,退出设置显示0时
[size=13.63636302948px]                    当有按键按下时数码管全灭不显示,按键松开数码管显示
[size=13.63636302948px]               6,启动定时器中断,能轻微看见数码管闪烁
[size=13.63636302948px]             //7,闹钟有问题,麻烦增加闹钟函数,
[size=13.63636302948px]             // 闹钟开指示灯亮,闹钟关指示灯灭
[size=13.63636302948px]              8,如何提高走时精度[size=13.63636302948px]      这里是源工程文件 加按键测试01.zip (33.2 KB, 下载次数: 22)
[size=13.63636302948px]源程序:
  1. /************************

  2. ds1302数码管显示时间

  3. ************************/

  4. /*****头文件*****/
  5. #include<reg52.h>
  6. #include<intrins.h>
  7. #define uchar unsigned char
  8. #define uint unsigned int

  9. /*****DS1302引脚定义*****/
  10. sbit rst = P0^7;    /*实时时钟复位线引脚*///(所有的数据传输在rst置1时进行,rst为0时终止传输)
  11. sbit io = P0^6;     /*实时时钟数据线引脚*/
  12. sbit clk = P0^5;    /*实时时钟时钟线引脚*/

  13. sbit  ACC0 = ACC^0; /*累加寄存器的第0位*/
  14. sbit  ACC7 = ACC^7; /*累加寄存器的第7位*/

  15. sbit  LED1 = P3^0;  /*未定义(高电平亮)(3mm红发红光)*/
  16. sbit  LED2 = P3^1;  /*闹钟指示灯(低电平亮)(亮:闹钟开;灭:闹钟关)(alarm clock)(3mm白发绿光)*/
  17. sbit  tele = P3^2;  /*红外遥控数据线引脚(telecontrol)*/
  18. sbit  motor = P3^3; /*振子,马达,震动(低电平转)(vibration,motor)*/
  19. sbit  beep = P3^4;  /*蜂鸣器(低电平响)*/

  20. /*****按键定义*****/
  21. sbit key1 = P3^7; /*设置键*/
  22. sbit key2 = P3^6; /*加键*/
  23. sbit key3 = P3^5; /*减键*/

  24. uchar keynum; /*按键计数*/
  25.         
  26. /*****数码管数组*****/
  27. uchar code smg_du[]=                 /*共阳数码管段码表*//*P2引脚*/
  28. {0xc0,0xf9,0xa4,0xb0,0x99,          /*0-9*/
  29. 0x92,0x82,0xf8,0x80,0x90,0xbf};
  30. uchar code smg_we[]=        /*数码管位码*//*P1引脚*/
  31. {0xC1,0xC2,0xC4,             /*从左到右*/
  32. 0xC8,0xD0,0xE0};        /*位置1,2,3,4,5,6*/

  33. /*****时间初值*****/
  34. uchar table_shijian[7]={15,7,12,30,23,59,55}; /*年周月日时分秒*/

  35. uchar write_add[7]={0x8c,0x8a,0x88,0x86,0x84,0x82,0x80}; /*定义写寄存器(年周月日时分秒)*/
  36. uchar read_add[7]={0x8d,0x8b,0x89,0x87,0x85,0x83,0x81};         /*定义读寄存器(年周月日时分秒)*/

  37. uchar disp[6]; //存放转换成十进制后并分离出个位十位的数/*6个显示元素(6个数码管)*/
  38.          
  39. /******函数声明*****/
  40. void write_ds1302byte(uchar com);  /*写一个字节*/
  41. void write_ds1302(uchar com,uchar dat);        /*写一个字节命令和一个字节数据*/
  42. uchar read_ds1302(uchar com); /*读一字节数据*/
  43. void ds1302_init(void);        /*初始化ds1302*/
  44. void read_rtc(void);    /*读取时间*/
  45. void display(void);            /*显示处理函数*/
  46. void display_shijian(void);        /*显示函数*/
  47. void delay(uchar z); /*延时*/
  48. void tiaoshi(void);         /*按键扫描*/
  49. void Timer0Init();   /*定时器初始化*/
  50. void Timer0zd();          /*定时器中断*/

  51. /************************************************************************/

  52. /************主函数***************/
  53. void main(void)
  54. {
  55.         delay(200);           //等待系统稳定
  56.         LED1=0;                   //关闭未定义LED (P3.0)
  57. //    Timer0Init();  //初始化定时器0         
  58.     ds1302_init(); //初始化ds1302/*一次即可*/
  59.         while(1)                         /*大循环*/
  60.         {
  61.           tiaoshi();                 /*调用按键扫描函数*/
  62.           display();             /*调用显示处理函数*/
  63.           display_shijian(); /*调用显示时间函数*/         
  64.           if(keynum==0)                 /*如果按键没有动作则执行以下指令*/
  65.           {
  66.                 read_rtc();             /*调用读时间函数*/
  67.           }
  68.         }  
  69. }

  70. /*****定时器*****/
  71. //void Timer0Init()         //20毫秒@11.0592MHz
  72. //{
  73. //        TMOD |= 0x01; //定时器0模式1定时器时钟12T模式
  74. //        TL0 = 0x00;         //设置定时初值
  75. //        TH0 = 0xb8;         //设置定时初值
  76. //        TF0 = 0;         //清除TF0标志
  77. //        TR0 = 1;           //开定时器0
  78. //        ET0 = 1;         //开定时器中断
  79. //        EA = 1;                 //开总中断
  80. //}
  81. ////
  82. //void Timer0zd() interrupt 1
  83. //{        
  84. //        TL0 = 0x00;         //设置定时初值
  85. //        TH0 = 0xb8;         //设置定时初值
  86. //
  87. //           display();
  88. ////display_shijian(); /*调用显示时间函数*/
  89. ////        read_rtc();           /*调用读时间函数*/
  90. //  if(keynum==0)                 /*如果按键没有动作则执行以下指令*/
  91. //          {
  92. //                read_rtc();             /*调用读时间函数*/
  93. //          }
  94. //}

  95. /*****延时函数*****/
  96. void delay(uchar z)          /*延时≈1毫秒*/
  97. {
  98.   uint x,y;
  99.   for(x=z;x>0;x--)
  100.   for(y=115;y>0;y--);
  101. }

  102. /*****按键扫描模块*****/
  103. void tiaoshi(void)
  104. {
  105.   if(key1==0)            //如果设置键按下
  106.   {
  107.         delay(10);            //延时消抖
  108.         if(key1==0)            //判断,防误操作
  109.         {
  110.           while(!key1); //松手检测
  111.           write_ds1302(0x8e,0x00); //关闭写保护(可以写操作)
  112.           write_ds1302(0x80,0x80); //晶振停振时钟停止秒停
  113.       keynum++;                           //设置键按键计数自动加1*/
  114.          switch(keynum)                  //设置键按键次数统计*/
  115.          {
  116.            case 1:P2=smg_du[disp[1]];P1=smg_we[1];delay(100);break; /*按1次*//*调小时*//*数码管第二位闪烁*//*时个位*/
  117.            case 2:P2=smg_du[disp[3]];P1=smg_we[3];delay(100);break; /*按2次*//*调分钟*//*数码管第四位闪烁*//*分个位*/
  118.            case 3:P2=smg_du[disp[5]];P1=smg_we[5];delay(100);break; /*按3次*//*调秒钟*//*数码管第六位闪烁*//*秒个位*/
  119.            case 4:keynum=0;write_ds1302(0x80,0x00);write_ds1302(0x8e,0x80);break;         /*按4次*//*退出设置*//*启动晶振开始走时*//*开写保护*/
  120.            default:break;
  121.          }                                                                                                        
  122.         }                  
  123.   }
  124.   if(keynum!=0)                  //如果keynum不是0说明设置键有动作
  125.   {                                          //则执行以下指令
  126.     if(key2==0)                  //如果加键被按下
  127.     {                                  //则执行以下指令
  128.           delay(10);          //延时消抖
  129.            while(!key2)   //松手检测
  130.            switch(keynum) /*设置键按键计数*/
  131.            {
  132.             case 1:                  /*加键按一次*//*时加1*/
  133.              table_shijian[4]=read_ds1302(read_add[4]); //读出当前的小时               
  134.              table_shijian[4]=((((table_shijian[4]&0xf0)>>4)*10)+(table_shijian[4]&0x0f)); //将当前的时转换成十进制数
  135.          table_shijian[4]++;         
  136.          if(table_shijian[4]==24) table_shijian[4]=0;
  137.          table_shijian[4]=(((table_shijian[4]/10)<<4)|table_shijian[4]%10); //将时部分十进制数转换为十六进制数
  138.              write_ds1302(write_add[4],table_shijian[4]);break; //写入ds1302时位置
  139.                
  140.                 case 2:                  /*加键按一次*//*分加1*/
  141.              table_shijian[5]=read_ds1302(read_add[5]);                   //读出当前的分钟
  142.          table_shijian[5]=((((table_shijian[5]&0xf0)>>4)*10)+(table_shijian[5]&0x0f)); //将当前的分钟转换成十进制数
  143.          table_shijian[5]++;         
  144.          if(table_shijian[5]==60) table_shijian[5]=0;
  145.          table_shijian[5]=(((table_shijian[5]/10)<<4)|table_shijian[5]%10); //将分钟部分十进制数转换为十六进制数
  146.          write_ds1302(write_add[5],table_shijian[5]);break; //写入ds1302分位置
  147.                
  148.                 case 3:                  /*加键按一次*//*秒加1*/
  149.              table_shijian[6]=read_ds1302(read_add[6]);  //读出当前的秒
  150.          table_shijian[6]=((((table_shijian[6]&0xf0)>>4)*10)+(table_shijian[6]&0x0f)); //将当前的秒转换成十进制数
  151.          table_shijian[6]++;         
  152.          if(table_shijian[6]==60) table_shijian[6]=0;
  153.          table_shijian[6]=(((table_shijian[6]/10)<<4)|table_shijian[6]%10); //将秒部分十进制数转换为十六进制数
  154.          write_ds1302(write_add[6],table_shijian[6]);break; //写入ds1302秒位置
  155.            }               
  156.     }                 
  157.   }
  158.   if(keynum!=0)                //如果keynum不是0说明设置键有动作
  159.   {                                         //则执行以下指令
  160.     if(key3==0)     //如果减键被按下
  161.     {                                //则执行以下指令
  162.           delay(10);    //延时消抖
  163.           while(!key3)  //松手检测
  164.          switch(keynum) /*设置键按键计数*/
  165.          {
  166.           case 1:                /*减键按一次*//*时减1*/
  167.            table_shijian[4]=read_ds1302(read_add[4]); //读出当前的时
  168.        table_shijian[4]=((((table_shijian[4]&0xf0)>>4)*10)+(table_shijian[4]&0x0f)); //将当前的时转换成十进制数
  169.        table_shijian[4]--;         
  170.        if(table_shijian[4]==-1) table_shijian[4]=23;
  171.        table_shijian[4]=(((table_shijian[4]/10)<<4)|table_shijian[4]%10);   //将时部分十进制数转换为十六进制数
  172.        write_ds1302(write_add[4],table_shijian[4]);break; //写入ds1302时位置
  173.          
  174.           case 2:                /*减键按一次*//*分减1*/
  175.            table_shijian[5]=read_ds1302(read_add[5]); //读出当前的分
  176.        table_shijian[5]=((((table_shijian[4]&0xf0)>>4)*10)+(table_shijian[5]&0x0f)); //将当前的分转换成十进制数
  177.        table_shijian[5]--;         
  178.        if(table_shijian[5]==-1) table_shijian[5]=59;
  179.        table_shijian[5]=(((table_shijian[5]/10)<<4)|table_shijian[5]%10);    //将分部分十进制数转换为十六进制数
  180.        write_ds1302(write_add[5],table_shijian[5]);break; //写入ds1302分位置
  181.          
  182.           case 3:                /*减键按一次*//*秒减1*/
  183.            table_shijian[6]=read_ds1302(read_add[6]); //读出当前的秒
  184.        table_shijian[6]=((((table_shijian[6]&0xf0)>>4)*10)+(table_shijian[6]&0x0f)); //将当前的秒转换成十进制数
  185.        table_shijian[6]--;         
  186.        if(table_shijian[6]==-1) table_shijian[6]=59;
  187.        table_shijian[6]=(((table_shijian[6]/10)<<4)|table_shijian[6]%10);    //将秒部分十进制数转换为十六进制数
  188.        write_ds1302(write_add[6],table_shijian[6]);break; //写入ds1302秒位置
  189.          }                        
  190.     }                 
  191.   }
  192. }  

  193. /*****ds1302模块*****/
  194. /*****写一个字节*****/
  195. void write_ds1302byte(uchar com)   
  196. {
  197.     uchar i;                 //定义一个变量i
  198.         for(i=0;i<8;i++) //连续写8个二进制位数据
  199.         {
  200.           clk=0;                 //拉低时钟线,为脉冲上升沿写入数据做好准备
  201.           io=com&0x01;         //取出com的第0位数据写入1302//(数据和0x01与)每次传输低字节
  202.           com=com>>1;         //将com的数据位右移1位
  203.           clk=1;                 //时钟线上升沿写入数据
  204.     }
  205. }

  206. /*****写一个字节命令和一个字节数据*****/
  207. void write_ds1302(uchar com,uchar dat)   
  208. {
  209. rst=0; _nop_(); //禁止数据传输//空操作(相当于短暂延时)
  210. clk=0; _nop_(); //确保写数据前时钟线被拉低//空操作
  211. rst=1; _nop_(); //启动数据传输//空操作
  212. write_ds1302byte(com);         //写命令地址
  213. write_ds1302byte(dat);         //写数据
  214. rst=0; _nop_(); //禁止数据传递(操作完赶紧拉低防止误操作)//空操作
  215. io=0; _nop_();         //释放io
  216. clk=1; _nop_(); //将时钟线电平置于已知状态
  217. }

  218. /*****读一个字节命令读一个字节数据*****/
  219. uchar read_ds1302(uchar com)
  220. {
  221. uchar i,value;
  222. clk=1; _nop_();  //确保写数据前时钟线被拉高//空操作
  223. io=1; _nop_();          //数据线拉高
  224. rst=1; _nop_();  //启动数据传输//空操作
  225. write_ds1302byte(com);        //写入命令字后/进行读操作之前要进行写命令(地址),以确定是读那个
  226. for(i=0;i<8;i++) //连续读8个二进制位数据
  227. {
  228.   clk=0;                  //拉低时钟线,形成脉冲下降沿读数据
  229.   value=value>>1; //将value的各数据位右移1位,先读出的是字节的最低位
  230.   if(io)                  //如果读出的数据是1
  231.    value=value|0x80; //将1取出,写在value的最高位
  232.   clk=1;                  //将clk拉高,为下次下降沿读出数据做好准备
  233. }
  234. rst=0;                          //禁止数据传输
  235. return(value);          //将读出的数据返回
  236. }

  237. /*****初始化DS1302*****/
  238. void ds1302_init(void)         
  239. {
  240.         uchar i,a,b;
  241. //        uchar CH;
  242.         write_ds1302(0x8e,0x00); //关闭写保护(可以写操作)
  243. //        write_ds1302(0x80,0x80); //装初值前晶振停止,秒停
  244.         for(i=0;i<7;i++)
  245.         {
  246.       a=table_shijian[i]%10;   //个位
  247.       b=table_shijian[i]/10;   //十位
  248.       table_shijian[i]=a+b*16; //转换为十六进制
  249.         }            
  250. // CH=read_ds1302(0x81); //先对ds1302进行读回秒寄存器,
  251. //                                                
  252. // if(CH&0x80)                        //判断秒寄存器最高位(CH)是否为1
  253. // {                                        //如为1,说明晶振停止工作,ds1302处于低功耗待命状态,对它写入初值
  254. //                                            //如为0,说明晶振开始振动,ds1302处于工作状态,(说明在断电时还起用了备用电池,所以ds1302还处于工作状态)
  255. //                                            //如果每次上电要求都写入初值,就无需这步操作,
  256. // for(i=0;i<7;i++)            //写入数据(年周月日时分秒)
  257. // {
  258. //   write_ds1302(write_add[i],table_shijian[i]); //写入命令,写数据        
  259. // }
  260. // }
  261.         for(i=0;i<7;i++)         //连续写7个数据(年周月日时分秒)
  262.     {
  263.       write_ds1302(write_add[i],table_shijian[i]); //写入命令,写数据//对号入座        
  264.     }


  265. //        write_ds1302(0x80,0x00); //启动晶振开始走时,秒走
  266.         write_ds1302(0x90,0xa5); //使能充电,1个二极管,2K电阻
  267.         write_ds1302(0x8e,0x80); //打开写保护(禁止写操作)
  268. }

  269. //*****读取时间*****/
  270. void read_rtc(void)         
  271. {
  272. uchar i;
  273. for(i=0;i<7;i++)         //连续读7个数据(年周月日时分秒)
  274. {
  275.   table_shijian[i]=read_ds1302(read_add[i]); //数据对应寄存器的地址
  276. }
  277. }

  278. /************显示处理**************/
  279. void display(void)
  280. {
  281.   disp[0]=table_shijian[4]/16; /*时的十位*/
  282.   disp[1]=table_shijian[4]%16; /*时的个位*/         
  283.   /*disp[2]=10;*/                                                   
  284.   disp[2]=table_shijian[5]/16; /*分的十位*/
  285.   disp[3]=table_shijian[5]%16; /*分的个位*/         
  286.   /*disp[5]=10; */                                            
  287.   disp[4]=table_shijian[6]/16; /*秒的十位*/
  288.   disp[5]=table_shijian[6]%16; /*秒的个位*/                  
  289. }                                                                                 
  290. /*****显示时间*****/
  291. void display_shijian(void)      
  292. {
  293.   uchar i;
  294.   for(i=0;i<6;i++)          /*连续写入6位,时时分分秒秒*/
  295.   {
  296.         P1=smg_we[i];       /*数码管位选*/
  297.         P2=smg_du[disp[i]]; /*数码管段选*/
  298.         delay(2); /*延时*/
  299.         P2=0xff;  /*消隐*/
  300. //        delay(2); /*延时*///这个延时影响数码管闪烁
  301.   }
  302. }
复制代码





分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:1 发表于 2015-1-14 09:39 来自手机 | 只看该作者
极有可能是精振频率不准1天快12秒,可以尝试采用高精度的,,晶体震荡器。或者,在程序中修正误差
回复

使用道具 举报

板凳
ID:67519 发表于 2015-1-14 23:32 | 只看该作者
admin 发表于 2015-1-14 09:39
极有可能是精振频率不准1天快12秒,可以尝试采用高精度的,,晶体震荡器。或者,在程序中修正误差

晶振更换正在测试中,还有按键扫描函数如何修改,望指点...
回复

使用道具 举报

地板
ID:72243 发表于 2015-1-15 00:27 | 只看该作者
我的晶振也不准,一天慢7秒左右

评分

参与人数 1黑币 +30 收起 理由
admin + 30 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

5#
ID:86421 发表于 2015-10-12 21:05 | 只看该作者
晶振不对的话就乱加
回复

使用道具 举报

6#
ID:92285 发表于 2015-10-12 22:13 | 只看该作者
晶振应该有问题
回复

使用道具 举报

7#
ID:92319 发表于 2015-10-13 11:48 | 只看该作者
极有可能是精振频率不准1天快12秒,可以尝试采用高精度的,,晶体震荡器。或者,在程序中修正误差等等
回复

使用道具 举报

8#
ID:92363 发表于 2015-10-13 19:55 | 只看该作者
晶振应该有问题
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表