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