找回密码
 立即注册

QQ登录

只需一步,快速开始

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

简明DS1302温度LED时钟制作详解(附单片机源码与PCB)

[复制链接]
跳转到指定楼层
楼主
回顾
这是楼主在高三时候做的一个项目,我和我的组员们共同完成了这个小钟从电路设计,焊接,再到编程调试的过程。我们将之前学习到的单片机和电路知识运用起来,虽然经历了不计其数的Bug和坎坷,可是就是在这样痛苦的过程中我们不知不觉中成长了许多,收获了更多的实战经验。
提出
一直以来,我多数在开发软件,偶尔碰碰Arduino实现点小功能,幸运的是我的高中有机器人实验室,于是我们就在龚鹏老师的带领下,系统的学习STC单片机开发和基础硬件知识。上个学期,课程已经基本学完,在7*5点阵的基础下,我们几个都想独立自主的完成一个有规模的项目,在大家的提议下,我们选择去做一个可以显示温度的LED时钟,而且显示要用LED才大气,于是我们开始了有序的准备工作。
实现
为了实现这个小钟的预期功能,我们听取了老师的意见。使用STC15W402AS芯片(宏晶科技生产)作为主控芯片,利用LED小灯组成的4个“8”作为显示部分,同时使用DS1302(Maxim生产)作为系统时钟芯片,使用DS18B20(Maxim生产)作为温度传感器芯片。电源使用MicroUSB+5V供电。电路图为自行设计,使用Protel绘制,再有工厂加工,随后按电路图焊接原件,最后进行编程调试。
图片展示

结构
使用STC15W402AS芯片(宏晶科技生产)作为主控芯片,利用LED小灯组成的4个“8”作为显示部分,同时使用DS1302(Maxim生产)作为系统时钟芯片,使用DS18B20(Maxim生产)作为温度传感器芯片。电源使用MicroUSB+5V供电。电路图为自行设计。
重要逻辑显示原理 - 视觉暂留扫描
由于我们使用了类似于放大版“数码管”的显示方式,限于单片机引脚数量限制,我们不能把每个8的每个部分都和单片机相连。于是我们采用了现在LED显示屏的显示原理--扫描点亮的原理。在我们的电路中,我们将4个“8”的8个组成部分(a,b,c,d,e,f,g)的正极和单片机P1端口(恰好8个端口)连接(也就是把每个“8”的a并连起来接到一个单片机引脚,把每个“8”的b并连起来接到另一个单片机引脚,以此类推),再将每个“8”的所有组成部分的所有负极并联,分别和单片机上P2端口上的4个引脚相连,组成显示控制电路。需要点亮第1个“8”时就先将这个“8”的负极引脚置低,再将需要点亮的部分引脚同时置高。例如下图所示想要显示“1”,就将b,c的引脚置高


这样问题就来了:现在我们能一个一个点亮了,如果我让这4个“8”一起亮怎么办?
这就需要视觉暂留原理了,大家都知道电影之所以能动起来就是因为我们人眼有0.1秒的视觉暂留。我们点亮小灯也如此,先点亮第一个,延迟5ms,再点亮第二个,延迟5ms,以此类推。5ms这样短的时间人根本看不出闪动感,于是完整的图像显示出来。
下面是时间送显的代码(chart数组里是预先设计好的自模):
  1. void convertShow(char hour_s1,hour_s2,min_s1,min_s2)
  2. {

  3.     E1 = 0;
  4.     P1 = chart[hour_s1];
  5.     delayMS(5);
  6.     P1 = allclear;
  7.     E1 = 1;

  8.     E2 = 0;
  9.     P1 = chart[hour_s2];
  10.     delayMS(5);
  11.     P1 = allclear;
  12.     E2 = 1;

  13.     E3 = 0;
  14.     P1 = chart[min_s1];
  15.     delayMS(5);
  16.     P1 = allclear;
  17.     E3 = 1;

  18.     E4 = 0;
  19.     P1 = chart[min_s2];
  20.     delayMS(5);
  21.     P1 = allclear;
  22.     E4 = 1;
  23.     if(sec_2%0x02==0x01)
  24.     {
  25.         MDLIGHT = 0;
  26.     }
  27.     else
  28.     MDLIGHT = 1;
复制代码

读取时间和温度
这个想起来很简单,无非是单片机一边显示,一边不断向时钟芯片或者温度芯片询问数据,至于芯片如何驱动,则是后面要讲的重头戏
时间修改/模式切换 - 按钮的使用
谁家的钟不能改时间啊,所以我们要支持时间修改功能,于是我们决定用传统的按钮来修改时间。使用三个按钮(称呼他们为Btn1,Btn2,Btn3)。Btn1负责加时间,短按是加1,长按递加,直到松开。Btn2和Btn1相反,而Btn3则是模式切换(一共三种:修改小时,修改分钟,显示温度)。
可是在实际编程时我们发现,按钮只要收到轻微扰动就响应,并不是按下才响应,这是为何?
原来通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖,如下图。

看来我们需要软件软件消抖(硬件消抖效果好可是我们的电路图已经印刷了啊)。软件消抖即检测出键闭合后执行一个延时程序,5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。程序如下:
  1. if (KEY_1==0 || KEY_2==0 || KEY_3==0)
  2.     {
  3.         delayMS(20);        //20毫秒软件防抖
  4.         if (KEY_1 == 0)
  5.         {
  6.             keyValue = 1;
  7.             while(KEY_1==0)
  8.             {
  9.                 modifyTime(status+8);
  10.                 delayShow(500);
  11.             }
  12.         }
  13.         if (KEY_2 == 0)
  14.         {
  15.             keyValue = 2;
  16.             while(KEY_2==0)
  17.             {
  18.                 modifyTime(status+16);
  19.                 delayShow(500);
  20.             }
  21.         }
  22.         if (KEY_3 == 0)
  23.         {
  24.             keyValue = 3;
  25.         }
  26.     }
  27. }
复制代码

下面是按钮修改的代码:
  1. /*************按钮扫描函数******************************/
  2. void keyScan()
  3. {
  4.     if (KEY_1==0 || KEY_2==0 || KEY_3==0)
  5.     {
  6.         delayMS(20);        //20毫秒软件防抖
  7.         if (KEY_1 == 0)
  8.         {
  9.             keyValue = 1;
  10.             while(KEY_1==0)
  11.             {
  12.                 modifyTime(status+8);
  13.                 delayShow(500);
  14.             }
  15.         }
  16.         if (KEY_2 == 0)
  17.         {
  18.             keyValue = 2;
  19.             while(KEY_2==0)
  20.             {
  21.                 modifyTime(status+16);
  22.                 delayShow(500);
  23.             }
  24.         }
  25.         if (KEY_3 == 0)
  26.         {
  27.             keyValue = 3;
  28.         }
  29.     }
  30. }

  31. /*************按钮响应处理函数******************************/
  32. void keyHandle()
  33. {
  34.     if(keyValue==1)
  35.     {
  36.         DS1302_Write(sec,min,hour,day,month,week,year);
  37.         keyValue = 0;
  38.     }
  39.     else if(keyValue==2)
  40.     {
  41.         DS1302_Write(sec,min,hour,day,month,week,year);
  42.         keyValue = 0;
  43.     }
  44.     else if(keyValue==3)
  45.     {
  46.         if (status==1)
  47.         {
  48.             status = 2;
  49.         }
  50.         else if(status==2)
  51.         {
  52.             status = 3;
  53.         }
  54.         else if(status==3)
  55.         {
  56.             status = 1;
  57.         }
  58.         keyValue = 0;

  59.     }
  60. }
复制代码

还有修改时间的一些诡异逻辑代码,涉及到一些标志变量,请仔细研读体会:
  1. /*************修改时间函数******************************/
  2. void modifyTime(int mode)  //mode指明修改小时或分钟   1:小时   2:分钟
  3. {

  4.     //修改小时(1)+递增(8)
  5.     if(mode == 9)
  6.     {
  7.         if(hour==0x23)
  8.         {
  9.             hour = 0x00;
  10.         }
  11.         else if(hour==0x09)
  12.         {
  13.             hour=0x10;
  14.         }
  15.         else if(hour==0x19)
  16.         {
  17.             hour=0x20;
  18.         }
  19.         else
  20.         {
  21.             hour=hour+0x01;
  22.         }

  23.         timeConvert();
  24.         convertShow(hour_1,hour_2,min_1,min_2);
  25.     }

  26.     //修改小时(1)+递减(16)
  27.     if(mode == 17)
  28.     {
  29.         if(hour==0x00)
  30.         {
  31.             hour = 0x23;
  32.         }
  33.         else if(hour==0x10)
  34.         {
  35.             hour = 0x09;
  36.         }
  37.         else if(hour==0x20)
  38.         {
  39.             hour = 0x19;
  40.         }
  41.         else
  42.         {
  43.             hour = hour-0x01;
  44.         }

  45.         timeConvert();
  46.         convertShow(hour_1,hour_2,min_1,min_2);
  47.     }

  48.     //修改分钟(2)+递增(8)
  49.     if(mode == 10)
  50.     {

  51.         if(min==0x59)
  52.         {
  53.             min=0x00;
  54.         }
  55.         else if(min-(min_1<<4)==0x09)
  56.         {
  57.             min=(min_1+0x01)<<4;
  58.         }
  59.         else
  60.         {
  61.             min=min+0x01;
  62.         }

  63.         timeConvert();
  64.         convertShow(hour_1,hour_2,min_1,min_2);
  65.     }

  66.     //修改分钟(2)+递减(16)
  67.     if(mode == 18)
  68.     {
  69.         if(min==0x00)
  70.         {
  71.             min=0x59;
  72.         }
  73.         else if(min_2==0x00)
  74.         {
  75.             min=min-0x10;
  76.             min=min+0x09;
  77.         }
  78.         else
  79.         {
  80.             min=min-0x01;
  81.         }

  82.         timeConvert();
  83.         convertShow(hour_1,hour_2,min_1,min_2);
  84.     }
  85. }
复制代码

整体逻辑
各部分的逻辑设计都完成了,我们现在开始设计整个程序的主函数部分。单片机上电后首先初始化(包括引脚置低什么的不再赘述)检测DS1302是否在运行(内置电池可以掉电走时),如果在运行就读取时间送显,不在运行就起振(开始走时)。然后在每个显示函数过后侦测按钮操作,如果发现有效按钮操作就进行处理按钮的逻辑,该修改时间就修改时间,该显示温度就显示温度。这个流程用死循环加以嵌套,就可以无止无休的运行下去(不断电的话)。下面是主函数代码:
  1. void main()
  2. {
  3.     init();

  4.     while(1)
  5.     {
  6.         if(status==3)
  7.         {
  8.             temp = DS18B20_ReadTemp();
  9.             tempHandle();
  10.             tempshow(temp_1,temp_2);
  11.         }
  12.         else
  13.         {
  14.             DS1302_readtime();
  15.             convertShow(hour_1,hour_2,min_1,min_2);
  16.         }
  17.         keyScan();
  18.         if(keyValue!=0&&KEY_1==1&&KEY_2==1&&KEY_3==1)
  19.         {
  20.             keyHandle();
  21.         }
  22.     }
  23. }
复制代码

下面是初始化函数:
  1. void init()
  2. {
  3.     //关闭所有小灯
  4.     E1=1;
  5.     E2=1;
  6.     E3=1;
  7.     E4=1;
  8.     MDLIGHT = 1;
  9.     P1 = allclear;
  10.     T_CE = 0;
  11.     T_SCLK = 0;

  12.     //默认初始化时间12:00
  13.     sec = 0x00;
  14.     min = 0x00;
  15.     hour = 0x16;
  16.     year = 0x01;
  17.     month = 0x01;
  18.     week = 0x01;
  19.     day = 0x01;
  20.     status = 1;
  21.     keyValue = 0;
  22.     delayMS(1000);
  23.     //DS1302初始化判断是否存在后备电源

  24.     if(DS1302_Read(0x81)&0x80==0x80)
  25.     {
  26.         DS1302_Write_one(0x8e,0x00);
  27.         DS1302_Write_one(0x80,sec);  //起振
  28.         DS1302_Write_one(0x8e,0x80);
  29.     }
  30.     else
  31.     {

  32.     }
  33. }
复制代码

下面附上DS1302类库,亲测好用:
  1. /*************写入一字节****************/
  2. void DS1302_Input_Byte(char Input)  //向时钟IC写入一字节
  3. {
  4.     char i;
  5.     T_SCLK = 0;
  6.     delay2us();
  7.     ACC =Input;
  8.     for(i=8; i>0; i--)
  9.     {
  10.         T_DIO = ACC_0;            //相当于汇编中的 RRC
  11.         delay2us();
  12.         T_SCLK = 1;
  13.         delay2us();
  14.         T_SCLK = 0;
  15.         ACC = ACC >> 1;
  16.     }
  17. }

  18. /*************读取一字节****************/
  19. char DS1302_Output_Byte(void)      //从时钟IC读取一字节()
  20. {
  21.     char i;
  22.     for(i=8; i>0; i--)
  23.     {
  24.         ACC>>=1;
  25.         T_DIO= 1;
  26.         delay2us();
  27.         ACC_7 = T_DIO;
  28.         T_SCLK = 1;                 //相当于汇编中的 RRC
  29.         delay2us();
  30.         T_SCLK = 0;
  31.         delay2us();
  32.     }
  33.     T_DIO = 0;
  34.     delay2us();
  35.     return(ACC);
  36. }

  37. /*************写一字节数据****************/
  38. void DS1302_Write_one( char addr,dat )       // 写入地址、数据子程序
  39. {
  40.     T_CE=0;                             //T_CE引脚为低,数据传送中止
  41.     T_SCLK=0;                          //清零时钟总线
  42.     T_CE = 1;                          //T_CE引脚为高,逻辑控制有效
  43.     DS1302_Input_Byte(addr);           // 地址,命令
  44.     DS1302_Input_Byte(dat);          // 写1Byte数据
  45.     T_SCLK = 1;
  46.     T_CE = 0;
  47. }

  48. /*************读一字节数据****************/
  49. char DS1302_Read ( char addr )    //数据读取子程序
  50. {
  51.     char date;
  52.     T_CE=0;
  53.     T_SCLK=0;
  54.     T_CE = 1;
  55.     DS1302_Input_Byte(addr);        // 地址,命令
  56.     date = DS1302_Output_Byte();         // 读1Byte数据
  57.     T_SCLK = 1;
  58.     T_CE = 0;
  59.     return(date);
  60. }


  61. /*************写入时间数据****************/
  62. void DS1302_Write(char sec_w,min_w,hour_w,day_w,month_w,week_w,year_w)
  63. {
  64.     DS1302_Write_one(0x8e,0x00);
  65.     DS1302_Write_one(0x82,min_w);
  66.     DS1302_Write_one(0x84,hour_w);
  67.     DS1302_Write_one(0x86,day_w);
  68.     DS1302_Write_one(0x88,month_w);
  69.     DS1302_Write_one(0x8a,week_w);
  70.     DS1302_Write_one(0x8c,year_w);
  71.     DS1302_Write_one(0x80,sec_w);
  72.     DS1302_Write_one(0x8e,0x80);

  73. }

  74. /*************时间转换为显示格式****************/
  75. void timeConvert()
  76. {
  77.     sec_1 = sec>>4;
  78.     sec_2 = sec&0x0f;
  79.     min_1 = min>>4;
  80.     min_2 = min&0x0f;
  81.     hour_1 = hour>>4;
  82.     hour_2 = hour&0x0f;

  83. }

  84. /*************从芯片读取时间****************/
  85. void DS1302_readtime()
  86. {
  87.     sec=DS1302_Read(0x81);                    //读秒
  88.     min=DS1302_Read(0x83);                    //读分
  89.     hour=DS1302_Read(0x85);                   //读时
  90.     day=DS1302_Read(0x87);                    //读日
  91.     month=DS1302_Read(0x89);                  //读月
  92.     year=DS1302_Read(0x8d);                   //读年
  93.     week=DS1302_Read(0x8b);                   //读星期
  94.     timeConvert();

  95. }
复制代码

DIY教程Windows
1.克隆或下载本仓库,PCB电路图在仓库内,印刷后按照引脚焊接元件。
2.下载宏晶科技官网所提供的相关烧录软件与教程,并安装。
3.下载KeilC51开发工具,并安装。
4.下载TKStudio,并安装(可选)。
5.使用USB转TTl烧录器连接板载串口与计算机(如果需要驱动请自行安装)
6.使用STC烧录软件,选择STC15W402AS型号,使用默认配置(单片机震荡频率11.0592MHz),载入想要烧录的二进制文件,上电烧录。
7.如果需要增加修改逻辑代码,可以使用TKStudio打开main-code/Clock.xmp进行二次开发,其他IDE也可。
最后附上代码以及PCB:



全部资料51hei下载地址:
ds1302-stc15-clock-master.zip (184.78 KB, 下载次数: 73)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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