找回密码
 立即注册

QQ登录

只需一步,快速开始

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

基于DS1302的日历时钟仿真

[复制链接]
跳转到指定楼层
楼主
仿真原理图:


单片机代码:
  1. #include<reg51.h>     //包含单片机寄存器的头文件
  2. #include<intrins.h>   //包含_nop_()函数定义的头文件
  3. /***********************************************************************
  4. 以下是DS1302芯片的操作程序
  5. ************************************************************************/
  6. unsigned char code digit[10]={"0123456789"};   //定义字符数组显示数字
  7. sbit DATA=P1^1;   //位定义1302芯片的接口,数据输出端定义在P1.1引脚
  8. sbit RST=P1^2;    //位定义1302芯片的接口,复位端口定义在P1.1引脚
  9. sbit SCLK=P1^0;   //位定义1302芯片的接口,时钟输出端口定义在P1.1引脚
  10. /*****************************************************
  11. 函数功能:延时若干微秒
  12. 入口参数:n
  13. ***************************************************/
  14. void delaynus(unsigned char n)
  15. {
  16. unsigned char i;
  17. for(i=0;i<n;i++)
  18.       ;
  19. }
  20. /*****************************************************
  21. 函数功能:向1302写一个字节数据
  22. 入口参数:x
  23. ***************************************************/
  24. void Write1302(unsigned char dat)
  25. {
  26.   unsigned char i;
  27.   SCLK=0;            //拉低SCLK,为脉冲上升沿写入数据做好准备
  28.   delaynus(2);       //稍微等待,使硬件做好准备
  29.   for(i=0;i<8;i++)      //连续写8个二进制位数据
  30.     {
  31.             DATA=dat&0x01;    //取出dat的第0位数据写入1302
  32.                 delaynus(2);       //稍微等待,使硬件做好准备
  33.                  SCLK=1;           //上升沿写入数据
  34.                  delaynus(2);      //稍微等待,使硬件做好准备
  35.                  SCLK=0;           //重新拉低SCLK,形成脉冲
  36.                  dat>>=1;          //将dat的各数据位右移1位,准备写入下一个数据位
  37.           }
  38.         
  39. }
  40. /*****************************************************
  41. 函数功能:根据命令字,向1302写一个字节数据
  42. 入口参数:Cmd,储存命令字;dat,储存待写的数据
  43. ***************************************************/
  44. void WriteSet1302(unsigned char Cmd,unsigned char dat)
  45. {
  46.    RST=0;           //禁止数据传递
  47.    SCLK=0;          //确保写数居前SCLK被拉低
  48.         RST=1;           //启动数据传输
  49.         delaynus(2);     //稍微等待,使硬件做好准备
  50.         Write1302(Cmd);  //写入命令字
  51.         Write1302(dat);  //写数据
  52.         SCLK=1;          //将时钟电平置于已知状态
  53.         RST=0;           //禁止数据传递
  54. }
  55. /*****************************************************
  56. 函数功能:从1302读一个字节数据
  57. 入口参数:x
  58. ***************************************************/
  59. unsigned char Read1302(void)
  60. {
  61.    unsigned char i,dat;
  62.         delaynus(2);       //稍微等待,使硬件做好准备
  63.         for(i=0;i<8;i++)   //连续读8个二进制位数据
  64.          {
  65.            dat>>=1;       //将dat的各数据位右移1位,因为先读出的是字节的最低位
  66.                 if(DATA==1)    //如果读出的数据是1
  67.                  dat|=0x80;    //将1取出,写在dat的最高位
  68.                  SCLK=1;       //将SCLK置于高电平,为下降沿读出
  69.                  delaynus(2);  //稍微等待
  70.                  SCLK=0;       //拉低SCLK,形成脉冲下降沿
  71.                  delaynus(2);  //稍微等待
  72.           }         
  73.   return dat;        //将读出的数据返回
  74. }  
  75. /*****************************************************
  76. 函数功能:根据命令字,从1302读取一个字节数据
  77. 入口参数:Cmd
  78. ***************************************************/
  79. unsigned char  ReadSet1302(unsigned char Cmd)
  80. {
  81.   unsigned char dat;
  82.   RST=0;                 //拉低RST
  83.   SCLK=0;                //确保写数居前SCLK被拉低
  84.   RST=1;                 //启动数据传输
  85.   Write1302(Cmd);       //写入命令字
  86.   dat=Read1302();       //读出数据
  87.   SCLK=1;              //将时钟电平置于已知状态
  88.   RST=0;               //禁止数据传递
  89.   return dat;          //将读出的数据返回
  90. }
  91. /*****************************************************
  92. 函数功能: 1302进行初始化设置
  93. ***************************************************/
  94. void Init_DS1302(void)
  95. {        
  96.    WriteSet1302(0x8E,0x00);                 //根据写状态寄存器命令字,写入不保护指令
  97.    WriteSet1302(0x80,((0/10)<<4|(0%10)));   //根据写秒寄存器命令字,写入秒的初始值
  98.         WriteSet1302(0x82,((0/10)<<4|(0%10)));   //根据写分寄存器命令字,写入分的初始值
  99.         WriteSet1302(0x84,((12/10)<<4|(12%10))); //根据写小时寄存器命令字,写入小时的初始值
  100.         WriteSet1302(0x86,((16/10)<<4|(16%10))); //根据写日寄存器命令字,写入日的初始值
  101.         WriteSet1302(0x88,((11/10)<<4|(11%10))); //根据写月寄存器命令字,写入月的初始值
  102.         WriteSet1302(0x8c,((8/10)<<4|(8%10)));   //根据写小时寄存器命令字,写入小时的初始值
  103. }
  104. /*******************************************************************************
  105. 以下是对液晶模块的操作程序
  106. *******************************************************************************/
  107. sbit RS=P2^0;           //寄存器选择位,将RS位定义为P2.0引脚
  108. sbit RW=P2^1;           //读写选择位,将RW位定义为P2.1引脚
  109. sbit E=P2^2;            //使能信号位,将E位定义为P2.2引脚
  110. sbit BF=P0^7;           //忙碌标志位,,将BF位定义为P0.7引脚
  111. /*****************************************************
  112. 函数功能:延时1ms
  113. (3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
  114. ***************************************************/
  115. void delay1ms()
  116. {
  117.    unsigned char i,j;        
  118.          for(i=0;i<10;i++)
  119.           for(j=0;j<33;j++)
  120.            ;                 
  121. }
  122. /*****************************************************
  123. 函数功能:延时若干毫秒
  124. 入口参数:n
  125. ***************************************************/
  126. void delaynms(unsigned char n)
  127. {
  128.    unsigned char i;
  129.         for(i=0;i<n;i++)
  130.            delay1ms();
  131. }
  132. /*****************************************************
  133. 函数功能:判断液晶模块的忙碌状态
  134. 返回值:result。result=1,忙碌;result=0,不忙
  135. ***************************************************/
  136. bit BusyTest(void)
  137.   {
  138.     bit result;
  139.         RS=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态
  140.     RW=1;
  141.     E=1;        //E=1,才允许读写
  142.     _nop_();   //空操作
  143.     _nop_();
  144.     _nop_();
  145.     _nop_();   //空操作四个机器周期,给硬件反应时间        
  146.     result=BF;  //将忙碌标志电平赋给result
  147.    E=0;         //将E恢复低电平
  148.    return result;
  149.   }
  150. /*****************************************************
  151. 函数功能:将模式设置指令或显示地址写入液晶模块
  152. 入口参数:dictate
  153. ***************************************************/
  154. void WriteInstruction (unsigned char dictate)
  155. {   
  156.     while(BusyTest()==1);   //如果忙就等待
  157.          RS=0;                  //根据规定,RS和R/W同时为低电平时,可以写入指令
  158.          RW=0;   
  159.          E=0;                   //E置低电平(根据表8-6,写指令时,E为高脉冲,
  160.                            // 就是让E从0到1发生正跳变,所以应先置"0"
  161.          _nop_();
  162.          _nop_();               //空操作两个机器周期,给硬件反应时间
  163.          P0=dictate;            //将数据送入P0口,即写入指令或地址
  164.          _nop_();
  165.          _nop_();
  166.          _nop_();
  167.          _nop_();               //空操作四个机器周期,给硬件反应时间
  168.          E=1;                   //E置高电平
  169.          _nop_();
  170.          _nop_();
  171.          _nop_();
  172.          _nop_();               //空操作四个机器周期,给硬件反应时间
  173.           E=0;                  //当E由高电平跳变成低电平时,液晶模块开始执行命令
  174. }
  175. /*****************************************************
  176. 函数功能:指定字符显示的实际地址
  177. 入口参数:x
  178. ***************************************************/
  179. void WriteAddress(unsigned char x)
  180. {
  181.      WriteInstruction(x|0x80); //显示位置的确定方法规定为"80H+地址码x"
  182. }
  183. /*****************************************************
  184. 函数功能:将数据(字符的标准ASCII码)写入液晶模块
  185. 入口参数:y(为字符常量)
  186. ***************************************************/
  187. void WriteData(unsigned char y)
  188. {
  189.     while(BusyTest()==1);  
  190.           RS=1;           //RS为高电平,RW为低电平时,可以写入数据
  191.           RW=0;
  192.           E=0;            //E置低电平(根据表8-6,写指令时,E为高脉冲,
  193.                      // 就是让E从0到1发生正跳变,所以应先置"0"
  194.           P0=y;           //将数据送入P0口,即将数据写入液晶模块
  195.           _nop_();
  196.           _nop_();
  197.            _nop_();
  198.      _nop_();       //空操作四个机器周期,给硬件反应时间
  199.           E=1;           //E置高电平
  200.           _nop_();
  201.           _nop_();
  202.           _nop_();
  203.          _nop_();        //空操作四个机器周期,给硬件反应时间
  204.          E=0;            //当E由高电平跳变成低电平时,液晶模块开始执行命令
  205. }
  206. /*****************************************************
  207. 函数功能:对LCD的显示模式进行初始化设置
  208. ***************************************************/
  209. void LcdInitiate(void)
  210. {
  211.     delaynms(15);               //延时15ms,首次写指令时应给LCD一段较长的反应时间
  212.     WriteInstruction(0x38);     //显示模式设置:16×2显示,5×7点阵,8位数据接口
  213.         delaynms(5);                //延时5ms ,给硬件一点反应时间
  214.     WriteInstruction(0x38);
  215.         delaynms(5);               //延时5ms ,给硬件一点反应时间
  216.         WriteInstruction(0x38);     //连续三次,确保初始化成功
  217.         delaynms(5);               //延时5ms ,给硬件一点反应时间
  218.         WriteInstruction(0x0c);     //显示模式设置:显示开,无光标,光标不闪烁
  219.         delaynms(5);               //延时5ms ,给硬件一点反应时间
  220.         WriteInstruction(0x06);     //显示模式设置:光标右移,字符不移
  221.         delaynms(5);                //延时5ms ,给硬件一点反应时间
  222.         WriteInstruction(0x01);     //清屏幕指令,将以前的显示内容清除
  223.         delaynms(5);             //延时5ms ,给硬件一点反应时间

  224. }
  225. /**************************************************************
  226. 以下是1302数据的显示程序
  227. **************************************************************/
  228. /*****************************************************
  229. 函数功能:显示秒
  230. 入口参数:x
  231. ***************************************************/
  232. void DisplaySecond(unsigned char x)
  233. {
  234. unsigned char i,j;     //j,k,l分别储存温度的百位、十位和个位
  235.         i=x/10;//取十位
  236.         j=x%10;//取个位     
  237.         WriteAddress(0x49);    //写显示地址,将在第2行第7列开始显示
  238.         WriteData(digit[i]);    //将百位数字的字符常量写入LCD
  239.         WriteData(digit[j]);    //将十位数字的字符常量写入LCD
  240.         delaynms(50);         //延时1ms给硬件一点反应时间   
  241. }

  242. /*****************************************************
  243. 函数功能:显示分钟
  244. 入口参数:x
  245. ***************************************************/
  246. void DisplayMinute(unsigned char x)
  247. {
  248. unsigned char i,j;     //j,k,l分别储存温度的百位、十位和个位
  249.         i=x/10;//取十位
  250.         j=x%10;//取个位     
  251.         WriteAddress(0x46);    //写显示地址,将在第2行第7列开始显示
  252.         WriteData(digit[i]);    //将百位数字的字符常量写入LCD
  253.         WriteData(digit[j]);    //将十位数字的字符常量写入LCD
  254.         delaynms(50);         //延时1ms给硬件一点反应时间   
  255. }
  256. /*****************************************************
  257. 函数功能:显示小时
  258. 入口参数:x
  259. ***************************************************/
  260. void DisplayHour(unsigned char x)
  261. {
  262. unsigned char i,j;     //j,k,l分别储存温度的百位、十位和个位
  263.         i=x/10;//取十位
  264.         j=x%10;//取个位     
  265.         WriteAddress(0x43);    //写显示地址,将在第2行第7列开始显示
  266.         WriteData(digit[i]);    //将百位数字的字符常量写入LCD
  267.         WriteData(digit[j]);    //将十位数字的字符常量写入LCD
  268.         delaynms(50);         //延时1ms给硬件一点反应时间   
  269. }
  270. /*****************************************************
  271. 函数功能:显示日
  272. 入口参数:x
  273. ***************************************************/
  274. void DisplayDay(unsigned char x)
  275. {
  276. unsigned char i,j;     //j,k,l分别储存温度的百位、十位和个位
  277.         i=x/10;//取十位
  278.         j=x%10;//取个位     
  279.         WriteAddress(0x0c);    //写显示地址,将在第2行第7列开始显示
  280.         WriteData(digit[i]);    //将百位数字的字符常量写入LCD
  281.         WriteData(digit[j]);    //将十位数字的字符常量写入LCD
  282.         delaynms(50);         //延时1ms给硬件一点反应时间   
  283. }
  284. /*****************************************************
  285. 函数功能:显示月
  286. 入口参数:x
  287. ***************************************************/
  288. void DisplayMonth(unsigned char x)
  289. {
  290. unsigned char i,j;     //j,k,l分别储存温度的百位、十位和个位
  291.         i=x/10;//取十位
  292.         j=x%10;//取个位     
  293.         WriteAddress(0x09);    //写显示地址,将在第2行第7列开始显示
  294.         WriteData(digit[i]);    //将百位数字的字符常量写入LCD
  295.         WriteData(digit[j]);    //将十位数字的字符常量写入LCD
  296.         delaynms(50);         //延时1ms给硬件一点反应时间   
  297. }
  298. /*****************************************************
  299. 函数功能:显示年
  300. 入口参数:x
  301. ***************************************************/
  302. void DisplayYear(unsigned char x)
  303. {
  304. unsigned char i,j;     //j,k,l分别储存温度的百位、十位和个位
  305.         i=x/10;//取十位
  306.         j=x%10;//取个位     
  307.         WriteAddress(0x06);    //写显示地址,将在第2行第7列开始显示
  308.         WriteData(digit[i]);    //将百位数字的字符常量写入LCD
  309.         WriteData(digit[j]);    //将十位数字的字符常量写入LCD
  310.         delaynms(50);         //延时1ms给硬件一点反应时间   
  311. }

  312. /*****************************************************
  313. 函数功能:主函数
  314. ***************************************************/
  315. void main(void)
  316. {
  317.   unsigned char second,minute,hour,day,month,year;      //分别储存苗、分、小时,日,月,年
  318.   unsigned char ReadValue;   //储存从1302读取的数据
  319.   LcdInitiate();             //将液晶初始化
  320.   WriteAddress(0x01);  //写Date的显示地址,将在第1行第2列开始显示
  321.   WriteData('D');      //将字符常量写入LCD
  322.   WriteData('a');      //将字符常量写入LCD
  323.   WriteData('t');      //将字符常量写入LCD
  324.   WriteData('e');      //将字符常量写入LCD
  325.   WriteData(':');      //将字符常量写入LCD
  326.   WriteAddress(0x08);  //写年月分隔符的显示地址, 显示在第1行第9列
  327.   WriteData('-');      //将字符常量写入LCD
  328.   WriteAddress(0x0b);  //写月日分隔符的显示地址, 显示在第1行第12列
  329.   WriteData('-');      //将字符常量写入LCD
  330.   WriteAddress(0x45);  //写小时与分钟分隔符的显示地址, 显示在第2行第6列
  331.   WriteData(':');      //将字符常量写入LCD
  332.   WriteAddress(0x48);  //写分钟与秒分隔符的显示地址, 显示在第2行第9列
  333.   WriteData(':');      //将字符常量写入LCD
  334.   Init_DS1302();       //将1302初始化        
  335.   while(1)
  336.     {
  337.           ReadValue = ReadSet1302(0x81);   //从秒寄存器读数据
  338.      second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
  339.           DisplaySecond(second);          //显示秒
  340.           ReadValue = ReadSet1302(0x83);  //从分寄存器读
  341.      minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //将读出数据转化
  342.           DisplayMinute(minute);               //显示分
  343.      ReadValue = ReadSet1302(0x85);  //从分寄存器读
  344.      hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //将读出数据转化
  345.           DisplayHour(hour);                     //显示小时
  346.      ReadValue = ReadSet1302(0x87);  //从分寄存器读
  347.      day=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //将读出数据转化
  348.           DisplayDay(day);                //显示日
  349.           ReadValue = ReadSet1302(0x89);  //从分寄存器读
  350.      month=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //将读出数据转化
  351.           DisplayMonth(month);            //显示月
  352.           ReadValue = ReadSet1302(0x8d);  //从分寄存器读
  353.      year=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //将读出数据转化
  354.           DisplayYear(year);              //显示年
  355.          }
  356. }

复制代码



全部资料下载地址:
基于DS1302的日历时钟.zip (60.35 KB, 下载次数: 73)


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

使用道具 举报

沙发
ID:341150 发表于 2018-5-30 10:11 | 只看该作者
楼主厉害了,学到了学到了
回复

使用道具 举报

板凳
ID:341150 发表于 2018-5-30 12:14 | 只看该作者
很不错,学习了,希望楼主继续发这样的帖子
回复

使用道具 举报

地板
ID:341150 发表于 2018-6-6 13:19 | 只看该作者
但是如何调节时间呢
回复

使用道具 举报

5#
ID:337883 发表于 2018-12-5 12:43 | 只看该作者
请问一下楼主,这个如何调节时间呢
回复

使用道具 举报

6#
ID:385250 发表于 2019-7-9 09:57 | 只看该作者
时间如何调节
回复

使用道具 举报

7#
ID:84745 发表于 2020-9-19 22:29 来自手机 | 只看该作者
没Uv2文件,初学者不会
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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