PROTEUS仿真——万年历_C51+LCD1602+DS1302+DS18B20

PROTEUS仿真——万年历

LCD1602显示年月日、星期、时间和温度。

原理图如下:

 DS1302时钟芯片驱动代码

//DS1302写8位地址和数据 
void RTC_wcom(uchar addr,uchar wdata)
{
   uchar i,j;
   RTC_RST=0;
   RTC_CLK=1;
   RTC_RST=1;
   for(i=0;i<8;i++)
   {
      RTC_DAT=addr >> i & 0x01;
      RTC_CLK=0;
      RTC_CLK=1;
   }
   for(j=0;j<8;j++)
   {
      RTC_DAT=wdata >> j & 0x01;
      RTC_CLK=0;
      RTC_CLK=1;
   }
   RTC_CLK=1;
   RTC_RST=0;
}

//DS1302读数据
uchar RTC_rcom(uchar addr)
{
   uchar i,rdata=0;
   RTC_RST=1;
   for(i=0;i<8;i++)
   {
      RTC_CLK=0;
      RTC_DAT=addr>>i & 0x01;
      RTC_CLK=1;
   }
   for(i=0;i<8;i++)
   {
      RTC_CLK=1;
      RTC_CLK=0;
      if(RTC_DAT)
      rdata |= (0x01<<i);
   }
   RTC_DAT=0;
   RTC_CLK=1;
   RTC_RST=0;
   return rdata;
}

//DS1302初始化
void RTC_init(void)
{
   RTC_wcom(0x8e,0x00);	//00允许写,80写保护
   RTC_wcom(0x8c,0x00);	//年,00~99,初始00
   RTC_wcom(0x88,0x01);	//月,01~12,初始01    
   RTC_wcom(0x86,0x01);	//日,01~31,初始01
   RTC_wcom(0x8a,0x06);	//星期,1~7,初始6   
   RTC_wcom(0x84,0x12);	//时,00~23,初始12
   RTC_wcom(0x82,0x00);	//分,00~59,初始00
   RTC_wcom(0x80,0x00);	//秒,00~59,初始00
}

DS18B20驱动代码

uint tmp;
uchar tmp_sign;
//DS18B20对时序有一定要求,有问题的话查一下延迟时序是否符合规格书要求
//DS18B20初始化
void tmp_init(void)
{
   TMP_DQ=0;
   delay(80);		//拉低480~960us
   TMP_DQ=1;
   delay(1);
   while(TMP_DQ);	//等待应答
   while(!TMP_DQ);	//应答结束后等待DS18B20释放总线
}
//DS18B20写命令
void tmp_write(uchar cmd)
{
   uchar i;
   for(i=0;i<8;i++)
   {
      TMP_DQ=0;
      _nop_();
      TMP_DQ=cmd & 0x01;
      cmd >>= 1;
      delay(5);
      TMP_DQ=1;
      _nop_();
   }
}
//读取数据,先低后高
uchar tmp_read(void)
{
   uchar i;
   uchar dat=0;
   for(i=0;i<8;i++)
   {
      TMP_DQ=0;
      _nop_();
      TMP_DQ=1;
      _nop_();
      dat >>= 1;
      if(TMP_DQ==1)
      dat |= 0x80;
      delay(5);
      TMP_DQ=1;
      _nop_();
   }
   return dat;
}

void get_tmp(void)
{
   uchar tmpL,tmpH;
   tmp_init();		//初始化
   tmp_write(0xcc);	//跳过ROM
   tmp_write(0x44);	//温度转换
   tmp_init();		//再初始化
   tmp_write(0xcc);	//跳过ROM
   tmp_write(0xbe);	//请求数据读取
   tmpL=tmp_read();	//接收温度低位
   tmpH=tmp_read();	//接收温度低位
   tmp=tmpH;		//高位和低位合并
   tmp=(tmp<<8)|tmpL;
   if(tmp<0x0fff)	//判断是正温度
   {
      tmp_sign=10;	//符号为空
      tmp=tmp*5/8;	//数值*0.0625为温度值,留一位小数,为了方便数据处理,乘以10倍,转为整型
   }			//这里用小数的话,代码长度刚好超2k了,一个方法是直接改成(*5/8),节省空间
   else			//另一个方法是设置里,把ROM由small改为large
   {
      tmp_sign=11;	//负温度显示负号“-”
      tmp=(~tmp+1)*5/8;
   }
}

LCD1602驱动代码

uchar code num[] = "0123456789 -";
uchar code week[][3] = {"MON","TUE","WED","THU","FRI","SAT","SUN"};
uchar yyH,yyL,moH,moL,ddH,ddL,hhH,hhL,miH,miL,ssH,ssL;
uint wk;

//LCD1602写命令
void LCD_wr_cmd(uchar cmd)
{
   busy_check();
   LCD_RS=0;
   LCD_RW=0;
   LCD_CE=1;
   LCD_DA=cmd;
   delay(100);
   LCD_CE=0;
}
//LCD1602写数据
void LCD_wr_dat(uchar dat)
{
   busy_check();
   LCD_RS=1;
   LCD_RW=0;
   LCD_CE=1;
   LCD_DA=dat;
   delay(100);
   LCD_CE=0;
}
//LCD1602查忙,检查前面数据是否已处理完
void busy_check(void)
{
   uchar busy_flag;
   busy_flag = 0xff;
   LCD_RS=0;
   LCD_RW=1;
   do
   {
      LCD_CE=1;
      busy_flag=LCD_DA;
      delay(100);
      LCD_CE=0;
   }while(busy_flag & 0x80);
}
//LCD初始化
void LCD_init(void)
{
   LCD_wr_cmd(0x01);	//清显示
   LCD_wr_cmd(0x06);	//光标右移,文字不动
   LCD_wr_cmd(0x0c);	//开显示,无光标,不闪烁
   LCD_wr_cmd(0x38);	//8位总线,双行显示,5X7的点阵字符
}
//获取年月日,星期,时间数据
void get_dat(void)
{
   yyH=RTC_rcom(0x8d)/16;
   yyL=RTC_rcom(0x8d)%16;
   moH=RTC_rcom(0x89)/16;
   moL=RTC_rcom(0x89)%16;
   ddH=RTC_rcom(0x87)/16;
   ddL=RTC_rcom(0x87)%16;
   wk =RTC_rcom(0x8b);
   hhH=RTC_rcom(0x85)/16;
   hhL=RTC_rcom(0x85)%16;
   miH=RTC_rcom(0x83)/16;
   miL=RTC_rcom(0x83)%16;
   ssH=RTC_rcom(0x81)/16;
   ssL=RTC_rcom(0x81)%16;
}

按键设置

uchar key=0;
uchar code month_day[]={31,28,31,30,31,30,31,31,30,31,30,31};
//按键扫描
void key_scan(void)
{
   if(!t_set)
      {
	 delay10ms(1);
	 if(!t_set)
	 while(!t_set);
	 key=1;
      }
   if(!t_rst)
      {
	 delay10ms(1);
	 if(!t_rst)
	 while(!t_rst);
	 key=2;
      }
}
//按加减键设置时间
void manual_set(uchar min,uchar max,uchar LCD_cmd,uchar RTC_cmd)
{
   uchar temp=min;
   do
   {
      key_scan();	//按键扫描
      if(!t_add)	//加按键按下,按住就一直增加
      {
	 delay10ms(10);
	 if(!t_add)
	 {
	    ++temp;
	    if(temp==max+1)temp=min;
	    LCD_wr_cmd(LCD_cmd|0x80);	//显示调整的值
	    LCD_wr_dat(num[temp/10]);
	    LCD_wr_dat(num[temp%10]);
	    delay10ms(20);		//增加延时,调节增加的速度
	 }
      }
      if(!t_dec)	//减按键按下,功能同上
      {
	 delay10ms(10);
	 if(!t_dec)
	 {
	    if(temp==min)temp=max+1;
	    --temp;
	    LCD_wr_cmd(LCD_cmd|0x80);
	    LCD_wr_dat(num[temp/10]);
	    LCD_wr_dat(num[temp%10]);
	    delay10ms(20);
	 }
      }
   }while(key!=1);	//确定设置键是否按下,按下就跳出循环
   key=0;		//按键值清零
   RTC_wcom(RTC_cmd,temp/10*16+temp%10);	//最终值写入DS1302
}
//星期的计算
void week_set(void)
{
   uchar i;
   yyH=RTC_rcom(0x8d)/16;	//获取年月日数据
   yyL=RTC_rcom(0x8d)%16;
   moH=RTC_rcom(0x89)/16;
   moL=RTC_rcom(0x89)%16;
   ddH=RTC_rcom(0x87)/16;
   ddL=RTC_rcom(0x87)%16;
   wk=(yyH*10+yyL)*365+(yyH*10+yyL)/4;	//先按年的天数计算,加上闰年天数
   for(i=0;i<moH*10+moL-1;i++)		//再加上月的天数
   {
      wk=wk+month_day[i];
   }
   wk=wk+ddH*10+ddL;		//再加上天数
   if((yyH*10+yyL)/4==0)	//如果当年是闰年
   {
   if(moH*10+moL<3)		//且当月不到3月份
      wk=wk-1;			//需减去1天
   }
   wk=(wk%7+5)%7+1;	//总天数除以7取余,加上2000年1月1日补偿数5,可能超过7,再取余,避免出现0,再加1
   RTC_wcom(0x8a,wk);	//写入寄存器
}
//时间设置
void time_set(void)
{
   key_scan();
   if(key==1)	//设置键按下
   {
      key=0;
      LCD_wr_cmd(0x01);		//清显示
      LCD_wr_cmd(0x0f);		//开显示,有光标,闪烁
      LCD_wr_cmd(0x00|0x80);
      LCD_wr_dat('2');
      LCD_wr_dat('0');
      LCD_wr_dat('0');
      LCD_wr_dat('0');
      RTC_wcom(0x8e,0x00);	//00允许写
      RTC_wcom(0x80,0x80);	//停止计时
      manual_set(0,99,0x02,0x8c);	//设置年
      LCD_wr_dat('-');
      LCD_wr_dat('0');
      LCD_wr_dat('1');
      manual_set(1,12,0x05,0x88);	//设置月
      LCD_wr_dat('-');
      LCD_wr_dat('0');
      LCD_wr_dat('1');
      manual_set(1,31,0x08,0x86);	//设置日
      LCD_wr_cmd(0x40|0x80);
      LCD_wr_dat('0');
      LCD_wr_dat('0');
      manual_set(0,23,0x40,0x84);	//设置小时
      LCD_wr_dat(':');
      LCD_wr_dat('0');
      LCD_wr_dat('0');
      manual_set(0,59,0x43,0x82);	//设置分钟
      week_set();
      RTC_wcom(0x80,0x00);	//开始计时
      LCD_wr_cmd(0x0c);		//开显示,无光标,不闪烁
   }
   if(key==2)	//复位键按下
   {
      key=0;
      RTC_init();	//DS1302恢复初始值
   }
}

主函数及显示部分:

#include <reg51.h>

sbit LCD_RS=P2^0;	//LCD相关端口
sbit LCD_RW=P2^1;
sbit LCD_CE=P2^2;

sbit t_set=P2^4;	//按键
sbit t_add=P2^5;
sbit t_dec=P2^6;
sbit t_rst=P2^7;

sbit RTC_DAT=P1^0;	//DS1302相关端口
sbit RTC_CLK=P1^1;
sbit RTC_RST=P1^2;

sbit TMP_DQ=P3^7;

#define LCD_DA P0	//LCD数据口
typedef unsigned char uchar;
typedef unsigned int uint;

void display(void)
{
   uchar i;
   uchar a,b,c;
   get_dat();
   //显示日期
   LCD_wr_cmd(0x00|0x80);	//从1行0列开始显示
   LCD_wr_dat('2');
   LCD_wr_dat('0');
   LCD_wr_dat(num[yyH]);
   LCD_wr_dat(num[yyL]);
   LCD_wr_dat('-');
   LCD_wr_dat(num[moH]);
   LCD_wr_dat(num[moL]);
   LCD_wr_dat('-');
   LCD_wr_dat(num[ddH]);
   LCD_wr_dat(num[ddL]);
   //显示星期
   LCD_wr_cmd(0x0c|0x80);
   for(i=0;i<3;i++)
   {
      LCD_wr_dat(week[wk-1][i]);
   }   
   //显示时间
   LCD_wr_cmd(0x40|0x80);	//从2行0列开始显示
   LCD_wr_dat(num[hhH]);
   LCD_wr_dat(num[hhL]);
   LCD_wr_dat(':');
   LCD_wr_dat(num[miH]);
   LCD_wr_dat(num[miL]);
   LCD_wr_dat(':');
   LCD_wr_dat(num[ssH]);
   LCD_wr_dat(num[ssL]);
   //显示温度
   get_tmp();	//获取已乘10倍的温度值
   a=tmp/100;	//取百位数作为十位数
   b=tmp/10%10;	//取十位数作为个位数
   c=tmp%10;	//取个位数作为小数点后一位数
   LCD_wr_cmd(0x49|0x80);	//从2行9列开始显示
   LCD_wr_dat(num[tmp_sign]);	//显示温度符号,正温度显示空格
   if(a!=0)	//如果十位不是0,就显示,否则跳过不显示
   LCD_wr_dat(num[a]);
   LCD_wr_dat(num[b]);
   LCD_wr_dat('.');
   LCD_wr_dat(num[c]);
   LCD_wr_dat(0xdf);	//角度符号,加上下面的大写“C”,作为温度符号
   LCD_wr_dat('C');
   LCD_wr_dat(num[10]);	//末尾加一个空格,避免十位不显示时,这里有残留的“C”符号
}

void main(void)
 { 
   RTC_init();	//DS1302初始化
   LCD_init();	//LCD1602初始化
   while (1)
   {
      display();	//显示
      time_set();	//时间设置
   }
 }

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值