基于单片机和DS1302电子万学年历的设计.docx
《基于单片机和DS1302电子万学年历的设计.docx》由会员分享,可在线阅读,更多相关《基于单片机和DS1302电子万学年历的设计.docx(35页珍藏版)》请在冰豆网上搜索。
基于单片机和DS1302电子万学年历的设计
1DS1302实时时钟
(1)时钟芯片DS1302的工作原理:
DS1302在每次进行读、写程序前都必须初始化,先把SCLK端置“0”,接着把RST端置“1”,最后才给予SCLK脉冲;读/写时序如下图所示。
为DS1302的控制字,此控制字的位7必须置1,若为0则不能把对DS1302进行读写数据。
对于位6,若对程序进行读/写时RAM=1,对时间进行读/写时,CK=0。
位1至位5指操作单元的地址。
位0是读/写操作位,进行读操作时,该位为1;该位为0则表示进行的是写操作。
控制字节总是从最低位开始输入/输出的。
表6为DS1302的日历、时间寄存器内容:
“CH”是时钟暂停标志位,当该位为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位为0时,时钟开始运行。
“WP”是写保护位,在任何的对时钟和RAM的写操作之前,WP必须为0。
当“WP”为1时,写保护位防止对任一寄存器的写操作。
(2)DS1302的控制字节
DS1302的控制字如表-1所示。
控制字节的高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出
表-1DS1302的控制字格式
RAMRD
1A4A3A2A1A0
/CK/WR
(3)数据输入输出(I/O)
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。
同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
如下图1所示
图1DS1302读/写时序图
(4)DS1302的寄存器
DS1302有12个寄存器,其中有7个寄存器和日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字见表-2。
表-2DS1302的日历、时间寄存器
写寄存器
读寄存器
Bit7
Bit6
Bit5
Bit4
Bit3
Bit2
Bit1
Bit0
80H
81H
CH
10秒
秒
82H
83H
10分
分
84H
85H
12/
0
10
时
时
/PM
86H
87H
0
0
10日
日
88H
89H
0
0
0
10月
月
8AH
8BH
0
0
0
0
0
星期
8CH
8DH
10年
年
8EH
8FH
WP
0
0
0
0
0
0
0
此外,DS1302还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及和RAM相关的寄存器等。
时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。
DS1302和RAM相关的寄存器分为两类:
一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。
2方案论证和设计
2.1控制器部分方案设计
硬件控制电路主要用了AT89C52芯片处理器、1602LCD显示器、DS1302实时时钟、DB18B20温度传感器。
根据各自芯片的功能互相连接成电子万年历的控制电路。
软件控制程序主要有主控程序、电子万年历的时间控制程序、时间显示及温度显示程序等组成。
设计框图如图2.1所示
图2.1框图设计
2.2显示部分的方案论证
方案一:
采用8段数码管虽经济实惠,但操作比液晶显示来说略显繁琐。
方案二:
液晶显示方式。
液晶显示效果出众,可以运用菜单项来方便操作,比较简单,所以,最后选择液晶显示方案。
显示电路图如图2.2所示。
图2.2液晶显示电路
2.3实时时钟电路设计
图2.3是DS1302和单片机的连接,其中Vcc1为后备电源,Vcc2为主电源。
在主电源关闭的情况下,也能保持时钟的连续运行。
DS1302由Vcc1或Vcc2两者中的较大者供电。
当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。
当Vcc2小于Vcc1时,DS1302由Vcc1供电。
X1和X2是振荡源,外接32.768KHz晶振。
图2.3DS1302时钟电路
2.4温度采集模块设计
如图2.4所示,采用数字式温度传感器DS18B20,它是数字式温度传感器,具有测量精度高,电路连接简单特点,此类传感器仅需要一条数据线进行数据传输,使用P3.7和DS18B20的I/O口连接加一个上拉电阻,Vcc接电源,Vss接地。
图2.4DS18B20温度采集模块
2.5功能按钮设计
当按钮被按下时,该按钮对应的I/O口被拉为低电平,松开时按钮对应的I/O口由内部的上拉电阻将该I/O拉为高电平,如图2.5所示:
图2.5键盘电路设计
2.6总体电路图
3软件设计流程
3.1系统总流程图
系统总体流程图如图3.1所示,系统流程图设计分析如下:
首系统初始化,系统运行,当有设置键按下时进入时间修改模式,无按键按下时读取时间温度等数据送入显示器显示。
在时间修改模式下设置时间完成后再将时间送入显示器显示。
图3.1系统流程图
3.2温度程序流程图
温度读取程序流程图如图3.2所示。
流程图分析:
开始进入初始化DS18B20,就是通过主机拉低单线产生复位脉冲然后释放总线,如果有应答就发起ROM命令,当成功执行操作命令后,就使用温度转换,当温度转换完成后又初始化DS18B20是否有应答脉冲,若有就发起读暂存器和CRC命令,同时读出第一第二字节即温度数据。
图3.2温度读取程序流程图
3.3DS1302时钟程序流程图
时钟流程图如图3.3所示,流程图分析:
DS1302开始计时时,首先进行初始化,当有中断信号时,读取时钟芯片的时间数据送入液晶显示。
这时若有设置键按下,进行时间修改,完成后将时间数据送入1302芯片,若没有按键按下,直接送入EPROM中,送入液晶显示。
图3.3时钟程序流程图
DS1302时钟部分子程序
voidwrite_byte(uchardat)//写一个字节
{
ACC=dat;
RST=1;
for(a=8;a>0;a--)
{
IO=ACC0;
SCLK=0;//产生上升沿写入数据,从低位写入
SCLK=1;
ACC=ACC>>1;
}
}
ucharread_byte()//读一个字节
{
RST=1;
for(a=8;a>0;a--)
{
ACC7=IO;
SCLK=1;//产生下降沿输出数据,先输出低位,保存到ACC中
SCLK=0;
ACC=ACC>>1;
}
return(ACC);
}
voidwrite_1302(ucharadd,uchardat)//向1302芯片写函数,指定写入地址,数据
{
RST=0;
SCLK=0;
RST=1;
write_byte(add);
write_byte(dat);
SCLK=1;
RST=0;
}
ucharread_1302(ucharadd)//从1302读数据函数,指定读取数据来源地址
{
uchartemp;
RST=0;
SCLK=0;
RST=1;
write_byte(add);
temp=read_byte();
SCLK=1;
RST=0;
return(temp);
}
ucharBCD_Decimal(ucharbcd)//BCD码转十进制函数,输入BCD,返回十进制
{
ucharDecimal;
Decimal=bcd>>4;
return(Decimal=Decimal*10+(bcd&=0x0F));
}
voidds1302_init()//1302芯片初始化子函数(2012-06-18,12:
00:
00,week7)
{
RST=0;
SCLK=0;
write_1302(0x8e,0x00);//允许写,禁止写保护
write_1302(0x80,0x00);//向DS1302内写秒寄存器80H写入初始秒数据00
write_1302(0x82,0x00);//向DS1302内写分寄存器82H写入初始分数据00
write_1302(0x84,0x12);//向DS1302内写小时寄存器84H写入初始小时数据12
write_1302(0x8a,0x07);//向DS1302内写周寄存器8aH写入初始周数据4
write_1302(0x86,0x17);//向DS1302内写日期寄存器86H写入初始日期数据18
write_1302(0x88,0x06);//向DS1302内写月份寄存器88H写入初始月份数据06
write_1302(0x8c,0x12);//向DS1302内写年份寄存器8cH写入初始年份数据12
write_1302(0x8e,0x80);//打开写保护
}
3.4LCD显示程序流程图
显示流程图如图3.4所示,流程图分析如下:
首先对1602显示屏进行初始化(初始化大约持续10ms),然后检查忙信号,若BF=0,则获得显示RAM地址,写入相应的数据显示。
若BF=1,则代表模块正在进行内部操作,不接受任何外部指令和数据,直到BF=0为止。
图3.4LCD显示程序流程图
4万年历的仿真和调试结果
上电后的显示
秒调节
功能按键,自上而下功能依次为调节按键、加按键、减按键
5心得体会
本次设计是我们遇到过的较大的设计,所以遇到的问题也比较的多,尤其是以前没有接触过如此复杂的硬件电路以及软件编程,在软、硬件设计和调试中遇到了不少的困难,在同学的帮助才逐一克服了难题,学习到了不少的专业知识。
在整个设计过程之前,我已经在网上找了相关方面的资料,万事开始难,一开始不知道从哪里下手。
后来慢慢学会分析系统,将系统模块化,各个模块可以在软件或者硬件上实现。
在确保各个模块的硬件电路和和之相搭配的程序能够正常工作后在把它们组成一个系统。
在今后的日子里,我会进一步加强自己的动手能力,丰富自己的知识面。
参考文献
[1]李朝青.单片机原理及接口技术[M],北京:
北京航天航空大学出版社,2005
[2]李广弟.单片机基础[M],北京:
北京航空航天大学出版社,2000
[3]万光毅.单片机实验和实践教程[M],北京:
北京航空航天大学出版社,2003
[4]唐亚平、李移伦.单片机原理实训和学习指导[M],长沙:
中南大学出版社,2006
[5]刘军.单片机原理和接口技术[M],华东理工大学出版社,2006
[6]谢自美.电子线路设计、实验、测试[M],武汉:
华中理工大学出版社,2000
附件
万年历源程序
#include
#include"DS18B20_3.H"
#defineuintunsignedint
#defineucharunsignedchar
uchara,miao,shi,fen,ri,yue,nian,week,flag,key1n,temp;
//flag用于读取头文件中的温度值,和显示温度值
#defineyh0x80//LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80)
#defineer0x80+0x40//LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)
//液晶屏的和C51之间的引脚连接定义(显示数据线接C51的P0口)
sbitrs=P2^6;//寄存器选择
sbiten=P2^7;//下降沿使能
sbitrw=P2^5;//读写信号线
//DS1302时钟芯片和C51之间的引脚连接定义
sbitIO=P3^4;//数据线
sbitSCLK=P3^6;
sbitRST=P3^5;
sbitACC0=ACC^0;
sbitACC7=ACC^7;
ACC累加器=A
ACC.0=E0H
//校时按键和C51的引脚连接定义
sbitkey1=P2^0;//设置键
sbitkey2=P2^1;//加键
sbitkey3=P2^2;//减键
sbitbuzzer=P1^5;//蜂鸣器,端口低电平响
ucharcodetab1[]={"20--"};//年显示的固定字符
ucharcodetab2[]={":
:
"};//时间显示的固定字符
//延时函数,后面经常调用
voiddelay(uintxms)//延时函数,有参函数
{
uintx,y;
for(x=xms;x>0;x--)
for(y=120;y>0;y--);
}
voidwrite_1602com(ucharcom)//****液晶写入指令函数****
{
rs=0;//数据/指令选择置为指令
rw=0;//读写选择置为写
P0=com;//送入数据
delay
(1);
en=1;//拉高使能端,为制造有效的下降沿做准备
delay
(1);
en=0;//en由高变低,产生下降沿,液晶执行命令
}
voidwrite_1602dat(uchardat)//***液晶写入数据函数****
{
rs=1;//数据/指令选择置为数据
rw=0;//读写选择置为写
P0=dat;//送入数据
delay
(1);
en=1;//en置高电平,为制造下降沿做准备
delay
(1);
en=0;//en由高变低,产生下降沿,液晶执行命令
}
voidlcd_init()//***液晶初始化函数****
{
write_1602com(0x38);//设置液晶工作模式,意思:
16*2行显示,5*7点阵,8位数据
write_1602com(0x0c);//开显示不显示光标
write_1602com(0x06);//整屏不移动,光标自动右移
write_1602com(0x01);//清显示
write_1602com(yh+1);//日历显示固定符号从第一行第1个位置之后开始显示
for(a=0;a<14;a++)
{
write_1602dat(tab1[a]);//向液晶屏写日历显示的固定符号部分
//delay(3);
}
write_1602com(er+2);//时间显示固定符号写入位置,从第2个位置后开始显示
for(a=0;a<8;a++)
{
write_1602dat(tab2[a]);//写显示时间固定符号,两个冒号
//delay(3);
}
}
voidwrite_byte(uchardat)//写一个字节
{
ACC=dat;
RST=1;
for(a=8;a>0;a--)
{
IO=ACC0;
SCLK=0;//产生上升沿写入数据,从低位写入
SCLK=1;
ACC=ACC>>1;
}
}
ucharread_byte()//读一个字节
{
RST=1;
for(a=8;a>0;a--)
{
ACC7=IO;
SCLK=1;//产生下降沿输出数据,先输出低位,保存到ACC中
SCLK=0;
ACC=ACC>>1;
}
return(ACC);
}
voidwrite_1302(ucharadd,uchardat)//向1302芯片写函数,指定写入地址,数据
{
RST=0;
SCLK=0;
RST=1;
write_byte(add);
write_byte(dat);
SCLK=1;
RST=0;
}
ucharread_1302(ucharadd)//从1302读数据函数,指定读取数据来源地址
{
uchartemp;
RST=0;
SCLK=0;
RST=1;
write_byte(add);
temp=read_byte();
SCLK=1;
RST=0;
return(temp);
}
ucharBCD_Decimal(ucharbcd)//BCD码转十进制函数,输入BCD,返回十进制
{
ucharDecimal;
Decimal=bcd>>4;
return(Decimal=Decimal*10+(bcd&=0x0F));
}
voidds1302_init()//1302芯片初始化子函数(2012-06-18,12:
00:
00,week7)
{
RST=0;
SCLK=0;
write_1302(0x8e,0x00);//允许写,禁止写保护
write_1302(0x80,0x00);//向DS1302内写秒寄存器80H写入初始秒数据00
write_1302(0x82,0x00);//向DS1302内写分寄存器82H写入初始分数据00
write_1302(0x84,0x12);//向DS1302内写小时寄存器84H写入初始小时数据12
write_1302(0x8a,0x07);//向DS1302内写周寄存器8aH写入初始周数据4
write_1302(0x86,0x17);//向DS1302内写日期寄存器86H写入初始日期数据18
write_1302(0x88,0x06);//向DS1302内写月份寄存器88H写入初始月份数据06
write_1302(0x8c,0x12);//向DS1302内写年份寄存器8cH写入初始年份数据12
write_1302(0x8e,0x80);//打开写保护
}
//温度显示子函数
voidwrite_temp(ucharadd,uchardat)//向LCD写温度数据,并指定显示位置
{
uchargw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(er+add);//er是头文件规定的值0x80+0x40
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码
write_1602dat(0x43);//显示"C"符号,0x43是液晶屏字符库里大写C的地址码
}
//时分秒显示子函数
voidwrite_sfm(ucharadd,uchardat)//向LCD写时分秒,有显示位置加、现示数据,两个参数
{
uchargw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(er+add);//er是头文件规定的值0x80+0x40
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
}
//年月日显示子函数
voidwrite_nyr(ucharadd,uchardat)//向LCD写年月日,有显示位置加数、显示数据,两个参数
{
uchargw,sw;
gw=dat%10;//取得个位数字
sw=dat/10;//取得十位数字
write_1602com(yh+add);//设定显示位置为第一个位置+add
write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码
}
voidwrite_week(ucharweek)//写星期函数
{
write_1602com(yh+0x0c);//星期字符的显示位置
switch(week)
{
case1:
write_1602dat('M');//星期数为1时,显示
write_1602dat('O');
write_1602dat('N');
break;
case2:
write_1602dat('T');//星期数据为2时显示
write_1602dat('U');
write_1602dat('E');
break;
case3:
write_1602dat('W');//星期数据为3时显示
write_1602dat('E');
write_1602dat('D');
break;
case4:
write_1602dat('T');//星期数据为4是显示
write_1602dat('H');
write_1602dat('U');
break;
case5:
write_1602dat('F');//星期数据为5时显示
write_1602dat('R');
write_1602dat('I');
break;
case6:
write_1602dat('S');//星期数据为6时显示
write_1602dat('T');
write_1602dat('A');
break;
case7:
write_1602dat('S');//星期数据为7时显示
write_1602dat('U');
write_1602dat('N');
break;
}
}
//****************键盘扫描有关函数**********************
voidkeyscan()
{
if(key1==0)//---------------key1为功能键(设置键)--------------------
{
delay(9);//延时,用于消抖动
if(key1==0)//延时后再次确认按键按下
{
buzzer=0;//蜂鸣器短响一次
delay(20);
buzzer=1;
while(!
key1);
key1n++;
if(key1n==9)
key1n=1;//设置按键共有秒、分、时、星期、日、月、年、返回,8个功能循环
switch(key1n)
{
case1:
TR0=0;//关闭定时器
//TR1=0;
write_1602com(er+0x09);//设置按键按动一次,秒位置显示光标
write_1602com(0x0f);//设置光标为闪烁
temp=(miao)/10*16+(miao)%10;//秒数据写入DS1302
write_1302(0x8e,0x00);
write_1302(0x80,0x80|temp);//miao
write_1302(0x8e,0x80);
break;
case2:
write_1602com(er+6);//按2次fen位置显示光标
//write_1602com(0x0f);
break;