/**
程序功能:顯示年月日,時分秒,星期,溫度
*/
#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的部分都重新來
|