236 显示模块的设计
本设计中由于要对时间、温度进行显示,所以选择液晶显示屏1602模块作为输出。1602字符型LCD通常有14条引脚线或16条引脚线的LCD,多出来的2条线是背光电源线。它可以显示两行,每行16个字符,采用单+5V电源供电,外围电路配置简单,价格便宜,具有很高的性价比。1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。管脚功能如表2-1所示。
表2-1 LCD1602引脚功能
引脚 符号 功能说明
1 VSS 一般接地
2 VDD 接电源(+5V)
3 V0 液晶显示器对比度调整端。
4 RS RS为寄存器选择。
5 R/W R/W为读写信号线。
6 E E(或EN)端为使能(enable)端,下降沿使能。
7 DB0 低4位三态、 双向数据总线 0位(最低位)
8 DB1 低4位三态、 双向数据总线 1位
9 DB2 低4位三态、 双向数据总线 2位
10 DB3 低4位三态、 双向数据总线 3位
11 DB4 高4位三态、 双向数据总线 4位
12 DB5 高4位三态、 双向数据总线 5位
13 DB6 高4位三态、 双向数据总线 6位
14 DB7 高4位三态、 双向数据总线 7位(最高位)
LCD1602主要管脚介绍:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生鬼影使用时可以通过一个10K的电位器调整对比度。RS为寄存器选择端,高电平时选择数据寄存器,低电平时选择指令寄存器。R/W为读写信号线端,高电平时进行读 *** 作,低电平时进行写 *** 作。当RS和R/W共同为低电平时可以写入指令或者显示地址;当RS为高电平R/W 为低电平时可以写入数据。E为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
将LCD1602的RS端和P20,R/W端和P21, E 端和P22相连,当RS=0时,对LCD1602写入指令;当RS=1时,对LCD1602写入数据。当R/W端接高电平时芯片处于读数据状态,反之处于写数据状态,E端为使能信号端。当R/W为高电平,E端也为高电平,RS为低电平时,液晶显示屏显示需要显示的示数。图211为1602液晶显示屏与单片机的硬件连接图。
图211 LCD液晶显示与单片机硬件连线图
附录二 部分程序说明
void delay(uint xms) //延时函数,有参函数//
{ uint x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
//ds18b20芯片有关子程序/
void DS18B20_init() //初始化
{ uint i; tem_ds=0;
i=160; while(i>0) //给DS18B20单总线至少480uS的低电平信号
i--; tem_ds=1; //拉高15~60uS
i=8; while(i>0) i--;
}
void temwritebit(bit instruc_data) //写一位//
{ int time;
if(instruc_data)
{ tem_ds=0;
time=3; //延时14us
while(time>0)
time--;
tem_ds=1; //写"1"
time=8; //延时35us
while(time>0)
time--;
}
else
{
tem_ds=0; //写0
time=14; //延时62us
while(time>0)
time--;
}
tem_ds=1; //释放数据线
time++;time++;
}
bit temreadbit() //读一位//
{ uint i;
bit datbit;
tem_ds=0;
i++;
tem_ds=1;
i++;i++;
datbit=tem_ds; //读数据
i=10; //延时45
while(i>0)
i--;
tem_ds=1;
return(datbit);
}
uchar temreadbyte() //读字节//
{ uchar i,j,dat;
dat=0;
for(i=0;i<8;i++)
{ j=temreadbit() ;
dat=(j<<7)|(dat>>1);
}
return(dat);
}
void temwritebyte(uchar instru) //写字节//
{ int i;
for(i=0;i<8;i++)
{temwritebit(instru&01);
instru>>=1;
}
}
uint get_tem() //获取温度//
{ uchar tem_L,tem_H;
DS18B20_init();
delay(1);
temwritebyte(0xcc); //写跳过ROM指令;
temwritebyte(0xbe); //读数据
tem_L=temreadbyte();
tem_H=temreadbyte();
tem=tem_H<<8|tem_L;
f_tem=tem00625;
tem=f_tem ;
return (tem);
}
//液晶相关程序/
write_1602com(uchar com) //液晶写入指令函数//
{ rs=0; //数据/指令选择置为指令
rw=0; //读写选择置为写
P0=com; //送入数据
delay(1);
en=1; //拉高使能端,为制造有效的下降沿做准备
delay(1);
en=0; //en由高变低,产生下降沿,液晶执行命令
}
write_1602dat(uchar dat) //液晶写入数据函数//
{ rs=1; //数据指令选择置为数据
rw=0; //读写选择置为写
P0=dat; //送入数据
delay(1);
en=1; //en置高电平,为制造下降沿做准备
delay(1);
en=0; //en由高变低,产生下降沿,液晶执行命令
}
lcd_init() //液晶初始化函数//
{ write_1602com(0x38); //设置液晶工作模式 意思:162行显示,57点阵,8位数据
write_1602com(0x01); //清显示
write_1602com(0x06); //整屏不移动,光标自动右移
write_1602com(yh+1); //日历显示固定符号从第一行第1个位置之后开始显示
write_1602com(0x0c); //开显示不显示光标
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);
}
}
/DS1302有关子函数/
void write_byte(uchar dat) //写一个字节//
{ ACC=dat;
RST=1;
for(a=8;a>0;a--)
{ IO=ACC0;
SCLK=0; /在控制指令字输入后的下一个SCLK时钟的上升沿时/
SCLK=1; /数据被写入DS1302/
ACC=ACC>>1;
}
}
uchar read_byte() //读一个字节//
{ RST=1;
for(a=8;a>0;a--)
{ ACC7=IO;
SCLK=1;
SCLK=0;
ACC=ACC>>1;
}
return (ACC);
}
void write_1302(uchar add,uchar dat) //向1302芯片写函数,指定写入地址,数据//
{ RST=0;
SCLK=0;
RST=1;
write_byte(add);
write_byte(dat);
SCLK=1;
RST=0;
}
uchar read_1302(uchar add) //从1302读数据函数,指定读取数据来源地址//
{ uchar temp;
RST=0;
SCLK=0;
RST=1;
write_byte(add);
temp=read_byte();
SCLK=1;
RST=0;
return(temp);
}
uchar BCD_Decimal(uchar bcd) //BCD码转十进制函数//
{ uchar Decimal;
Decimal=bcd>>4; //BCD高四位表示十位,低四位个位,8421码;
return(Decimal=Decimal10+(bcd&=0x0F));
}
void ds1302_init() //1302芯片初始化子函数(2010-01-07,12:00:00,week4)//
{ 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,0x04); //向DS1302内写周寄存器8aH写入初始周数据4
write_1302(0x86,0x07); //向DS1302内写日期寄存器86H写入初始日期数据07
write_1302(0x88,0x01); //向DS1302内写月份寄存器88H写入初始月份数据01
write_1302(0x8c,0x10); //向DS1302内写年份寄存器8cH写入初始年份数据10
write_1302(0x8e,0x80); //打开写保护
}
void write_temp(uchar add,uchar dat) //向LCD写温度数据,并指定显示位置//
{ uchar gw,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的地址码
}
void write_sfm(uchar add,uchar dat) //向LCD写时分秒,有显示位置加显示数据,两个参数
{ uchar gw,sw;
gw=dat%10; //取得个位数字
sw=dat/10; //取得十位数字
write_1602com(er+add); //er是头文件规定的值0x80+0x40//年月日显示将er改为yh即可,其他相同;
write_1602dat(0x30+sw); //数字+30得到该数字的LCD1602显示码
write_1602dat(0x30+gw); //数字+30得到该数字的LCD1602显示码
}
void write_week(uchar week) //写星期函数//
{ write_1602com(yh+0x0c); //星期字符的显示位置
switch(week)
{ case 1:write_1602dat('M'); //星期数为1时,显示
write_1602dat('O');
write_1602dat('N');
break; //下面六种选择形式相同将括号中的字符相应修改就行了
}
}
void keyscan() //键盘扫描有关函数
{ 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)
{ case 1: TR0=0; //关闭定时器
write_1602com(er+0x09); //设置按键按动一次,秒位置显示光标
write_1602com(0x0f); //设置光标为闪烁
temp=(miao)/1016+(miao)%10; //秒数据写入DS1302
write_1302(0x8e,0x00);
write_1302(0x80,0x80|temp); //miao
write_1302(0x8e,0x80);
break;
case 2: write_1602com(er+6); //按2次fen位置显示光标
write_1602com(0x0f); //下面case3~case7基本相同,改变地址就行了
case 8: write_1602com(0x0c); //按动到第8次,设置光标不闪烁
TR0=1;//打开定时器
temp=(miao)/1016+(miao)%10;
write_1302(0x8e,0x00);
write_1302(0x80,0x00|temp);//miao数据写入DS1302
write_1302(0x8e,0x80);
break;
}
}
}
//------------------------------加键key2----------------------------
if(key1n!=0) //当key1按下以下。再按以下键才有效(按键次数不等于零)
{if(key2==0) //上调键
{delay(10);
if(key2==0)
{buzzer=0; //蜂鸣器短响一次
delay(20);
buzzer=1;
while(!key2);
switch(key1n)
{ case 1:miao++; //设置键按动1次,调秒
if(miao==60)
miao=0;
write_sfm(0x08,miao);
temp=(miao)/1016+(miao)%10; //十进制转换成DS1302要求的BCD码
write_1302(0x8e,0x00); //允许写,禁止写保护
write_1302(0x80,temp); ;//向DS1302内写秒寄存器80H写入调整后的数据BCD码
write_1302(0x8e,0x80); //打开写保护
write_1602com(er+0x09); //因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位
break;
//下面case2~case7和case1相同将相应的地址改一下就行了。
}
}
}
下面的key3与加键key2形式基本相同;
}
}
void init() //定时器、计数器设置函数
{ TMOD=0x01; //指定定时/计数器的工作方式为1
TH0=0; //定时器T0的高四位=0
TL0=0; //定时器T0的低四位=0
EA=1; //系统允许有开放的中断
ET0=1; //允许T0中断
TR0=1; //开启中断,启动定时器
}
void main() //主函数
{ lcd_init(); //调用液晶屏初始化子函数
ds1302_init(); //调用DS1302时钟的初始化子函数
init(); //调用定时计数器的设置子函数
buzzer=0; //蜂鸣器长响一次
delay(80);
buzzer=1;
while(1) //无限循环下面的语句:
{keyscan();} //调用键盘扫描子函数
}
Void timer0() interrupt 1 //取得并显示日历和时间
{ TH0=0; //重复初始值
TL0=0;
DS18B20_init();
delay(1);
temwritebyte(0xcc); //写跳过ROM指令;
temwritebyte(0x44); //启动温度转换;
flag=get_tem(); //将18b20运行返回的函数结果送到变量flag中,用于显示
//读取秒时分周日月年七个数据(DS1302的读寄存器与写寄存器不一样):
miao = BCD_Decimal(read_1302(0x81));//下面分秒年月日形式相同,改变地址就可以了
//显示温度、秒、时、分数据:
write_temp(12,flag);//显示温度,从第二行第12个字符后开始显示
write_sfm(8,miao);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数)
write_sfm(5,fen);//分,从第二行第5个字符后开始显示
write_sfm(2,shi);//小时,从第二行第2个字符后开始显示
//显示日、月、年数据:
write_nyr(9,ri);//日期,从第二行第9个字符后开始显示
write_nyr(6,yue);//月份,从第二行第6个字符后开始显示
write_nyr(3,nian);//年,从第二行第3个字符后开始显示
write_week(week);
}
我的是1602显示的,很容易改成数码管的,给你参考一下。
#include<stc12c5ah>
#include<intrinsh>
#define uchar unsigned char
#define uint unsigned int
sbit lcrs=P2^2;//数据/命令
sbit lcwr=P2^1;//读/写
sbit lcden=P2^0;//使能
sbit SCLK = P2^5;//DS1302时钟口P10
sbit IO = P2^4; //DS1302数据口P11
sbit RS = P2^3; //DS1302片选口P12
sbit key1=P4^4;
sbit key2=P4^5;
sbit key3=P4^1;
sbit key4=P4^6;
//秒 分 时 日 月 星期 年
uchar init[] = {0x45, 0x40, 0x20, 0x018, 0x04, 0x03, 0x12};
uchar now[7];
uchar code table[]={48,49,50,51,52,53,54,55,56,57};
uchar dis[16];
uchar H;//定义小时
uchar Mi;//定义分钟
uchar S;//定义秒
uchar Y;//定义年
uchar Mo;//定义月
uchar D;//定义日期
uchar W;//定义星期
uchar key=0;
uchar bcdto(uchar temp);
void delayms(uint ms);
void DS1302_Initial();
void DS1302_SetTime(uchar p);
void DS1302_GetTime(uchar p);
////////////////////// 延时子程序/////////////////////////////
void delayms(uint ms) //在110592M晶振下,单周期指令的ms级延时
{
uint i;
while(ms--)
{
for(i = 0; i < 850; i++);
}
}
//////1062/////////
void ydelay(uchar x)
{
uint a,b;
for(a=x;a>0;a--)
for(b=10;b>0;b--);
}
void write_com(uchar com)
{
P0=com;
lcwr=0;
lcrs=0;
lcden=0;
ydelay(20);
lcden=1;
ydelay(20);
lcden=0;
lcwr=1;
}
void write_date(uchar date)//写数据
{
P0=date;
lcwr=0;
lcrs=1;
lcden=0;
ydelay(20);
lcden=1;
ydelay(20);
lcden=0;
lcwr=1;
}
void init1602()//初始化
{
write_com(0x38);//设置显示模式
ydelay(40);
write_com(0x0c);//开显示
ydelay(40);
write_com(0x06);//指针和光标自动加一
ydelay(40);
write_com(0x01);//清屏指令
ydelay(40);
}
void display(uchar a)//显示
{
uint i;
dis[0]='2';dis[1]='0';dis[2]=table[Y/10];dis[3]=table[Y%10];
dis[4]='';dis[5]=table[Mo/10];dis[6]=table[Mo%10];
dis[7]='';dis[8]=table[D/10];dis[9]=table[D%10];dis[10]=' ';
dis[11]='W';dis[12]='e';dis[13]='e';dis[14]=' ';
dis[15]=table[W%10];
if(a==3)
{dis[2]=' ';dis[3]=' ';}
else if(a==4)
{dis[5]=' ';dis[6]=' ';}
else if(a==5)
{dis[8]=' ';dis[9]=' ';}
else if(a==6)
{dis[15]=' ';}
write_com(0x0c);
write_com(0xc0);
for(i=0;i<16;i++)
write_date(dis[i]);
dis[0]=table[H/10];dis[1]=table[H%10];dis[2]=':';
dis[3]=table[Mi/10]; dis[4]=table[Mi%10];dis[5]=':';
dis[6]=table[S/10];dis[7]=table[S%10];dis[8]=' ';
if(a==1)
{
dis[0]=' ';dis[1]=' ';
}
else if(a==2)
{dis[3]=' ';dis[4]=' ';}
write_com(0x80+4);
for(i=0;i<9;i++)
write_date(dis[i]);
}
uchar tobcd(uchar temp)//十进制转换成BCD码
{
uchar a,b,c;
a=temp;
b=0;
if(a>=10)
{
while(a>=10)
{
a=a-10;
b=b+16;
c=a+b;
temp=c;
}
}
return temp;
}
uchar bcdto(uchar temp)//BCD码转换成十进制
{
uchar a,b,c;
a=temp;
b=0;
if(a>=16)
{
while(a>=16)
{
a=a-16;
b=b+10;
c=a+b;
temp=c;
}
}
return temp;
}
void keyf(uchar num,uchar up,uchar du)
{
uint z;
if(key2==0)
{
delayms(10);
if(key2==0)
{
if(num>=up)
{
if((key==4)||(key==5)||(key==6))
num=1;
else
num=0;
}
else
num=num+1;
for(z=0;z<50;z++)
{
display(0);
if(key2!=0)
break;
}
while(!key2)
{
for(z=0;z<25;z++)
{display(0);}
if(num>=up)
{
if((key==4)||(key==5)||(key==6))
num=1;
else
num=0;
}
else
num=num+1;
}
}
}
if(key3==0)
{
delayms(10);
if(key3==0)
{
if(num==du)
{
if(key==1)
num=23;
else if(key==2)
num=59;
else if(key==3)
num=99;
else if(key==4)
num=12;
else if(key==5)
num=31;
else if(key==6)
num=7;
}
else
num=num-1;
for(z=0;z<50;z++)
{
display(0);
if(key3!=0)
break;
}
while(!key3)
{
for(z=0;z<25;z++)
{display(0);}
if(num==du)
{
if(key==1)
num=23;
else if(key==2)
num=59;
else if(key==3)
num=99;
else if(key==4)
num=12;
else if(key==5)
num=31;
else if(key==6)
num=7;
}
else
num=num-1;
}
}
}
}
void keyjc()
{
uint i,j;
if(key1==0)
delayms(10);
if(key1==0)
{
key++;
if(key>=7)
{
key=0;
display(0);
}
while(!key1);
if(key==1)
{
for(i=0;i<25;i++)
{
for(j=0;j<25;j++)
display(1);
for(j=0;j<25;j++)
display(0);
if((key2==0)||(key3==0))
{
delayms(10);
if((key2==0)||(key3==0))
{
i=0;
keyf(&H,23,0);
}
}
if(key1==0)
{
delayms(10);
if(key1==0)
{
key=2;
while(!key1);
break;
}
}
}
}
if(key==2)
{
for(i=0;i<25;i++)
{
for(j=0;j<25;j++)
display(2);
for(j=0;j<25;j++)
display(0);
if((key2==0)||(key3==0))
{
delayms(10);
if((key2==0)||(key3==0))
{
i=0;
keyf(&Mi,59,0);
}
}
if(key1==0)
{
delayms(10);
if(key1==0)
{
key=3;
while(!key1);
break;
}
}
}
}
if(key==3)
{
for(i=0;i<25;i++)
{
for(j=0;j<25;j++)
display(3);
for(j=0;j<25;j++)
display(0);
if((key2==0)||(key3==0))
{
delayms(10);
if((key2==0)||(key3==0))
{
i=0;
keyf(&Y,99,0);
}
}
if(key1==0)
{
delayms(10);
if(key1==0)
{
key=4;
while(!key1);
break;
}
}
}
}
if(key==4)
{
for(i=0;i<25;i++)
{
for(j=0;j<25;j++)
display(4);
for(j=0;j<25;j++)
display(0);
if((key2==0)||(key3==0))
{
delayms(10);
if((key2==0)||(key3==0))
{
i=0;
keyf(&Mo,12,1);
}
}
if(key1==0)
{
delayms(10);
if(key1==0)
{
key=5;
while(!key1);
break;
}
}
}
}
if(key==5)
{
for(i=0;i<25;i++)
{
for(j=0;j<25;j++)
display(5);
for(j=0;j<25;j++)
display(0);
if((key2==0)||(key3==0))
{
delayms(10);
if((key2==0)||(key3==0))
{
i=0;
keyf(&D,31,1);
}
}
if(key1==0)
{
delayms(10);
if(key1==0)
{
key=6;
while(!key1);
break;
}
}
}
}
if(key==6)
{
for(i=0;i<25;i++)
{
for(j=0;j<25;j++)
display(6);
for(j=0;j<25;j++)
display(0);
if((key2==0)||(key3==0))
{
delayms(10);
if((key2==0)||(key3==0))
{
i=0;
keyf(&W,7,1);
}
}
if(key1==0)
{
delayms(10);
if(key1==0)
{
key=0;
while(!key1);
break;
}
}
}
}
init[0]=tobcd(S);
init[1]=tobcd(Mi);
init[2]=tobcd(H);
init[3]=tobcd(D);
init[4]=tobcd(Mo);
init[5]=tobcd(W);
init[6]=tobcd(Y);
DS1302_SetTime(init);
key=0;
}
}
void main()
{
init1602();
DS1302_Initial(); //初始化DS1302
P4SW=0x70;
while (1)
{
delayms(10);
DS1302_GetTime(now);//读取当前时间
Y=bcdto(now[6]);
Mo=bcdto(now[4]);
D=bcdto(now[3]);
W=bcdto(now[5]);
if(W==7)
W=8;
H=bcdto(now[2]);
Mi=bcdto(now[1]);
S=bcdto(now[0]);
display(0);
keyjc();
}
}
/
延时X微秒(STC12C5A60S2@12M)
不同的工作环境,需要调整此函数
此延时函数是使用1T的指令周期进行计算,与传统的12T的MCU不同
/
void Delay()
{
_nop_();
_nop_();
}
/
从DS1302读1字节数据
/
uchar DS1302_ReadByte()
{
uchar i;
uchar dat = 0;
for (i=0; i<8; i++) //8位计数器
{
SCLK = 0; //时钟线拉低
Delay(); //延时等待
dat >>= 1; //数据右移一位
if (IO) dat |= 0x80; //读取数据
SCLK = 1; //时钟线拉高
Delay(); //延时等待
}
return dat;
}
/
向DS1302写1字节数据
/
void DS1302_WriteByte(uchar dat)
{
char i;
for (i=0; i<8; i++) //8位计数器
{
SCLK = 0; //时钟线拉低
Delay(); //延时等待
dat >>= 1; //移出数据
IO = CY; //送出到端口
SCLK = 1; //时钟线拉高
Delay(); //延时等待
}
}
/
读DS1302某地址的的数据
/
uchar DS1302_ReadData(uchar addr)
{
uchar dat;
RS = 0;
Delay();
SCLK = 0;
Delay();
RS = 1;
Delay();
DS1302_WriteByte(addr); //写地址
dat = DS1302_ReadByte(); //读数据
SCLK = 1;
RS = 0;
return dat;
}
/
往DS1302的某个地址写入数据
/
void DS1302_WriteData(uchar addr, uchar dat)
{
RS = 0;
Delay();
SCLK = 0;
Delay();
RS = 1;
Delay();
DS1302_WriteByte(addr); //写地址
DS1302_WriteByte(dat); //写数据
SCLK = 1;
RS = 0;
}
/
写入初始时间
/
void DS1302_SetTime(uchar p)
{
uchar addr = 0x80;
uchar n = 7;
DS1302_WriteData(0x8e, 0x00); //允许写 *** 作
while (n--)
{
DS1302_WriteData(addr, p++);
addr += 2;
}
DS1302_WriteData(0x8e, 0x80); //写保护
}
/
读取当前时间
/
void DS1302_GetTime(uchar p)
{
uchar addr = 0x81;
uchar n = 7;
while (n--)
{
p++ = DS1302_ReadData(addr);
addr += 2;
}
}
/
初始化DS1302
/
void DS1302_Initial()
{
RS = 0;
SCLK = 0;
DS1302_WriteData(0x8e, 0x00);
DS1302_WriteData(0x80, 0x00);
DS1302_WriteData(0x90, 0xaa);
DS1302_WriteData(0x8e, 0x80);
}
现在有很多流行的串行时钟芯片,如DS1302,DS1307,PCF8485等,由于简单的接口,低成本和易用性,他们被广泛应用于电话、传真、便携式仪器等产品领域。在本实验中,我们将使用DS1302实时时钟(RTC)模块获取当前日期和时间。
DS1302可以用于数据记录,特别是对某些具有特殊意义的数据点的记录,能实现数据与出现该数据的时间同时记录。这种记录对长时间的连续测控系统结果的分析,及对异常数据出现的原因的查找具有重要意义。
传统的数据记录方式是隔时采样或定时采样,没有具体的时间记录,因此,只能记录数据而无法准确记录其出现的时间;若采用单片机计时,一方面需要采用计数器,占用硬件资源,另一方面需要设置中断、查询等,同样耗费单片机的资源,而且,某些测控系统可能不允许。但是,如果在系统中采用时钟芯片DS1302,则能很好地解决这个问题。
★Raspberry Pi 3主板1
★树莓派电源1
★40P软排线1
★DS1302实时时钟模块1
★面包板1
★跳线若干
DS1302是DALLAS(达拉斯)公司出的一款涓流充电时钟芯片,2001年DALLAS被MAXIM(美信)收购。
DS1302实时时钟芯片广泛应用于电话、传真、便携式仪器等产品领域,他的主要性能指标如下:
1、DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有软年自动调整的能力,可以通过配置AM/PM来决定采用24小时格式还是12小时格式。
2、拥有31字节数据存储RAM。
3、串行I/O通信方式,相对并行来说比较节省IO口的使用。
4、DS1302的工作电压比较宽,大概是20V~55V都可以正常工作。
5、DS1302这种时钟芯片功耗一般都很低,它在工作电压20V的时候,工作电流小于300nA。
6、DS1302共有8个引脚,有两种封装形式,一种是DIP-8封装,芯片宽度(不含引脚)是300mil,一种是SOP-8封装,有两 种宽度,一种是150mil,一种是208mil。我们看一下DS1302的引脚封装图:
7、当供电电压是5V的时候,兼容标准的TTL电平标准,这里的意思是,可以完美的和单片机进行通信。
8、由于DS1302是DS1202的升级版本,所以所有的功能都兼容DS1202。此外DS1302有两个电源输入,一个是主电源, 另外一个是备用电源,比如可以用电池或者大电容,这样是为了保证系统掉电的情况下,我们的时钟还会继续走。如果使用的是充电电池,还可以在正常工作时,设置充电功能,给我们的备用电池进行充电。
DS1302的特点第二条“拥有31字节数据存储RAM”,这是DS1302额外存在的资源。这31字节的RAM相当于一个存储器一样,我们编写单片机程序的时候,可以把我们想存储的数据存储在DS1302里边,需要的时候读出来,这块功能和EEPROM有点类似,相当于一个掉电丢失数据的“EEPROM”,如果我们的时钟电路加上备用电池,那么这31个字节的RAM就可以替代EEPROM的功能了。
DS1302一共有8个引脚,下边要根据引脚分布图和典型电路图来介绍一下每个引脚的功能:
DS1302的电路一个重点就是时钟电路,它所使用的晶振是一个32768k的晶振,晶振外部也不需要额外添加其他的电容或者电阻电路了。时钟的精度,首先取决于晶振的精度以及晶振的引脚负载电容。如果晶振不准或者负载电容过大过小,都会导致时钟误差过大。在这一切都搞定后,最终一个考虑因素是晶振的温漂。随着温度的变化,晶振往往精度会发生变化,因此,在实际的系统中,其中一种方法就是经常校对。比如我们所用的电脑的时钟,通常我们会设置一个选项“将计算机设置于internet时间同步”。选中这个选项后,一般可以过一段时间,我们的计算机就会和internet时间校准同步一次。
对DS1302的 *** 作就是对其内部寄存器的 *** 作,DS1302内部共有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式。此外,DS1302还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读/写除充电寄存器以外的寄存器。
DS1302的一条指令一个字节8位,其中第7位(即最高位)是固定1,这一位如果是0的话,那写进去是无效的。第6位是选择RAM还是CLOCK的,这里主要讲CLOCK时钟的使用,它的RAM功能我们不用,所以如果选择CLOCK功能,第6位是0,如果要用RAM,那第6位就是1。从第5到第1位,决定了寄存器的5位地址,而第0位是读写位,如果要写,这一位就是0,如果要读,这一位就是1。
DS1302时钟的寄存器,其中8个和时钟有关的,5位地址分别是00000一直到00111这8个地址,还有一个寄存器的地址是01000,这是涓流充电所用的寄存器,我们这里不讲。在DS1302的数据手册里的地址,直接把第7位、第6位和第0位值给出来了,所以指令就成了80H、81H那些了,最低位是1,那么表示读,最低位是0表示写。
寄存器一:最高位CH是一个时钟停止标志位。如果我们的时钟电路有备用电源部分,上电后,我们要先检测一下这一位,如果这一位是0,那说明我们的时钟在系统掉电后,由于备用电源的供给,时钟是持续正常运行的;如果这一位是1,那么说明我们的时钟在系统掉电后,时钟部分不工作了。若我们的Vcc1悬空或者是电池没电了,当我们下次重新上电时,读取这一位,那这一位就是1,我们可以通过这一位判断时钟在单片机系统掉电后是否持续运行。剩下的7位高3位是秒的十位,低4位是秒的个位,这里注意再提一次,DS1302内部是BCD码,而秒的十位最大是5,所以3个二进制位就够了。
寄存器二:bit7没意义,剩下的7位高3位是分钟的十位,低4位是分钟的个位。
寄存器三:bit7是1的话代表是12小时制,是0的话代表是24小时制,bit6固定是0,bit5在12小时制下0代表的是上午,1代表的是下午,在24小时制下和bit4一起代表了小时的十位,低4位代表的是小时的个位。
寄存器四:高2位固定是0,bit5和bit4是日期的十位,低4位是日期的个位。
寄存器五:高3位固定是0,bit4是月的十位,低4位是月的个位。
寄存器六:高5位固定是0,低3位代表了星期。
寄存器七:高4位代表了年的十位,低4位代表了年的个位。这里特别注意,这里的00到99年指的是2000年到2099年。
寄存器八:bit7是一个保护位,如果这一位是1,那么是禁止给任何其他的寄存器或者那31个字节的RAM写数据的。因此在写数据之前,这一位必须先写成0。
物理上,DS1302的通信接口由3个口线组成,即RST,SCLK,I/O。其中RST从低电平变成高电平启动一次数据传输过程,SCLK是时钟线,I/O是数据线。这个DS1302的通信线定义和SPI很像,事实上,DS1302的通信是SPI的变异种类,它用了SPI的通信时序,但是通信的时候没有完全按照SPI的规则来,下面我们介绍DS1302的变异SPI通信方式。
请注意数据是对时钟信号敏感的,而且一般数据是在下降沿写入,上升沿读出。平时SCLK保持低电平,当需要写命令或者写数据时,在时钟输出变为高电平之前先输出数据;当需要读数据时,在时钟输出变为高电平之前采样读取数据。
第1步: 连接电路。
第2步: DS1302的Python程序比较复杂,我们先编写一个模块ds1302py,在里面创建一个类DS1302(),在里面编写读取时钟信息等方法。
第3步: 编写实际控制程序,导入上面的模块ds1302。运行本文件,不断循环读取并打印时钟信息。
实验结果示例:
1302c
#include<DS1302h>
#include<keyh>
uchar bit_ser[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
uchar seven_seg[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
/时间显示/
void timer0_init(void) //T0初始化函数,用于时间的动态显示
{
TMOD = 0x21;
TL0 = (65536-5000) % 256;
TH0 = (65536-5000) / 256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
void timer0_isr(void) interrupt 1 //T0中断处理函数
{
char flag; //flag用于表示调整时闪烁的亮或灭
TR0 = 0;
TL0 = (65536-5000) % 256;
TH0 = (65536-5000) / 256;
TR0 = 1;
flag = x / 100 0xff; //设置闪烁标志,如果x大于100则flag为0xff,小于100则为0x00
x++;
if(x > 200)
x = 0;
switch(i)
{
case 0:
P2 = bit_ser[0];
if(setflag == 3) //根据setflag的值判断当前位是否需要闪烁
P0 = flag | seven_seg[dis_buffer[0]];
else
P0 = seven_seg[dis_buffer[0]];
break;
case 1:
P2 = bit_ser[1];
if(setflag == 3)
P0 =flag | seven_seg[dis_buffer[1]];
else
P0 =seven_seg[dis_buffer[1]];
break;
case 2:
P2 = bit_ser[2];
if(setflag == 2)
P0 =flag | seven_seg[dis_buffer[2]];
else
P0 =seven_seg[dis_buffer[2]];
break;
case 3:
P2 = bit_ser[3];
if(setflag == 2)
P0 =flag | seven_seg[dis_buffer[3]];
else
P0 =seven_seg[dis_buffer[3]];
break;
case 4:
P2 = bit_ser[4];
if(setflag == 1)
P0 =flag | seven_seg[dis_buffer[4]];
else
P0 =seven_seg[dis_buffer[4]];
break;
case 5:
P2 = bit_ser[5];
if(setflag == 1)
P0 =flag | seven_seg[dis_buffer[5]];
else
P0 =seven_seg[dis_buffer[5]];
break;
}
i++;
if(i >= 6)
{
i = 0;
if(j == 10)
{
j = 0;
if(setflag == 0)
DS1302_GetTime(&Time); //如果setflag是0,就从1302中读出时间,因为setflag不是0时,说明处于调整状态,不需要读时间
dis_buffer[5] = TimeSecond % 10; //把当前时间放入显示缓冲区
dis_buffer[4] = TimeSecond / 10;
dis_buffer[3] = TimeMinute % 10;
dis_buffer[2] = TimeMinute / 10;
dis_buffer[1] = TimeHour % 10;
dis_buffer[0] = TimeHour / 10;
}
j++;
}
}
void main()
{
Initial_DS1302(Time);
timer0_init();
while(1)
{
set_down();
timer_down();
up_down();
down_down();
beepflag_down();
if(setflag == 0 && TimeHour == romhour && TimeMinute == romminute && Beepflag == 1) //判断蜂鸣器是否要响
Beep = !Beep;
}
}
//keyc
#include<reg51h>
#define uchar unsigned char
#define uint unsigned int
uchar i = 0,j = 0,x = 0,setflag,flag_set,flag_timer; //setflag用来表示调整的位置,flag_set和flag_timer分别表示当前处于调整状态还是定时状态
SYSTEMTIME Time={0,20,15,3,30,6,10}; //系统时间的初始值2010年6月30日星期三,15时20分0秒
char dis_buffer[6]; //存放显示数据的缓冲区
sbit Beep_flag = P3^2; //蜂鸣器的接口
sbit key_timer = P3^4; //定时按钮
sbit key_set = P3^5; //调整按钮
sbit key_up = P3^6; //增加按钮
sbit key_down = P3^7; //减小按钮
char romhour,romminute,romsec; //分别存放定时的时,分,秒
bit Beepflag; //标记闹钟是否开启
//延时函数
void delays(uchar x)
{
while(x) x--;
}
//设置键的处理函数
void set()
{
setflag ++;
flag_set = 1;
if(setflag >= 4)
{
setflag = 0;
flag_set = 0;
Initial_DS1302(Time);
}
}
//定时间的处理函数
void timer()
{
setflag ++;
flag_timer = 1;
if(setflag == 1)
{
TimeHour = romhour;
TimeMinute = romminute;
TimeSecond = romsec;
}
else if(setflag >= 4)
{
setflag = 0;
flag_timer = 0;
romhour = TimeHour;
romminute = TimeMinute;
romsec = TimeSecond;
}
}
//增加键的处理函数
void up()
{
switch(setflag)
{
case 0:
break;
case 1:
TimeSecond ++;
if(TimeSecond >= 60)
TimeSecond = 0;
break;
case 2:
TimeMinute ++;
if(TimeMinute >= 60)
TimeMinute = 0;
break;
case 3:
TimeHour ++;
if(TimeHour >= 24)
TimeHour = 0;
break;
}
}
//减小键的处理函数
void down()
{
switch(setflag)
{
case 0:
break;
case 1:
TimeSecond --;
if(TimeSecond < 0)
TimeSecond = 59;
break;
case 2:
TimeMinute --;
if(TimeMinute < 0)
TimeMinute = 59;
break;
case 3:
TimeHour --;
if(TimeHour < 0)
TimeHour = 23;
break;
}
}
//设置键的扫描函数
void set_down()
{
if(key_set == 0 && flag_timer == 0)
{
delays(100);
if(key_set == 0)
{
set();
}
while(!key_set);
}
}
//定时键的扫描函数
void timer_down()
{
if(key_timer == 0 && flag_set == 0)
{
delays(100);
if(key_timer == 0)
{
timer();
}
while(!key_timer);
}
}
//增加键的扫描函数
void up_down()
{
if(key_up == 0 && setflag != 0)
{
delays(100);
if(key_up == 0)
{
up();
while(!key_up);
}
}
}
//减少键的处理函数
void down_down()
{
if(key_down == 0 && setflag != 0)
{
delays(100);
if(key_down == 0)
{
down();
while(!key_down);
}
}
}
//定时开关的扫描处理函数
void beepflag_down()
{
if(Beep_flag == 0)
{
delays(100);
{
Beepflag = !Beepflag;
while(!Beep_flag);
}
}
}
//ds1302h
#ifndef _REAL_TIMER_DS1302
#define _REAL_TIMER_DS1302
#include <REG51h>
sbit DS1302_CLK = P1^1; //实时时钟时钟线引脚
sbit DS1302_IO = P1^2; //实时时钟数据线引脚
sbit DS1302_RST = P1^3; //实时时钟复位线引脚
sbit ACC0 = ACC^0;
sbit ACC7 = ACC^7;
sbit Beep = P2^7;
typedef struct __SYSTEMTIME__
{ char Second;
char Minute;
char Hour;
char Week;
char Day;
char Month;
char Year;
}SYSTEMTIME; //定义的时间类型
#define AM(X) X
#define PM(X) (X+12) // 转成24小时制
#define DS1302_SECOND 0x80 //秒寄存器
#define DS1302_MINUTE 0x82 //分寄存器
#define DS1302_HOUR 0x84
#define DS1302_WEEK 0x8A
#define DS1302_DAY 0x86
#define DS1302_MONTH 0x88
#define DS1302_YEAR 0x8C
#define DS1302_RAM(X) (0xC0+(X)2) //用于计算 DS1302_RAM 地址的宏
void DS1302InputByte(unsigned char d) //实时时钟写入一字节(内部函数)
{ unsigned char i;
ACC = d;
for(i=8; i>0; i--)
{ DS1302_IO = ACC0; //相当于汇编中的 RRC
DS1302_CLK = 1;
DS1302_CLK = 0; //发一个高跳变到低的脉冲
ACC = ACC >> 1;
}
}
unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数)
{ unsigned char i;
for(i=8; i>0; i--)
{ ACC = ACC >>1; //相当于汇编中的 RRC
ACC7 = DS1302_IO;
DS1302_CLK = 1;
DS1302_CLK = 0; //发一个高跳变到低的脉冲
}
return(ACC);
}
void Write1302(unsigned char ucAddr, unsigned char ucDa)//ucAddr: DS1302地址, ucData: 要写的数据
{ DS1302_RST = 0;
DS1302_CLK = 0;
DS1302_RST = 1;
DS1302InputByte(ucAddr); // 地址,命令
DS1302InputByte(ucDa); // 写1Byte数据
DS1302_CLK = 1;
DS1302_RST = 0; //RST 0->1->0,CLK 0->1
}
unsigned char Read1302(unsigned char ucAddr) //读取DS1302某地址的数据
{ unsigned char ucData;
DS1302_RST = 0;
DS1302_CLK = 0;
DS1302_RST = 1; //enable
DS1302InputByte(ucAddr|0x01); // 地址,命令
ucData = DS1302OutputByte(); // 读1Byte数据
DS1302_CLK = 1; //RST 0->1->0,CLK 0->1
DS1302_RST = 0;
return(ucData);
}
void DS1302_SetProtect(bit flag) //是否写保护
{ if(flag)
Write1302(0x8E,0x80); //WP=1,不能写入
else
Write1302(0x8E,0x00);//WP=0,可以写入
}
void DS1302_SetTime(unsigned char Address, unsigned char Value) // 设置时间函数
{ DS1302_SetProtect(0);
Write1302(Address, ((Value/10)<<4 | (Value%10))); //高4位为十位,低4位为个位
DS1302_SetProtect(1);
}
//获取时间函数,从DS1302内读取时间然后存入Time内
void DS1302_GetTime(SYSTEMTIME Time)
{ unsigned char ReadValue;
ReadValue = Read1302(DS1302_SECOND);
Time->Second = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);//转换成10进制的秒
ReadValue = Read1302(DS1302_MINUTE);
Time->Minute = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_HOUR);
Time->Hour = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_DAY);
Time->Day = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_WEEK);
Time->Week = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_MONTH);
Time->Month = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);
ReadValue = Read1302(DS1302_YEAR);
Time->Year = ((ReadValue&0x70)>>4)10 + (ReadValue&0x0F);
}
//利用STime初始化DS1302
void Initial_DS1302(SYSTEMTIME STime)
{ unsigned char Second=Read1302(DS1302_SECOND);
if(Second&0x80) DS1302_SetTime(DS1302_SECOND,0); //如果第七为1(表明没有启动), 则启动时钟
DS1302_SetTime(DS1302_SECOND,STimeSecond); //设定起始时间
DS1302_SetTime(DS1302_MINUTE,STimeMinute);
DS1302_SetTime(DS1302_HOUR,STimeHour);
DS1302_SetTime(DS1302_DAY,STimeDay);
DS1302_SetTime(DS1302_MONTH,STimeMonth);
DS1302_SetTime(DS1302_YEAR,STimeYear);
DS1302_SetTime(DS1302_WEEK,STimeWeek);
}
#endif
以上就是关于时钟芯片DS1302调整时间的思路是怎么样的最好能给一个调整时间的程序,谢谢全部的内容,包括:时钟芯片DS1302调整时间的思路是怎么样的最好能给一个调整时间的程序,谢谢、有木有大佬帮我解释一下这段ds1302时钟芯片程序是什么意思、ds1302时钟芯片工作原理等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)