/**
程序功能:显示年月日,时分秒,星期,温度
*/
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
sbit RS=P2^5;//数据命令选择端
sbit RW=P2^6;//读写选择端
sbit E=P2^7;//控制使能端
sbit DQ=P1^0;
sbit RST=P2^4;//片选输入信号端
sbit IO=P2^3;//串行时钟输入端
sbit CLK=P2^2;//串行数据输入输出端
uchar str1[16];
uchar str2[16];
uchar str3[16];
uchar second,minute,hour,day,month,year,week,yearH=20;
sbit select=P3^2;//选择调整部分按钮
sbit up=P3^1;//加按钮
sbit down=P3^0;//减按钮
void jisuan();
void jisuan1();
void display();
void gettemp();
//temp:临时变量,用于保存要写入ds1302的内容
//count:调整部分计数,1:年,2:月,3:日,
// 4:时,5:分,6:秒,7:星期,8:退出调整
//done:调整标志,1:调整,0:不调整
//flag:时钟停止标志,1:走,0:停
//up_flag:加按键操作标志;down_flag:减按键操作标志
uchar temp=0x00,count=8,flag,done,up_flag,down_flag;
uchar t,flag_t;
void delay(uint x){//毫秒级的延时
uint i,j;
for(i=0;i<x;i++)
for(j=0;j<120;j++);
}
//////////////////1602操作////////////////////////////
uint checkBusy(){//检查忙标志
uchar temp;
P0=0xff;//I/O口准备
RS=0;//选择指令寄存器
RW=1;//选择读模式
E=1;//使能LCD
_nop_();_nop_();_nop_();_nop_();//延时
temp=P0&0x80;//取忙标志
E=0;//禁止LCD
if(temp!=0x00)return 1;//忙返回1
else return 0;//不忙返回0
}
void writeIR(uchar cmd){//写指令
while(checkBusy());//检查忙状态
RS=0;//选择指令模式
RW=0;//选择写模式
E=0;//禁止LCD
P0=cmd;//写入命令字
_nop_();_nop_();_nop_();_nop_();//延时
E=1;//使能LCD
_nop_();_nop_();_nop_();_nop_();//延时
E=0;//禁止LCD
}
void writecom(uchar com)
{
checkBusy();
RS=0;
RW=0;
E=0;
P0=com;
delay(3);
E=1;
delay(3);
E=0;
}
void writeDDR(uchar dat){//写数据
while(checkBusy());//检查忙状态
RS=1;//选择数据模式
RW=0;//选择写模式
E=0;//禁止LCD
P0=dat;//写入数据
_nop_();_nop_();_nop_();_nop_();//延时
E=1;//使能LCD
_nop_();_nop_();_nop_();_nop_();//延时
E=0;//禁止LCD
}
void showstr(uchar str[]){
uint i;
i=0;
while(str[i]!='\0'){
writeDDR(str[i]);//显示字符
i++;
}
}
///////////////////显示子函数//////////////////////
/*void display(){
jisuan();
writeIR(0x80);
showstr(str1);
writeIR(0xc0);
showstr(str2);
}*/
/////////////////DS1302操作//////////////////////////
//DS1302:写入操作(写一个字节,上升沿操作)
void write_byte(uchar da){
uchar i,temp;
temp=da;//要写的数据送入临时变量temp
for(i=8;i>0;i--){//将一个字节的数据分8次,一位一位送数据线
IO=temp&0x01;//将临时变量temp的最低位送入IO数据线
CLK=0; //时钟信号拉低
CLK=1;//时钟信号拉高
temp=temp>>1;//将下次要送的那位数据送到临时变量temp最低位
}
CLK=0;//写完一字节数据后,拉低时钟信号
}
//DS1302:读取操作(读一个字节,下降沿)
uchar read_byte()
{
uchar i,a;
a=0x00;//a清0,这步也可不做
for(i=0;i<8;i++){//一个字节的数据分8次读,一位一位从数据线读
a=a>>1;//空出临时变量a的最高位
if(IO)a=a|0x80;//从数据线上读位数据到临时变量a的最高位
else a=a|0x00;
CLK=1; //时钟信号拉高
CLK=0; //时钟信号拉低
}
CLK=0;//读完一字节数据后,拉低时钟信号
return a;//将临时变量a内的值返回
}
//DS1302:写入数据(先送地址,再写数据)
void write_1302(uchar addr,uchar da){
RST=0; //停止工作
CLK=0; //时钟信号拉低
RST=1; //重新工作
write_byte(addr); //写入地址
write_byte(da);//写入数据(da是十六进制数据,BCD码)
CLK=0;//时钟信号拉低
RST=0;//停止工作
}
//DS1302:读取数据(先送地址,再读数据),读出的数据是BCD码
uchar read_1302(uchar addr)
{
uchar temp;
RST=0; //停止工作
CLK=0; //时钟信号拉低
RST=1;//重新工作
write_byte(addr); //写入地址
temp=read_byte();//读取数据(16进制,BCD码)
CLK=0;//时钟信号拉低
RST=0;//停止工作
return temp;
}
uchar toDEC(uchar da){
uchar temp,dat1,dat2;
temp=da;
dat1=temp/16; //提取BCD码的十位和个位
dat2=temp%16;
temp=dat1*10+dat2; //转换成10进制数
return temp;
}
uchar toBCD(uchar da){
uchar temp;
temp=(da/10)<<4|(da%10);//转换成BCD码
return temp;
}
void setProtect(bit flag){
if(flag)
write_1302(0x8e,0x80);//打开写保护
else
write_1302(0x8e,0x00);//关闭写保护
}
//设置时间函数提供参数:控制字,要写入的10进制数据
void setTime(uchar addr,uchar da){
setProtect(0);//关闭写保护
write_1302(addr,toBCD(da));//写入BCD码到指定寄存器
}
void getTime(){//获取时钟芯片的时钟数据
second=read_1302(0x81);//读秒(BCD码)
second=toDEC(second&0x7f);//BCD码转十进制
minute=read_1302(0x83);//读分(BCD码)
minute=toDEC(minute);//BCD码转十进制
hour=read_1302(0x85);//读时(BCD码)
hour=toDEC(hour);//BCD码转十进制
day=read_1302(0x87);//读日(BCD码)]
day=toDEC(day);//BCD码转十进制
month=read_1302(0x89);//读月(BCD码)
month=toDEC(month);//BCD码转十进制
year=read_1302(0x8d);//读年(BCD码)
year=toDEC(year);//BCD码转十进制
week=read_1302(0x8b);//读星期(BCD码)
week=toDEC(week);//BCD码转十进制
}
void jisuan(){//计算子程序,计算时间各位的值
uint i;
i=week-1;
if(i==0)i=7;
str1[0]=yearH/10+'0';//提取年的千位
str1[1]=yearH%10+'0';//提取年的百位
str1[2]=year/10+'0';//提取年的十位
str1[3]=year%10+'0';//提取年的个位
str1[4]='-';
str1[5]=month/10+'0';//提取月的十位
str1[6]=month%10+'0';//提取月的个位
str1[7]='-';
str1[8]=day/10+'0';//提取日的十位
str1[9]=day%10+'0';//提取日的个位
str1[10]=' ';
str1[11]=' ';
str1[12]=i+'0';//星期
str2[0]=hour/10+'0';//提取小时的十位
str2[1]=hour%10+'0';//提取小时的个位
str2[2]=':';
str2[3]=minute/10+'0';//提取分钟的十位
str2[4]=minute%10+'0';//提取分钟的个位
str2[5]=':';
str2[6]=second/10+'0';//提取秒钟的十位
str2[7]=second%10+'0';//提取秒钟的个位
str2[8]=' ';
}
void initial(){//初始化函数
uchar second=read_1302(0x81);
if(second&0x80){//时钟停止,则初始化
setTime(0x80,0);
setTime(0x82,20);
setTime(0x84,8);
setTime(0x86,1);
setTime(0x88,6);
setTime(0x8a,5);
setTime(0x8c,12);
}
}
//按键部分程序
void Upkey(){//加按钮
up=1;
if(up==0){ //加按键按下
delay(10);
if(up==0){//加按键确实按下
switch(count){//根据选择的模块进行调整
case 7://调整星期
temp=week+1;//秒数加1
if(week==7) temp=1;//越界处理
up_flag=1;//数据调整后更新标志
break;
case 6://调整秒
temp=second+1;//秒数加1
if(second==59) temp=0;//越界处理
up_flag=1;//数据调整后更新标志
break;
case 5://调整分
temp=minute+1;//分数加1
if(minute==59) temp=0;//越界处理
up_flag=1;//数据调整后更新标志
break;
case 4://调整时
temp=hour+1;//小时数加1
if(hour==23) temp=0;//越界处理
up_flag=1;//数据调整后更新标志
break;
case 3://调整日
temp=day+1;//日数加1
if(day==31) temp=1;//越界处理
up_flag=1;//数据调整后更新标志
break;
case 2://调整月
temp=month+1;//月数加1
if(month==12) temp=1;//越界处理
up_flag=1;//数据调整后更新标志
break;
case 1://调整年
temp=year+1;//年数加1
if(year==99){ temp=0;yearH=yearH+1;}//越界处理
up_flag=1;//数据调整后更新标志
break;
default:break;
}
while(up==0)display();
}
}
}
void Downkey(){//减按钮
down=1;
if(down==0){
delay(10);
if(down==0){
switch(count){
case 7://调整星期
temp=week-1;
if(week==0) temp=7;
down_flag=1;
break;
case 6://调整秒
temp=second-1;
if(second==0) temp=59;
down_flag=1;
break;
case 5://调整分
temp=minute-1;
if(minute==0) temp=59;
down_flag=1;
break;
case 4://调整时
temp=hour-1;
if(hour==0) temp=23;
down_flag=1;
break;
case 3://调整日
temp=day-1;
if(day==1) temp=31;
down_flag=1;
break;
case 2://调整月
temp=month-1;
if(month==1) temp=12;
down_flag=1;
break;
case 1://调整年
temp=year-1;
if(year==0){ temp=99;yearH=yearH-1;}
down_flag=1;
break;
default:break;
}
while(down==0)display();
}
}
}
void Selkey(){//调整部分选择按键
select=1;
if(select==0){
delay(10);
if(select==0){
count=count+1;
if(count==9) count=1;//调整部分计数加1
done=1;//进入调整模式
}
}
while(select==0)display();
}
void keydone(){//按键功能执行
if(flag==0){//关闭时钟,停止计时
setProtect(0);//写入允许
temp=read_1302(0x81);
write_1302(0x80,temp|0x80);
setProtect(1);//禁止写入
flag=1;
}
Selkey();//扫描调整部分选择按键
switch(count){
case 1:do{
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x8c,toBCD(temp));//写入新的年数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==2);
break;
case 2:do{
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x88,toBCD(temp));//写入新的月数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==3);
break;
case 3:do{
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x86,toBCD(temp));//写入新的日数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==4);
break;
case 4:do{//调整时
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x84,toBCD(temp));//写入新的小时数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==5);
break;
case 5:do{//调整分
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x82,toBCD(temp));//写入新的分数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==6);
break;
case 6:do{//调整秒
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x80,toBCD(temp)|0x80);//写入新的秒数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==7);
break;
case 7:do{//调整星期
Upkey();//扫描加按钮
Downkey();//扫描减按钮
if(up_flag==1||down_flag==1){//数据更新,重新写入新的数据
setProtect(0);//写入允许
write_1302(0x8a,toBCD(temp));//写入新的星期数
setProtect(1);//禁止写入
up_flag=0;
down_flag=0;
}
getTime();
display();
}while(count==8);
break;
case 8:do{
count=0;
second=read_1302(0x81);
setProtect(0);//写入允许
write_1302(0x80,second&0x7f);
setProtect(1);//禁止写入
done=0;
display();
}while(count==9);
break;
default:break;
}
}
////////////////////主函数////////////////////////
/*void main(){
//1602初始化工作
writeIR(0x01);//清屏,同时光标移到左上角
writeIR(0x38);//功能设置:8位接口,2行,5*7字符
writeIR(0x0c);//控制字符、光标及闪烁:显示器开,光标关,光标不闪烁
writeIR(0x06);//输入方式设置指令:光标不动,字符自动右移一格
//writeIR(0x80);//送DDRAM地址设置指令80H,从第1行第1列开始显示
//DS1302初始化工作
flag=1;
initial();
up_flag=0;
down_flag=0;
done=0;
while(1){
while(done==1)keydone();
while(done==0){
getTime();
display();
flag=0;
Selkey();
}
}
}*/
/////////////////////////////////////////////////////////////////
void display(){
jisuan();
writeIR(0x80);
showstr(str1);
writeIR(0xc0);
showstr(str2);
writecom(0x80);
showstr(str1);
writecom(0xc0);
showstr(str2);
jisuan1();
writecom(0x00);
showstr(str3);
}
void jisuan1(){//计算子程序,计算时间各位的值
//uint i;
if(flag_t)str3[0]='-';
else str3[0]='+';
str3[1]=t%1000/100+'0';
str3[2]=t%100/10+'0';
str3[3]='.';
str3[4]=t%10+'0';
str3[5]=223;
str3[6]='C';
}
///////////////////DS18B20操作//////////////////////
//延时us级
void delay_us(uchar us){
while(us--);
}
//初始化DS18B20
bit reset(){
bit flag;//初始化标志位
DQ=1;//dq复位,也可以不要
delay_us(1);//稍做延时
DQ=0;//单片机将dq拉低
delay_us(80);//精确延时大于480us
DQ=1;//拉高总线
delay_us(8);//延时
flag=DQ;//flag=0初始化成功,flag=1则初始化失败
delay_us(20);
return flag;
}
//DS18B20写一个字节
void write_tbyte(uchar dat){
uchar i;
bit onebit;
for(i=0;i<8;i++){//一位一位写数据
onebit=dat&0x01;//提取要写数据的最低位
dat=dat>>1;//数据右移,准备下一次数据的提取
if(onebit){//要写位为1时
DQ=0;//dq拉低时,空操作
_nop_();
_nop_();
DQ=1;//dq拉高时,延时
delay_us(5);
}
else{//要写位为0时
DQ=0;//dq拉低时,延时
delay_us(8);
DQ=1;//dq拉高时,空操作
_nop_();
_nop_();
}
}
}
//DS18B20读一个位
bit read_tbit(){
bit dat;
DQ=0;//dq拉低,1us之后释放总线(即拉高dq)
_nop_();
DQ=1;//dq拉高
_nop_();
_nop_();
dat=DQ;//从总线dq上读位数据
delay_us(10);//延时,确保读时序的长度60us
return dat;
}
//DS18B20读一个字节
uchar read_tbyte(){
uchar value,i,j;
value=0;//value初始化清0,用于保存每次读取
//1位后的字节数据
for(i=0;i<8;i++){//一位一位读数据(从最低位开始)
j=read_tbit();//读位数据到j的最低位
value=(j<<7)|(value>>1);//构造读取的数据
//构造原理:第一步,将刚读到的位数据移到j的最高位
//第二步,将前一次读取的数据值右移一位
//第三步,将新的j与新的value进行或操作,
//得到当前新的读取数据
}
return value;
}
uint getTemp(){
uchar a,b;
float f_temp;//定义浮点型温度数据
if(!reset()){//如果初始化成功
write_tbyte(0xcc);//跳过ROM
write_tbyte(0x44);//温度转换
}
if(!reset()){//如果初始化成功
write_tbyte(0xcc);//跳过ROM
write_tbyte(0xbe);//读暂存器
a=read_tbyte();//读取温度低8位
b=read_tbyte();//读取温度高8位
}
flag_t=0;
t=b;//把温度数据的高8位赋给整型温度数据temp的低8位
t<<=8;//把以上8位数据从temp的低8位移到高8位
t=t|a;//两字节合并得到整型变量
if(t>0x0fff){
flag_t=1;
t=~t+1;
}
f_temp=t*0.0625;//得到真实的浮点型温度数据
t=f_temp*10+0.5;//将浮点型温度数据放大十倍,并加0.5(加0.5是用于四舍五入)
//然后将处理后的浮点型温度数据强制取整,赋给整型温度数据
//这个赋值语句的目的将小数点后第一位也转换为可显示数字
return t;
}
void main(){
//1602初始化工作
writeIR(0x01);//清屏,同时光标移到左上角
writeIR(0x38);//功能设置:8位接口,2行,5*7字符
writeIR(0x0c);//控制字符、光标及闪烁:显示器开,光标关,光标不闪烁
writeIR(0x06);//输入方式设置指令:光标不动,字符自动右移一格
//writeIR(0x80);//送DDRAM地址设置指令80H,从第1行第1列开始显示
//DS1302初始化工作
flag=1;
initial();
up_flag=0;
down_flag=0;
done=0;
while(1){
while(done==1)keydone();
while(done==0){
getTime();
gettemp();
display();
flag=0;
Selkey();
}
}
}
这是我的程序 但是最高的温度就是16° 超过16的部分都重新来
|