#include < reg52.h >
#include < intrins.h > //用到nop()時(shí)使用
#define uchar unsigned char
#define uint unsigned int
sbit K5 = P1^4; //調(diào)節(jié)數(shù)位切換鍵
sbit K6 = P1^5; //數(shù)值增加鍵
sbit K7 = P1^6; //數(shù)值減小鍵
sbit K8 = P1^7; //確認(rèn)鍵
uchar K5flag = 0;
sbit LCD_RS = P2^0; //LCD1602數(shù)據(jù)/命令選擇端
sbit LCD_RW = P2^1; //LCD1602讀/寫選擇端
sbit LCD_EN = P2^2; //LCD1602使能端
sbit DQ=P3^3; //DS18B20信號(hào)端
uint year=2015;
uchar month=2;
uchar day=27,daytemp;
uchar weekdayflag=5;
char H=12;
char M=0;
char S=0;
uchar displaybuf[9];
uchar code cdis1[20]= {"0123456789:.C- "};
uchar code cdis2[7][5]=
{"Mon",
"Tue",
"Wed",
"Thu",
"Fir",
"Sat",
"Sun"};
/**********************************************************
5us 延時(shí)子程序 ms延時(shí)函數(shù)
**********************************************************/
void delayNOP()
{
_nop_(); //一個(gè)機(jī)器周期1us延時(shí)
_nop_();
_nop_();
_nop_();
_nop_();
}
void delay(uint ms)
{
uint t;
while(ms--)
{
for(t = 0; t <100; t++);
}
}
/**********************************************************
1602液晶操作
**********************************************************/
//忙碌狀態(tài)判斷
bit lcd_busy() //RS/RW/EN=0/1/1為讀狀態(tài)
{
bit result;
LCD_RS = 0; //RS為1選擇數(shù)據(jù),RS為0選擇命令
LCD_RW = 1; //RW為1選擇讀操作,RW為0選擇寫操作
LCD_EN = 1; //EN等于1可以讀取信息,等于0可以執(zhí)行命令
delayNOP(); //5us延時(shí)
result = (bit)(P0&0x80); //確保STA7為0,說(shuō)明為“閑”狀態(tài),為1位“忙”操作;(bit)(P0&0x80)為強(qiáng)制類型轉(zhuǎn)換
LCD_EN = 0; //讀取數(shù)據(jù)結(jié)束之后,復(fù)位執(zhí)行使能操作
return(result);
}
//寫命令
void write_control(uchar control) //RS/RW/EN=0/0/高脈沖 為寫指令狀態(tài)
{
while(lcd_busy());
LCD_RS=0; //選擇命令操作
LCD_RW=0; //RW為0時(shí)寫數(shù)據(jù);
LCD_EN=0; //1602執(zhí)行命令
delayNOP();
P0=control; //發(fā)送高四位給1602數(shù)據(jù)P0口
LCD_EN = 1; //EN高脈沖
delayNOP();
LCD_EN = 0; //高脈沖之后恢復(fù)EN=0
delayNOP();
}
//寫地址
void write_address(uchar pos) // 寫入顯示地址命令
{
pos=(pos|0x80); //數(shù)據(jù)指針=80+地址變量
write_control(pos);
}
//寫數(shù)據(jù)
void write_data(uchar temp) // RS/RW/EN=1/0/高脈沖 為寫數(shù)據(jù)狀態(tài)
{
while(lcd_busy());
LCD_RS=1; //選擇命令操作
LCD_RW=0; //RW為0時(shí)寫數(shù)據(jù);
LCD_EN=0; //1602執(zhí)行命令
delayNOP();
P0=temp; //發(fā)送數(shù)據(jù)給1602數(shù)據(jù)P0口
LCD_EN = 1; //EN高脈沖
delayNOP();
LCD_EN = 0; //高脈沖之后恢復(fù)EN=0
delayNOP();
}
//液晶初始化
void lcd_init() //1602液晶初始化函數(shù)
{
delay(5);
write_control(0x38); //16*2顯示,5*7點(diǎn)陣,8位數(shù)據(jù)
delay(1);
write_control(0x38); //重復(fù)寫入,確保成功
delay(1);
write_control(0x38); //重復(fù)寫入,確保成功
delay(1);
write_control(0x0f); //光標(biāo)移動(dòng)
delay(1);
write_control(0x04); //光標(biāo)移動(dòng)
delay(1);
write_control(0x01); //清除LCD的顯示內(nèi)容
delay(5); //延時(shí)
}
//萬(wàn)年歷時(shí)間判斷顯示函數(shù)
void date_display()
{
while(lcd_busy());
write_address(0x00);
write_data(cdis1[year/1000%10]);
write_data(cdis1[year/100%10]);
write_data(cdis1[year/10%10]);
write_data(cdis1[year%10]);
write_data(cdis1[13]);
write_data(cdis1[month/10]);
write_data(cdis1[month%10]);
write_data(cdis1[13]);
write_data(cdis1[day/10]);
write_data(cdis1[day%10]);
}
//weekday顯示函數(shù)
void weekday_display()
{
int i=3;
while(lcd_busy());
write_address(0x0c);
write_control(0x0c);
for(i=0;i<3;i++)
{
write_data(cdis2[weekdayflag-1][i]);
}
}
//時(shí)間程序顯示函數(shù)
void TimeDisplay(jj) //時(shí)間程序顯示子函數(shù)
{
uchar temp;
displaybuf[0]=jj%10;
displaybuf[1]=jj/10;
displaybuf[3]=M%10;
displaybuf[4]=M/10;
displaybuf[6]=H%10;
displaybuf[7]=H/10;
while(lcd_busy());
write_address(0x48); //寫入時(shí)十位
temp=displaybuf[7];
write_data(cdis1[temp]);
temp=displaybuf[6]; //寫入時(shí)個(gè)位
write_data(cdis1[temp]);
write_data(cdis1[10]); //寫入":"
temp=displaybuf[4]; //寫入分十位
write_data(cdis1[temp]);
temp=displaybuf[3]; //寫入分個(gè)位
write_data(cdis1[temp]);
write_data(cdis1[10]); //:
temp=displaybuf[1]; //寫入秒十位
write_data(cdis1[temp]);
temp=displaybuf[0]; //寫入秒個(gè)位
write_data(cdis1[temp]);
}
//DS18B20操作函數(shù)
//初始化函數(shù)讀取應(yīng)答信號(hào)
unsigned char time; //設(shè)置全局變量,專門用于嚴(yán)格延時(shí)
bit Init_DS18B20(void)
{
bit flag; //儲(chǔ)存DS18B20是否存在的標(biāo)志,flag=0,表示存在;flag=1,表示不存在
DQ = 1; //先將數(shù)據(jù)線拉高
for(time=0;time<2;time++) //略微延時(shí)約6微秒
;
DQ = 0; //再將數(shù)據(jù)線從高拉低,要求保持480~960us
for(time=0;time<200;time++) //略微延時(shí)約600微秒
;
DQ = 1; //釋放數(shù)據(jù)線(將數(shù)據(jù)線拉高)
for(time=0;time<10;time++)
; //延時(shí)約30us(釋放總線后需等待15~60us讓DS18B20輸出存在脈沖)
flag=DQ; //讓單片機(jī)檢測(cè)是否輸出了存在脈沖(DQ=0表示存在)
for(time=0;time<200;time++) //延時(shí)足夠長(zhǎng)時(shí)間,等待存在脈沖輸出完畢
;
return (flag); //返回檢測(cè)成功標(biāo)志,返回1表示存在,返回0表示不存在
}
//從DS18B20中讀取數(shù)據(jù) ,讀取的數(shù)據(jù)從最低位開始
unsigned char ReadOneChar(void)
{
unsigned char i=0;
unsigned char dat; //儲(chǔ)存讀出的一個(gè)字節(jié)數(shù)據(jù)
for (i=0;i<8;i++)
{
DQ =1; // 先將數(shù)據(jù)線拉高
_nop_(); //等待一個(gè)機(jī)器周期
DQ = 0; //單片機(jī)從DS18B20讀書據(jù)時(shí),將數(shù)據(jù)線從高拉低即啟動(dòng)讀時(shí)序
dat>>=1; //復(fù)合運(yùn)算符,等于:dat=dat>>1;
_nop_(); //等待一個(gè)機(jī)器周期
DQ = 1; //將數(shù)據(jù)線"人為"拉高,為單片機(jī)檢測(cè)DS18B20的輸出電平作準(zhǔn)備
for(time=0;time<2;time++)
; //延時(shí)約6us,使主機(jī)在15us內(nèi)采樣
if(DQ==1)
dat|=0x80; //如果讀到的數(shù)據(jù)是1,則將1存入dat
else
dat|=0x00; //如果讀到的數(shù)據(jù)是0,則將0存入dat
//將單片機(jī)檢測(cè)到的電平信號(hào)DQ存入r[i]
for(time=0;time<8;time++)
; //延時(shí)3us,兩個(gè)讀時(shí)序之間必須有大于1us的恢復(fù)期
}
return(dat); //返回讀出的十進(jìn)制數(shù)據(jù)
}
//向DS18B20中寫入數(shù)據(jù)
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=0; i<8; i++)
{
DQ =1; // 先將數(shù)據(jù)線拉高
_nop_(); //等待一個(gè)機(jī)器周期
DQ=0; //將數(shù)據(jù)線從高拉低時(shí)即啟動(dòng)寫時(shí)序
DQ=dat&0x01; //利用與運(yùn)算取出要寫的某位二進(jìn)制數(shù)據(jù), 并將其送到數(shù)據(jù)線上等待DS18B20采樣
for(time=0;time<10;time++)
; //延時(shí)約30us,DS18B20在拉低后的約15~60us期間從數(shù)據(jù)線上采樣
DQ=1; //釋放數(shù)據(jù)線
for(time=0;time<1;time++)
; //延時(shí)3us,兩個(gè)寫時(shí)序間至少需要1us的恢復(fù)期
dat>>=1; //將dat中的各二進(jìn)制位數(shù)據(jù)右移1位
}
for(time=0;time<4;time++)
; //寫完數(shù)據(jù)之后稍作延時(shí),給硬件一點(diǎn)反應(yīng)時(shí)間
}
//溫度值計(jì)算及顯示函數(shù)
void Tdisplay()
{
uchar TL; //儲(chǔ)存暫存器的溫度低位
uchar TH; //儲(chǔ)存暫存器的溫度高位
uchar TN; //儲(chǔ)存溫度的整數(shù)部分
uchar TD; //儲(chǔ)存溫度的小數(shù)部分
uchar i,j,k; //計(jì)算溫度整數(shù)部分的個(gè)位,十位,百位
Init_DS18B20();
WriteOneChar(0xCC); // 單線單DS18B20可以跳過(guò)讀序號(hào)列號(hào)的操作,節(jié)省時(shí)間
WriteOneChar(0x44); // 啟動(dòng)溫度轉(zhuǎn)換命令
for(time=0;time<100;time++)
; //溫度轉(zhuǎn)換需要一點(diǎn)時(shí)間
Init_DS18B20(); //將DS18B20初始化
WriteOneChar(0xCC); //跳過(guò)讀序號(hào)列號(hào)的操作
WriteOneChar(0xBE); //讀取溫度寄存器,前兩個(gè)分別是溫度的低位和高位
TL=ReadOneChar(); //先讀的是溫度值低位
TH=ReadOneChar(); //接著讀的是溫度值高位
TN=TH*16+TL/16; //實(shí)際溫度值=(TH*256+TL)/16,即:TH*16+TL/16
//這樣得出的是溫度的整數(shù)部分,小數(shù)部分被丟棄了
k=TN/100%10; //取溫度值的百位
j=TN/10%10; //取溫度值的十位
i=TN%10; //取溫度值的個(gè)位
TD=(TL%16)*10/16; //計(jì)算溫度的小數(shù)部分,將余數(shù)乘以10再除以16取整,
//這樣得到的是溫度小數(shù)部分的第一位數(shù)字(保留1位小數(shù))
while(lcd_busy());
write_address(0x41); //寫入液晶開始顯示溫度的位置
//write_data(cdis1[k]); //寫溫度值的百位
write_data(cdis1[j]); //寫溫度值的十位
write_data(cdis1[i]); //寫溫度值的個(gè)位
write_data(cdis1[11]); //寫小數(shù)點(diǎn)
write_data(cdis1[TD]); //寫溫度值的個(gè)位
write_data(0xdf);
write_data(cdis1[12]); //寫溫度單位
delay(10);
}
//按鍵掃描程序
void key_scan(void)
{
switch(month)
{
case 1:daytemp=31;break;
case 2:if((year%4==0)&&(year%100!=0)||(year%400==0)) //閏年:年份能被4整除且不能被100整除,或者能被400整除
{
daytemp=29;
}
else
daytemp=28;break;
case 3:daytemp=31;break;
case 4:daytemp=30;break;
case 5:daytemp=31;break;
case 6:daytemp=30;break;
case 7:daytemp=31;break;
case 8:daytemp=31;break;
case 9:daytemp=30;break;
case 10:daytemp=31;break;
case 11:daytemp=30;break;
case 12:daytemp=31;break;
default:break;
}
P1=0xf0; //將P1口高4位置高電平“1”
if((P1&0xf0)!=0xf0) //有鍵按下
{
delay(10); //延時(shí)消抖延時(shí)60ms再檢測(cè)
if((P1&0xf0)!=0xf0) //確實(shí)有鍵按下
{
if(K5==0) //如果是K5鍵按下,切換到調(diào)節(jié)模式,按下一次是,年代數(shù)跳動(dòng)
{
K5flag++;
if(K5flag>=7)
K5flag=0;
switch(K5flag)
{
case 1:write_address(0x03);write_control(0x0f);break; //光標(biāo)跳動(dòng)命令于"year"調(diào)節(jié)模式
case 2:write_address(0x06);write_control(0x0f);break; //光標(biāo)跳動(dòng)命令于"month"調(diào)節(jié)模式
case 3:write_address(0x09);write_control(0x0f);break; //光標(biāo)跳動(dòng)命令"day"調(diào)節(jié)模式
case 4:write_address(0x0e);write_control(0x0f);break; //光標(biāo)跳動(dòng)命令"weekday"調(diào)節(jié)模式
case 5:write_address(0x49);write_control(0x0f);break; //光標(biāo)跳動(dòng)命令"H"調(diào)節(jié)模式
case 6:write_address(0x4C);write_control(0x0f);break; //光標(biāo)跳動(dòng)命令"M"調(diào)節(jié)模式
default:break;
}
while(!K5); //等待K5釋放
}
if(K6==0) //如果是k6鍵按下數(shù)值增加
{
switch(K5flag)
{
case 1:year++; break;
case 2:month++;if((month-1)>=12)month=1;break;
case 3:day++;if((day-1)>=daytemp)day=1;break;
case 4:weekdayflag++;if((weekdayflag-1)==7)weekdayflag=1;break;
case 5:H++;if(H>=24)H=0;break;
case 6:M++;if(M>=60)M=0;break;
default:break;
}
while(!K6); //等待K6釋放
}
if(K7==0)
{
switch(K5flag)
{
case 1:year--; break;
case 2:month--;if((month+1)==1)month=12;break;
case 3:day--;if((day+1)==1)day=daytemp;break;
case 4:weekdayflag--;if((weekdayflag+1)==7)weekdayflag=1;break;
case 5:H--;if(H<0)H=23;break;
case 6:M--;if(M<0)M=59;break;
default:break;
}
while(!K7); //等待K7釋放
}
if(K8==0) //如果是k8鍵確認(rèn)鍵按下,光標(biāo)停止閃爍,結(jié)束設(shè)置
{
write_control(0x0c);
K5flag=0;
}
}
}
}
/*
//串口通信函數(shù)
unsigned char tmp;
void send_char(unsigned char txd) // 傳送一個(gè)字符
{
SBUF = txd;
while(!TI); // 等特?cái)?shù)據(jù)傳送
TI = 0; // 清除數(shù)據(jù)傳送標(biāo)志
}
void interrupt1_init() //串口初始化程序
{
TMOD=0x20; // 定時(shí)器1工作于8位自動(dòng)重載模式, 用于產(chǎn)生波特率
TH1=TL1=0XFD; // 波特率9600,定時(shí)器裝初值
SCON=0X50; // 設(shè)定串行口工作方式1 ,REN置1允許數(shù)據(jù)接收
PCON=0X00; // 波特率不倍增
TR1=1; // 啟動(dòng)定時(shí)器1,不用開定時(shí)器中斷
}
main()
{
interrupt1_init();
while(1)
{
if(RI==1) // 允許接收標(biāo)志位,RI=1有數(shù)據(jù)到來(lái),允許接收,接收完一個(gè)字節(jié)數(shù)據(jù)后,自動(dòng)置1
{
RI = 0; //RI置0,暫停接收,等待數(shù)據(jù)處理
tmp = SBUF; // 暫存接收到的數(shù)據(jù)
P0 = tmp; // 數(shù)據(jù)傳送到P0口
send_char(tmp); // 回傳接收到的數(shù)據(jù),在電腦里顯示
}
}
}
*/
/**********************************************************/
/* 主函數(shù) */
void main()
{
lcd_init(); //LCD1602初始化
TMOD=0x01; //定時(shí)器T0設(shè)為16位定時(shí)器
EA=1;
ET0=1;
TR0=1;
while(1)
{
key_scan();
date_display(); //顯示萬(wàn)年歷部分
weekday_display(); //顯示W(wǎng)eekday信息
Tdisplay(); //顯示溫度部分
TimeDisplay(S); //顯示時(shí)鐘部分
}
}
/* 中斷函數(shù) */
void interserve(void) interrupt 1 using 1 //定時(shí)器T0的中斷編號(hào)為1,using 1寄存器組
{
uchar i;
TR0=0; //調(diào)用T0中斷程序時(shí)先要關(guān)閉定時(shí)器T0
i++; //每來(lái)一次中斷,中斷次數(shù)int_time自加1
if(i==20) //夠20次中斷,即1秒鐘進(jìn)行一次檢測(cè)結(jié)果采樣
{
i=0; //中斷次數(shù)清0
S++; //秒加1
if(S==60)
{
S=0;
M++;
if(M==60)
{
M=0;
H++;
if(H==24)
{
H=0;
day++;
if((day-1)==daytemp)
{
day=1;
month++;
if((month-1)==12)
{
month=1;
year++;
}
}
weekdayflag++;
if((weekdayflag-1)==7)
{
weekdayflag=1;
}
}
}
}
}
TH0=(65536-46080)/256; //重新給計(jì)數(shù)器T0賦初值50MS
TL0=(65536-46080)%256;
TR0=1; //啟動(dòng)定時(shí)器T0
}
|