1、前期需求
Proteus仿真软件+keil5 51
2、仿真图
3、代码
mian函数
//注意:当前日期不能乱改,DS1302容易报错。
#include <reg52.h> //调用单片机头文件
#include <intrins.h>
#include <lcd.h>
#include <ds1302.h>
#include <delay.h>
#include"Ds18b20.h"
#define uchar unsigned char //无符号字符型 宏定义 变量范围0~255
#define uint unsigned int //无符号整型 宏定义 变量范围0~65535
sbit out=P2^0; //蜂鸣器
sbit shijian=P1^1;//下一个
sbit jia=P1^2;//加
sbit jian=P1^3;//减
sbit off=P1^4;//关闭
sbit ok=P1^0;//ok
sbit shang=P3^3;//上一个
sbit next=P2^4;//切换
sbit s1=P2^1;
sbit s2=P2^2;
sbit s3=P2^3;
sbit test=P1^7;
//*****************************************
//定义允许报警的时间段,在该时间段内可以报警
uchar addr=0; //LCD显示位置追踪0-7,LCD上每个能被修改的位置从0-7编号,以便统一操作
uchar shi;//定时时间:小时
uchar fen;//定时时间:分钟
uchar off0=1;//关闭标志
uchar up=30,down=20;
uchar flag=0,mode=0,start=0;
uchar sec=0,miao=0,fen1=0;//秒表
/********************************************************************
* 名称 : BCD_INT()
* 功能 : 将BCD码转换成十进制
* 输入 : k亮度0-65535
* 输出 : 无
***********************************************************************/
uchar BCD_INT(uchar i)
{
return 10*(i>>4)+(i & 0x0F);
}
/********************************************************************
* 名称 : INT_BCD()
* 功能 : 转换为十进制
* 输入 : k亮度0-65535
* 输出 : 无
***********************************************************************/
uchar INT_BCD(uchar k)
{
return ((k/10)<<4)+(k % 10);
}
/********************************************************************
* 名称 : addr_set()
* 功能 : addr对应变量+1
* 输入 : 无
* 输出 : 无
***********************************************************************/
void addr_set()
{
uchar i;
switch(addr)
{
case 0: i=BCD_INT(shi);//定时时间加一不能大于23
if(i<23)
{
i++;
shi=INT_BCD(i);
}
break;
case 1: i=BCD_INT(fen);//定时分钟加一,小于60
if(i<59)
{
i++;
fen=INT_BCD(i);
}
break;
case 2: i=BCD_INT(TIME[6]);//年,加一
if(i<99)
{
i++;
TIME[6]=INT_BCD(i);
}
break;
break;
case 3: i=BCD_INT(TIME[4]);//月,加一
if(i<12)
{
i++;
TIME[4]=INT_BCD(i);
}
break;
case 4: i=BCD_INT(TIME[3]);//日,加一
if(i<30)
{
i++;
TIME[3]=INT_BCD(i);
}break;
case 5: i=BCD_INT(TIME[2]);//时,加一
if(i<23)
{
i++;
TIME[2]=INT_BCD(i);
}break;
case 6: i=BCD_INT(TIME[1]);//分,加一
if(i<59)
{
i++;
TIME[1]=INT_BCD(i);
}
break;
case 7: i=BCD_INT(TIME[5]);//星期,加一
if(i<6)
{
i++;
TIME[5]=INT_BCD(i);
}
break;
case 8: if(up<99)
up++;
break;
case 9: if(down<up)
down++;
}
}
/********************************************************************
* 名称 : addr_clr()
* 功能 : addr对应变量-1
* 输入 : 无
* 输出 : 无
***********************************************************************/
void addr_clr()
{
uchar i;
switch(addr)
{
case 0: i=BCD_INT(shi);//定时时间减一
if(i>0)
{
i--;
shi=INT_BCD(i);
}
break;
case 1: i=BCD_INT(fen);//定时分钟减一
if(i>0)
{
i--;
fen=INT_BCD(i);
}
break;
case 2: i=BCD_INT(TIME[6]);//年,减一
if(i>0)
{
i--;
TIME[6]=INT_BCD(i);
}
break;
case 3: i=BCD_INT(TIME[4]);//月,减一
if(i>1)
{
i--;
TIME[4]=INT_BCD(i);
}
break;
case 4: i=BCD_INT(TIME[3]);//日,减一
if(i>0)
{
i--;
TIME[3]=INT_BCD(i);
}
break;
case 5: i=BCD_INT(TIME[2]);//时,减一
if(i>0)
{
i--;
TIME[2]=INT_BCD(i);
}
break;
case 6: i=BCD_INT(TIME[1]);//分,减一
if(i>0)
{
i--;
TIME[1]=INT_BCD(i);
}
break;
case 7: i=BCD_INT(TIME[5]);//星期,减一
if(i>0)
{
i--;
TIME[5]=INT_BCD(i);
}
break;
case 8: if(up>down)
up--;
break;
case 9: if(down>0)
down--;
}
}
/********************************************************************
* 名称 : addr_guang()
* 功能 : addr对应变量+1
* 输入 : 无
* 输出 : 无
***********************************************************************/
void addr_guang()
{
switch(addr)
{
case 0: write_sfm(1,0);//设置光标到定时,小时
break;
case 1: write_sfm(1,3);//设置光标到定时,分钟
break;
case 2: write_sfm(2,0);//设置光标到年
break;
case 3: write_sfm(2,5);//设置光标到月
break;
case 4: write_sfm(2,8);//设置光标到日
break;
case 5: write_sfm(1,6);//设置光标到小时
break;
case 6: write_sfm(1,9);//设置光标到分钟
break;
case 7: write_sfm(1,15);//星期
break;
case 8: write_sfm(1,3);//温度上下限
break;
case 9: write_sfm(2,3);
}
if(addr<10)
write_com(0x0e);
else
write_com(0x0c);
}
//显示秒表
void disp()
{
write_com(0x0c); //关光标
write_string(1,0,"stopwatch:");
write_sfm(2,4); //
write_data(fen1/10+0x30);
write_data(fen1%10+0x30);
write_data(':');
write_data(miao/10+0x30);
write_data(miao%10+0x30);
write_data(':');
write_data(sec/10+0x30);
write_data(sec%10+0x30);
}
/********************************************************************
* 名称 : xianshi()
* 功能 : 读取日期,显示日期;判断报警
* 输入 : 无
* 输出 : 无
***********************************************************************/
void xianshi()
{
if(mode){
disp();
return;
}
write_com(0x0c); //关光标
write_sfm(2,0); //第二行第一列开始显示当前日期
write_data('2');
write_data('0');
write_data((TIME[6]>> 4)+0x30); //年
write_data((TIME[6] & 0x0f)+0x30);//9
write_data('-');
write_data((TIME[4]>> 4)+0x30); //月
write_data((TIME[4] & 0x0f)+0x30);
write_data('-');
write_data((TIME[3]>> 4)+0x30); //日
write_data((TIME[3] & 0x0f)+0x30);
write_data(' ');
write_data(' ');
write_data(' ');
write_data(' ');
write_data(' ');
write_sfm(1,6);
write_data((TIME[2]>> 4)+0x30); //时
write_data((TIME[2] & 0x0f)+0x30);
write_data(':');
write_data((TIME[1]>> 4)+0x30); //分
write_data((TIME[1] & 0x0f)+0x30);
write_data(':');
write_data((TIME[0]>> 4)+0x30); //秒
write_data((TIME[0] & 0x0f)+0x30);
write_data(' ');
if(TIME[5]>1)
write_data(TIME[5]+0x2f); //星期
else
write_data('7');
write_sfm(1,0); //显示定时时间
write_data((shi>> 4)+0x30);
write_data((shi & 0x0f)+0x30);
write_data(':');
write_data((fen>> 4)+0x30);
write_data((fen & 0x0f)+0x30);
write_data(' ');
write_sfm(2,12); //显示定时时间
// write_data(Ds18_temp/10+0x30);
// write_data(Ds18_temp%10+0x30);
// write_data(0xdf);
// write_data('C');
addr_guang();
}
/***************主函数*****************/
void main()
{
uint time0,time1;//临时变量
uchar led_time=1;//led点亮,中间变量
init_1602(); //1602液晶初始化
Ds1302ReadTime();//读取7个字节
xianshi();//调用显示函数初始化LCD
addr=7;
shi=0x16;//定时默认6点00分
fen=0x30;
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=(65536-10000)/256; //给定时器赋初值,定时10ms
TL0=(65536-10000)%256;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
while(1)
{
delay_uint(20000);
Ds1302ReadTime();//读取时间
if(mode==0)
{
xianshi();//显示
TR0=0;
}
else
{
TR0=1;
}
if(!next)//切换显示
{
mode=!mode;
write_string(1,0," ");
write_string(2,0," ");
if(mode)
disp();
while(!next);
}
//秒表控制
if(TR0)
{
if(!s1)
start=1;
if(!s2)
start=0;
if(!s3)
{
sec=0;
miao=0;
fen1=0;
start=0;
while(!s3);
}
}
time0=600*(TIME[2]>>4)+60*(TIME[2] & 0x0F)+10*(TIME[1]>>4)+(TIME[1] & 0x0F);//将读取的时间转换成分钟,便于计算
time1=600*(shi>>4)+60*(shi & 0x0F)+10*(fen>>4)+(fen & 0x0F);//将定时时间转换成分钟
if((time0==time1)&&(TIME[0]==0))//时间到
{
off0=0;
}
if(!off0 && (time0<time1+10) && (time0>time1-1))//如果时间到了,响铃,震动10分钟直到按下关闭按钮
out=1;
else
out=0;
//==========================================================================
if(!off)//检测关闭按钮
{
off0=1;
}
//=======================================
if(!shijian)//选择下一个要修改的时间
{
if(addr<7)
addr++;
else
addr=0;
addr_guang();
while(!shijian);
}
if(!shang)
{
if(addr>0)
addr--;
else
addr=7;
addr_guang();
while(!shang);
}
//=======================================
if(!ok)//确认
{
addr=8;
addr_guang();
while(!ok);
}
//=======================================
if(!jia)//加
{
addr_set();
while(!jia);
if(addr>1)
Ds1302WriteTime();//写入DS1302
while(!jia);
}
//=======================================
if(!jian)//减
{
addr_clr();
while(!jian);
if(addr>1)
Ds1302WriteTime();
while(!jian);
}
}
}
//定时器中断
void Timer0() interrupt 1
{
if(start)
{
if(sec<99)
sec++;
else
{
sec=0;
if(miao<59)
miao++;
else
{
miao=0;
if(fen1<99)
fen1++;
}
}
}
xianshi();//显示
TH0=(65536-10000)/256; //给定时器赋初值,定时10ms
TL0=(65536-10000)%256;
}
lcd.c
#include "lcd.h"
#include "delay.h"
/********************************************************************
* 名称 : write_com(uchar com)
* 功能 : 1602命令函数
* 输入 : 输入的命令值
* 输出 : 无
***********************************************************************/
void write_com(uchar com)
{
e=0;
rs=0;
rw=0;
P0=com;
delay_uint(3);
e=1;
delay_uint(25);
e=0;
}
/********************************************************************
* 名称 : write_data(uchar dat)
* 功能 : 1602写数据函数
* 输入 : 需要写入1602的数据
* 输出 : 无
***********************************************************************/
void write_data(uchar dat)
{
e=0;
rs=1;
rw=0;
P0=dat;
delay_uint(3);
e=1;
delay_uint(25);
e=0;
}
/********************************************************************
* 名称 : write_sfm(uchar hang,uchar add,uchar date)
* 功能 : 设置当前行和列
* 输入 : 行,列
* 输出 : 无
***********************************************************************/
void write_sfm(uchar hang,uchar add)
{
if(hang==1) //设置当前行
write_com(0x80+add);
else
write_com(0x80+0x40+add);
}
/********************************************************************
* 名称 : write_string(uchar hang,uchar add,uchar *p)
* 功能 : 改变液晶中某位的值,如果要让第一行,第五个字符开始显示"ab cd ef" ,调用该函数如下
write_string(1,5,"ab cd ef;")
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void write_string(uchar hang,uchar add,uchar *p)
{
if(hang==1)
write_com(0x80+add);
else
write_com(0x80+0x40+add);
while(1)
{
if(*p == '\0') break;
write_data(*p);
p++;
}
}
/********************************************************************
* 名称 : init_1602()
* 功能 : 初始化1602液晶
* 输入 : 无
* 输出 : 无
***********************************************************************/
void init_1602()
{
write_com(0x38); //数据总线为8位,显示2行,5x7点阵
write_com(0x0e); //开显示,有光标,光标闪烁
write_com(0x06); //光标自动右移
delay_uint(1000); //等待设置完成
}
lcd.h
#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS
/**********************************
包含头文件
**********************************/
#include<reg52.h>
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
/**********************************
PIN口定义
**********************************/
sbit rs=P2^6; //1602数据/命令选择引脚 H:数据 L:命令
sbit rw=P2^5; //1602读写引脚 H:数据寄存器 L:指令寄存器
sbit e =P2^7; //1602使能引脚 下降沿触发
sbit rs2=P1^5; //1602数据/命令选择引脚 H:数据 L:命令
sbit rw2=P1^6; //1602读写引脚 H:数据寄存器 L:指令寄存器
sbit e2 =P1^7; //1602使能引脚 下降沿触发
/**********************************
函数声明
**********************************/
void write_com(uchar com); //LCD1602写入8位数据子函数
void write_data(uchar dat); //1602写数据函数
void init_1602(); //LCD1602初始化子程序
void write_sfm(uchar hang,uchar add); //设置当前行和列
void write_string(uchar hang,uchar add,uchar *p);
#endif
ds1302.c
#include"ds1302.h"
//定义读地址
uchar code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; //秒分时日月周年
//定义写地址,秒分时日月周年
uchar code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
//定义时间数组,秒分时日月周年
uchar TIME[7]={0,0,0x12,0x16,0x09,0x01,0x19};
/*******************************************************************************
* 函 数 名 : Ds1302Write
* 函数功能 : 向DS1302命令(地址+数据)
* 输 入 : addr,dat
* 输 出 : 无
*******************************************************************************/
void Ds1302Write(uchar addr, uchar dat)
{
uchar n;
RST = 0;
_nop_();
SCLK = 0;//先将SCLK置低电平。
_nop_();
RST = 1; //然后将RST(CE)置高电平。
_nop_();
for (n=0; n<8; n++)//开始传送八位地址命令
{
DSIO = addr & 0x01;//数据从低位开始传送
addr >>= 1;
SCLK = 1;//数据在上升沿时,DS1302读取数据
_nop_();
SCLK = 0;
_nop_();
}
for (n=0; n<8; n++)//写入8位数据
{
DSIO = dat & 0x01;
dat >>= 1;
SCLK = 1;//数据在上升沿时,DS1302读取数据
_nop_();
SCLK = 0;
_nop_();
}
RST = 0;//传送数据结束
_nop_();
}
/*******************************************************************************
* 函 数 名 : Ds1302Read
* 函数功能 : 读取一个地址的数据
* 输 入 : addr
* 输 出 : dat
*******************************************************************************/
uchar Ds1302Read(uchar addr)
{
uchar n,dat,dat1;
RST = 0;
_nop_();
SCLK = 0;//先将SCLK置低电平。
_nop_();
RST = 1;//然后将RST(CE)置高电平。
_nop_();
for(n=0; n<8; n++)//开始传送八位地址命令
{
DSIO = addr & 0x01;//数据从低位开始传送
addr >>= 1;
SCLK = 1;//数据在上升沿时,DS1302读取数据
_nop_();
SCLK = 0;//DS1302下降沿时,放置数据
_nop_();
}
_nop_();
for(n=0; n<8; n++)//读取8位数据
{
dat1 = DSIO;//从最低位开始接收
dat = (dat>>1) | (dat1<<7);
SCLK = 1;
_nop_();
SCLK = 0;//DS1302下降沿时,放置数据
_nop_();
}
RST = 0;
_nop_(); //以下为DS1302复位的稳定时间,必须的。
SCLK = 1;
_nop_();
DSIO = 0;
_nop_();
DSIO = 1;
_nop_();
return dat;
}
/*******************************************************************************
* 函 数 名 : Ds1302WriteTime
* 函数功能 : 写入时钟信息
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Ds1302WriteTime()
{
uchar n;
Ds1302Write(0x8E,0x00);
for (n=0; n<7; n++)//写入7个字节的时钟信号:秒分时日月周年
{
Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);
}
Ds1302Write(0x8E,0x80);
}
/*******************************************************************************
* 函 数 名 : Ds1302ReadTime
* 函数功能 : 读取时钟信息
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Ds1302ReadTime()
{
uchar n;
for (n=0; n<7; n++)//读取7个字节的时钟信号:分秒时日月周年
{
TIME[n] = Ds1302Read(READ_RTC_ADDR[n]);
}
}
ds1302.h
#ifndef __DS1302_H_
#define __DS1302_H_
//---包含头文件---//
#include<reg52.h>
#include<intrins.h>
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//---定义ds1302使用的IO口---//
sbit DSIO=P3^7;
sbit RST=P3^5;
sbit SCLK=P3^6;
//---DS1302时钟初始化2019年9月16日星期一12点00分00秒。---//
//---存储顺序是秒分时日月周年,存储格式是用BCD码---//
extern uchar TIME[7];
extern uchar code READ_RTC_ADDR[7] ;
extern uchar code WRITE_RTC_ADDR[7];
//---定义全局函数---//
void Ds1302Write(uchar addr, uchar dat); //向DS1302命令(地址+数据)
uchar Ds1302Read(uchar addr); //读取一个地址的数据
void Ds1302ReadTime(); //读取时钟信息
void Ds1302WriteTime(); //写入时钟信息
#endif