最近在學習DS18B20外設時,接觸到了單總線協議。按照DS18B20的DataSheet寫了一版程序后,發現一直無法正常顯示溫度,最后發現是延時的問題,但具體原理一直沒想明白。
單片機芯片為STC89C52,晶振為11.0592MHz。一個_nop_()約為1.085us。
這是我寫的源代碼:- #include<reg52.h>
- #include<intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
-
- sbit DX=P2^6;//(P0為數碼管輸出端口)
- sbit WX=P2^7;
- sbit DS18B20_IO=P2^2;
-
- uchar code Table_D[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
- uchar code Table_W[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
-
- uchar DS18B20_Reset_OK;
- uint num;
-
- void Delay_Us(uint x);
- void Delay_Ms(uint x);
- void Display(uint x);
-
- void DS18B20_Init();
- void DS18B20_Write_Byte(uchar command);
- uchar DS18B20_Read_Byte();
- uint Start_DS18B20();
-
-
- void main()
- {
- while(1)
- {
- num=Start_DS18B20();
- Display(num);
- }
- }
-
- void Delay_Us(uint x)//11.0592MHz,一個nop為1.085us
- {
- uint a;
- for(a=x;a>0;a--)
- {
- _nop_();
- }
- }
-
-
- void DS18B20_Init()
- {
- DS18B20_IO=1;
- Delay_Us(2);
- DS18B20_IO=0;
- Delay_Us(500);
- DS18B20_IO=1;
- Delay_Us(60);
- if(DS18B20_IO==0)
- {DS18B20_Reset_OK=1;}
- else
- {DS18B20_Reset_OK=0;}
- Delay_Us(500);
- }
-
- void DS18B20_Write_Byte(uchar command)
- {
- uchar i,temp;
- temp=command;
-
- for(i=0;i<8;i++)
- {
- DS18B20_IO=0;
- _nop_();_nop_();
- DS18B20_IO=temp&0x01;
- Delay_Us(10);//?????
- //Delay_Us(60);
- DS18B20_IO=1;
- _nop_();_nop_();
- temp=temp>>1;
- }
- Delay_Us(10);
- }
-
- uchar DS18B20_Read_Byte()
- {
- uchar i,temp1,temp2;
-
- for(i=0;i<8;i++)
- {
- DS18B20_IO=0;
- _nop_(); _nop_();
- DS18B20_IO=1;
- _nop_(); _nop_();
- temp1=DS18B20_IO;
- temp2=(temp2)>>1|(temp1<<7);
- Delay_Us(60);
- }
-
- return temp2;
- }
-
- uint Start_DS18B20()
- {
- uchar a,b;
- uint temp=0;
- DS18B20_Init();
- DS18B20_Write_Byte(0xcc);
- DS18B20_Write_Byte(0x44);
- DS18B20_Init();
- DS18B20_Write_Byte(0xcc);
- DS18B20_Write_Byte(0xbe);
- a=DS18B20_Read_Byte();
- b=DS18B20_Read_Byte();
- temp=b;
- temp=(temp<<8)|a;
- temp=temp*0.0625*10+0.5;
-
- return temp;
- }
-
-
-
-
- void Delay_Ms(uint x)
- {
- uint a,b;
- for(a=x;a>0;a--)
- {
- for(b=0;b<1000;b++)
- {_nop_();}
- }
- }
-
-
- void Display(uint x)
- {
- P0=0xff;
- WX=1;
- WX=0;
- P0=Table_D[x/100];
- DX=1;
- DX=0;
- P0=Table_W[0];
- WX=1;
- WX=0;
- Delay_Ms(1);
-
- P0=0xff;
- WX=1;
- WX=0;
- P0=Table_D[x%100/10]|0x80;
- DX=1;
- DX=0;
- P0=Table_W[1];
- WX=1;
- WX=0;
- Delay_Ms(1);
-
- P0=0xff;
- WX=1;
- WX=0;
- P0=Table_D[x%100%10];
- DX=1;
- DX=0;
- P0=Table_W[2];
- WX=1;
- WX=0;
- Delay_Ms(1);
- }
復制代碼
現在字節寫入那里是:
- DS18B20_IO=temp&0x01;
- Delay_Us(10);//?????
- //Delay_Us(60);
復制代碼 運行結果如下:
51hei圖片_20211024204848.jpg (303.75 KB, 下載次數: 56)
下載附件
Delay(10)
2021-10-24 20:49 上傳
按照DataSheet,一位數據的讀/要持續最少60us,最大120us的時間。
Screenshot 2021-10-24 204235.png (143.37 KB, 下載次數: 46)
下載附件
DataSheet
2021-10-24 20:43 上傳
如果我將字節寫入部分改成:
- DS18B20_IO=temp&0x01;
- //Delay_Us(10);
- Delay_Us(60);
復制代碼 運行結果如下:
51hei圖片_20211024204956.jpg (343.3 KB, 下載次數: 51)
下載附件
Delay(60)
2021-10-24 20:50 上傳
我又參考了一下該開發板上外設的DS18B20例程,發現使用的都是非精確延時。
- void delayus(uint t) //微秒級的延時函數
- {
- while(t--);
- }
復制代碼- void ds_write_byte(uchar dat) //寫一個字節函數
- {
- uchar i;
- for(i=0;i<8;i++) //循環8次
- {
- ds=0; //把總線拉為低電平
- _nop_(); //延時一機器周期,約1微秒
- ds=dat&0x01; //dat寫0x01按位與,目的是先傳送dat的最低位
- delayus(6); //延時,讓整個讀時序持續60~120微秒
- ds=1; //把總線釋放,讓ds等于1
- dat=dat>>1; //讓dat右移一位,準備下一位的寫入
- }
- delayus(6); //延時,讓每個函數之間都有一定的間隔停頓
- }
復制代碼- void ds_reset() //單總線初始化函數
- {
- ds=1; //總線先置高,讓ds等于1
- delayus(5); //稍延時
- ds=0; //主機發送復位脈沖
- delayus(80); //延時(在480~960ms之間)
- ds=1; //釋放總線,讓ds等于1
- delayus(14); //等待(15~60ms)
- if(ds==0) //判斷總線ds是否等于0
- flag=1; //flag等于1表示DS18B20存在
- else
- flag=0; //flag等于0表示DS18B20不存在。
- delayus(20);
- }
復制代碼
可是我用Keil的仿真模式查看了一下延時時間,發現和DataSheet以及注釋都對不上,讓我有點想不明白。
這兩個時間差也不夠60us啊,Project Option那晶振頻率設的也是11.0592MHz,這個問題確實沒搞懂
Screenshot 2021-10-24 210102.png (55.89 KB, 下載次數: 65)
下載附件
2021-10-24 21:01 上傳
Screenshot 2021-10-24 210119.png (54.32 KB, 下載次數: 48)
下載附件
2021-10-24 21:01 上傳
|