我做了一個程序,要求顯示溫度濕度,記錄溫度的最大值以及溫度達到最大值的時間(年月日時分秒)。但是當我不讀取時間的時候(也就是沒有DS1302Init()和DS1302ReadTime()的時候)溫度和濕度能正常刷新,但是一加入讀取時間的函數就發現溫度濕度全都變為0,而且一直都是零。球大神幫我看看代碼錯在哪了,是不是加入時間函數后讀取溫濕度的時序出了問題?該如何修改?
一共有三個文件,其中ds1302的兩個文件都是買單片機的時候自帶的,不會出問題,所以問題應該出在tem.c和ds1302結合的時候tem.c的時序出了問題。
//DS1302.H
#ifndef __DS1302_H_
#define __DS1302_H_
//---包含頭文件---//
#include<reg51.h>
#include<intrins.h>
//---重定義關鍵詞---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
//---定義ds1302使用的IO口---//
sbit DSIO=P3^4;
sbit RST=P3^5;
sbit SCLK=P3^6;
//---定義全局函數---//
void Ds1302Write(uchar addr, uchar dat);
uchar Ds1302Read(uchar addr);
void Ds1302Init();
void Ds1302ReadTime();
//---加入全局變量--//
extern uchar TIME[7]; //加入全局變量
#endif
//ds1302.c
#include"ds1302.h"
//---DS1302寫入和讀取時分秒的地址命令---//
//---秒分時日月周年 最低位讀寫位;-------//
uchar code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
uchar code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
//---DS1302時鐘初始化2013年1月1日星期二12點00分00秒。---//
//---存儲順序是秒分時日月周年,存儲格式是用BCD碼---//
uchar TIME[7] = {0, 0, 0x12, 0x01, 0x01, 0x02, 0x13};
//---定義ds1302使用的IO口---//
/*******************************************************************************
* 函 數 名 : Ds1302Write
* 函數功能 : 向DS1302寫命令(地址(控制字)+數據,兩個字節)
* 輸 入 : addr,dat
* 輸 出 : 無
*******************************************************************************/
void Ds1302Write(uchar addr, uchar dat)
{
uchar n;
RST = 0;
_nop_();
SCLK = 0;//先將SCLK置低電平。
_nop_();
RST = 1; //然后將RST(CE)置高電平。
_nop_();
for (n=0; n<8; n++)//開始傳送八位地址命令
{
DSIO = addr & 0x01;//數據從低位開始傳送
addr >>= 1;
SCLK = 1;//數據在上升沿時,DS1302讀取數據
_nop_();
SCLK = 0;
_nop_();
}
for (n=0; n<8; n++)//寫入8位數據
{
DSIO = dat & 0x01;
dat >>= 1;
SCLK = 1;//數據在上升沿時,DS1302讀取數據
_nop_();
SCLK = 0;
_nop_();
}
RST = 0;//傳送數據結束
_nop_();
}
/*******************************************************************************
* 函 數 名 : Ds1302Read
* 函數功能 : 讀取一個地址的數據
* 輸 入 : addr
* 輸 出 : dat
*******************************************************************************/
uchar Ds1302Read(uchar addr)
{
uchar n,dat,dat1;
RST = 0;
_nop_();
SCLK = 0;//先將SCLK置低電平。
_nop_();
RST = 1;//然后將RST(CE)置高電平。
_nop_();
for(n=0; n<8; n++)//開始傳送八位地址命令
{
DSIO = addr & 0x01;//數據從低位開始傳送
addr >>= 1;
SCLK = 1;//數據在上升沿時,DS1302讀取數據
_nop_();
SCLK = 0;//DS1302下降沿時,放置數據
_nop_();
}
_nop_();
for(n=0; n<8; n++)//讀取8位數據
{
dat1 = DSIO;//從最低位開始接收
dat = (dat>>1) | (dat1<<7);
SCLK = 1;
_nop_();
SCLK = 0;//DS1302下降沿時,放置數據
_nop_();
}
RST = 0;
_nop_(); //以下為DS1302復位的穩定時間,必須的。
SCLK = 1;
_nop_();
DSIO = 0;
_nop_();
DSIO = 1;
_nop_();
return dat;
}
/*******************************************************************************
* 函 數 名 : Ds1302Init
* 函數功能 : 初始化DS1302.
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void Ds1302Init()
{
uchar n;
Ds1302Write(0x8E,0X00); //禁止寫保護,就是關閉寫保護功能
for (n=0; n<7; n++)//寫入7個字節的時鐘信號:分秒時日月周年
{
Ds1302Write(WRITE_RTC_ADDR[n],TIME[n]);
}
Ds1302Write(0x8E,0x80); //打開寫保護功能
}
/*******************************************************************************
* 函 數 名 : Ds1302ReadTime
* 函數功能 : 讀取時鐘信息
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void Ds1302ReadTime()
{
uchar n;
for (n=0; n<7; n++)//讀取7個字節的時鐘信號:分秒時日月周年
{
TIME[n] = Ds1302Read(READ_RTC_ADDR[n]);
}
}
//tem.c
#include"intrins.h" //包含含有_nop_()的頭文件
#include"stdio.h" //包含printf函數
#include"ds1302.h"
#define uint unsigned int
#define uchar unsigned char
uchar DHT11[5];
uchar FLAG; //超時標志位,工作原理是++至溢出256(uchar型)
uchar RTflag=0;
uchar display_flag=0;
sbit dat=P3^7; //DHT11
sbit RS=P2^6;
sbit RW=P2^5;
sbit EN=P2^7; //LCD1602
sbit k1=P3^0; //k1,k2用來在攝氏度與華氏度之間切換
sbit k2=P3^1;
sbit k3=P3^2;
sbit k4=P3^3;
uchar table[5];
uint wd,sd;
uchar max_tem_time[7];
uchar min_tem_time[7];
uchar max_hum_time[7];
uchar min_hum_time[7];
uint temperature ,tem_F;
uint humidity;
uint max_tem = 0,max_tem_F = 0,min_tem = 99,min_tem_F = 0;
uint i = 0;
uint test = 0;
void UsartConfiguration()
{
SCON=0X50; //設置串口為工作方式1
TMOD=0X20; //設置計數器為工作方式2
PCON=0X80; //波特率加倍
TH1=0XF3; //計數器初始值設置,注意波特率是4800的
TL1=0XF3;
// ES=1; //打開接收中斷
// EA=1; //打開總中斷
TR1=1; //打開計數器
}
void Delay_10us(void) //10us延時函數
{
uchar i;
i--;
i--;
i--;
i--;
i--;
i--;
}
void delay(uint z) // z毫秒延時函數
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void lcd_write_com(uchar com) //1602寫指令
{
RS=0;
RW=0;
EN=1;
P0=com;
delay(1); //延時1ms
EN=0;
}
void lcd_init() //1602初始化
{
lcd_write_com(0x38); //設置16*2顯示,5*7點陣,8位數據接口
delay(1);
lcd_write_com(0x80); //設置數據指針地址
delay(1);
lcd_write_com(0x01); //1602清屏指令
delay(1);
lcd_write_com(0x06); //當讀或寫一個字符后,地址指針加1;當寫一個字符,整屏顯示不移動
delay(1);
lcd_write_com(0x0C); //開顯示,但不顯示光標
delay(1);
}
void lcd_write_data(uchar date)//1602寫數據
{
RS=1;
RW=0;
EN=1;
P0=date;
delay(1); //延時1ms
EN=0;
}
void write_str(uchar x,uchar y,uchar *s)//在LCD任意地址寫符號字母或數字,y是行,共兩行,x是該行的具體位置
{
if(y==0)
lcd_write_com(0x80+x); //將數據寫在LCD的第一行
else
lcd_write_com(0xc0+x); //將數據寫在LCD的第二行
while(*s)
{
lcd_write_data(*s);
s++;
}
}
void write_shu(uchar x,uchar y,uchar num)//十位個位顯示函數
{
uchar s,g; //s代表十位,g代表個位
if(y==0)
lcd_write_com(0x80+x);
else
lcd_write_com(0xc0+x);
s=num/10; // 數據分離顯示
lcd_write_data(0x30+s); //數據以ASCII碼寫入
g=num%10; // 數據分離顯示
lcd_write_data(0x30+g); //數據以ASCII碼寫入
}
void write_p(uchar x,uchar y,uchar num)//小數位顯示函數
{
uchar p; //p代表小數
if(y==0)
lcd_write_com(0x80+x);
else
lcd_write_com(0xc0+x);
p=num/10; // 數據分離顯示
lcd_write_data(0x30+p);
}
uchar write_byte1() //讀一個字節
{
uchar i,comdata,temp1;
for(i=0;i<8;i++)
{
FLAG=2;
while((!dat)&&FLAG++);//判斷1bit開始數據的12-14us是否結束,當dat為1時跳出while循環
Delay_10us();
Delay_10us();
Delay_10us();
Delay_10us();
temp1=0; //假設讀出的1bit數據是0
if(dat) //當dat是1時,讀出的1bit數據是1
temp1=1;
FLAG=2; //FLAG設置成2是因為保證dat一直為1,不接受數據時,一直++,不至于一開始就break
//等待下1bit開始
while((dat)&&FLAG++);//flag先與后加1 如果dat一直為1 uchar型變量 flag 溢出變為0 再自加1
if(FLAG==1)break; //超時則跳出for循環
comdata<<=1;//左移一位 高位在前 低位在后
comdata|=temp1; //comdata=(comdata|temp1)
}
return (comdata);
}
void chuankou()
{
write_str(1,0,"Humi "); //第一行顯示濕度
write_shu(6,0,DHT11[0]);//顯示濕度十位個位
write_str(8,0,"."); //顯示小數點
write_p(9,0,DHT11[1]); //顯示濕度小數
write_str(11,0,"RH"); //顯示濕度單位"RH"
if(k3==0)
{
Ds1302ReadTime();
lcd_write_com(0x80);
lcd_write_data('0'+TIME[2]/16); //時
lcd_write_data('0'+(TIME[2]&0x0f));
lcd_write_data('-');
lcd_write_data('0'+TIME[1]/16); //分
lcd_write_data('0'+(TIME[1]&0x0f));
lcd_write_data('-');
lcd_write_data('0'+TIME[0]/16); //秒
lcd_write_data('0'+(TIME[0]&0x0f));
lcd_write_data('2');
lcd_write_data('0');
lcd_write_data('0'+TIME[6]/16); //年
lcd_write_data('0'+(TIME[6]&0x0f));
lcd_write_data('-');
lcd_write_data('0'+TIME[4]/16); //月
lcd_write_data('0'+(TIME[4]&0x0f));
lcd_write_data('-');
lcd_write_data('0'+TIME[3]/16); //日
lcd_write_data('0'+(TIME[3]&0x0f));
write_str(1,1,"maxT "); //第二行為顯示攝氏溫度
write_shu(6,1,max_tem);
write_str(8,1,".");
write_p(9,1,max_tem_F);
write_str(11,1,"^C");
write_shu(14,1,test);
delay(5000);
}
if(k1==0) //讀取k1按鍵按下
{
delay(5); //延時10ms消抖動
if(k1==0) //確保k1按鍵按下
{
display_flag=1;
//temperature=(int)(DHT11[2]*1.8+32); //按下,顯示華氏溫度的整數位
//tem_F=(int)((DHT11[2]*1.8+32)*10)%10; //按下,顯示華氏溫度的小數位
}
while(k1==0);//松手檢測
delay(10);
while(k1==0);
}
if(k2==0)
{
delay(5);
if(k2==0)
{
display_flag=0;
//temperature=DHT11[2]; //不按,顯示攝氏溫度的整數位
//tem_F=DHT11[3]; //不按,顯示攝氏溫度的小數位
}
while(k2==0);//松手檢測
delay(10);
while(k2==0);
}
if(display_flag==0) //k1按鍵不按下
{
write_str(1,1,"Temp "); //第二行為顯示攝氏溫度
write_shu(6,1,temperature);
write_str(8,1,".");
write_p(9,1,tem_F);
write_str(11,1,"^C");
}
else if(display_flag==1) //k1按鍵按下
{
write_str(1,1,"Temp "); //第二行為顯示華氏溫度
write_shu(6,1,temperature);
write_str(8,1,".");
write_p(9,1,tem_F);
write_str(11,1,"^F");
}
}
void DHT11_Read_Data() //讀5個字節數據 兩個字節為溫度數據 兩個字節為濕度數據 最后一個字節為校驗
{
uchar i,temp;
dat=0; //dat為主機(單片機)信號,可編程控制
delay(20); //主機至少拉低18ms
dat=1; //主機開始信號結束后拉高電平
//總線由上拉電阻拉高 主機拉高20-40us
Delay_10us();
Delay_10us();
Delay_10us();
Delay_10us();
//主機設為輸入 判斷從機響應信號(因為DHT11的響應電平無法編程控制,只能被動等待,被動檢測)
//檢測方法為:公用總線,dat同時表示單片機指令電平以及DHT11響應電平
dat=1;
//判斷從機是否有低電平響應信號 如不響應則跳出,響應則向下運行
if(!dat) //dat=0時有低電平響應,進入if函數
{
FLAG=2; //超時標志位
while((!dat)&&FLAG++);//判斷從機DHT發出的40-50us的低電平響應信號是否結束
/*FLAG的作用為防止響應超時,理解如下:根據時序圖,此時dat應該是低電平,
如果dat變成高電平,那么之后開始記錄數據,FLAG++是while循環的判斷條件
幫助跳出這個循環,FLAG是uchar類型,正常是最多到255就溢出,單步執行一次是6us,
若DHT11死機,則此次數據讀取失敗,若不加這個超時標志位,則永遠死在while循環
但是加了這個FLAG,DHT11死機后,FLAG在1500us=1.5ms后就溢出,溢出則為0*/
FLAG=2;
while((dat)&&FLAG++); //判斷從機DHT11拉高40-50us是否結束
//開始接收數據
for(i=0;i<5;i++)//數據接收狀態
{
DHT11=write_byte1();
}
dat=1; //釋放數據總線 為下一次讀取做好準備
temp=(DHT11[0]+DHT11[1]+DHT11[2]+DHT11[3]);
test = DHT11[2];
if(DHT11[2] + DHT11[3]/10 > max_tem + max_tem_F/10)
{
max_tem = DHT11[2];
max_tem_F = DHT11[3];
//Ds1302ReadTime();
}
if(DHT11[2] + DHT11[3]/10 < min_tem + min_tem_F/10)
{
min_tem = DHT11[2];
min_tem_F = DHT11[3];
//Ds1302ReadTime();
}
if (display_flag == 0)
{
humidity =DHT11[0];
temperature=DHT11[2];
tem_F =DHT11[3];
}
else
{
humidity =DHT11[0];
temperature=(int)(DHT11[2]*1.8+32); //按下,顯示華氏溫度的整數位
tem_F=(int)((DHT11[3]*1.8+32)*10)%10;
}
if(temp==DHT11[4]) //數據校驗
RTflag=1;
if(RTflag==1) //如果RTflag=1 說明讀取到得數據正確
{
RTflag=0; //為下次進行數據校驗做準備
chuankou();
}
}
}
void main()
{
UsartConfiguration();
lcd_init(); //1602初始化
k1 = 1;
k2 = 1;
k3 = 1;
k4 = 1;
Ds1302Init();
delay(3000); //等待DHT11溫濕度傳感器數據穩定 開始激活DHT11
while(1)//循環讀取 并更新數據顯示
{
delay(100);//等待DHT11溫濕度傳感器數據穩定 開始激活DHT11
DHT11_Read_Data(); //讀數據
delay(100); //延時等待
}
}
|