1. 先简要介绍本博客涉及的芯片:
(1)IAP15F2K61S2:蓝桥杯单片机赛道所用芯片,下文Code1来自蓝桥杯赛点资源包(做出了一些修改)。
(2)STC12C5A60S2:课程实验所用芯片,和IAP15F2K61S2都是宏晶公司的产品,因此该芯片也可以使用Code1读取ds18b20的温度。
(3)AT89S52:Proteus仿真所用芯片。
2. STC12C5A60S2读取温度
下面是Code1,来自蓝桥杯赛点资源包(做出了一些修改),包含三个函数,分别是延时、读、写一个字节。
#include <REGX51.H>
#include "intrins.h"
sbit DQ = P1^4;
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
Code1
下面是Code2,函数返回一个浮点型数据,即为温度。该函数可以直接编写在Code1下方
float read_temperature()
{
float tem=0;
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE);
tem=Read_DS18B20();
tem=Read_DS18B20()*256+tem;
tem=tem/16.0;
return tem;
}
Code2
使用Code1和Code2即可读出ds18b20的温度了。
3. AT89S52读取温度
老师要求有仿真结果,所以对Code1的Delay_OneWire函数做出修改,以适应Proteus的仿真。修改为:注释for循环语句,如Code3。下文会讲解为何注释掉for语句就可以正常读出温度。
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
// for(i=0;i<12;i++);
}
}
Code3
4. 为何注释掉for语句就可以在Proteus中使用AT89S52中读取ds18b20的温度?
(1)一个最简单的解释(不准确,因为我不是科班出身):
AT89是12T芯片,而STC12是1T芯片,即STC12的速度比AT89快12倍。在相同时间的延时中,STC12需要运行的代码量是AT89的12倍。如果将for循环注释掉刚好把代码量减少为原本的1/12。事实上,注释之后时间减少为1/18。
(2)波形图:
如果不注释for语句就直接跑仿真,init_ds18b20()函数中的Delay_OneWire(80)的时长为11.05ms(见图1波形图),温度显示为4095,而这个时长一般为600us(查手册可知480~960us都可以,这里折中一下)。
前者是后者的18.5倍。为了将时间缩短,我们将"i<12"改为"i<1"试试看能不能正确读出温度。结果见图2。测量得到时间为2.25ms。哎不对啊,我明明缩了12倍了啊,咋才变成1/5啊?原来for循环语句包含读取、判断、写入功能,所以for(i=0;i<1;i++);也是很占时间的。
那我们把for语句注释看看能不能成功,要是不行只能重写延时函数了。
最后发现:注释for语句后的时间刚好为730us。可以读出正确温度。
图1
图2
图3