最近在学习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);
复制代码 运行结果如下:
按照DataSheet,一位数据的读/要持续最少60us,最大120us的时间。
如果我将字节写入部分改成:
- DS18B20_IO=temp&0x01;
- //Delay_Us(10);
- Delay_Us(60);
复制代码 运行结果如下:
我又参考了一下该开发板上外设的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,这个问题确实没搞懂
|