目的:
使用DS18B20测量温度,并通过单片机在数码管上显示出来.(本次仿真使用的是一个传感器)
硬件内容:
DS18B20结构如下:
RAM中 64位序列号在出厂时已经设置好,相当于传感器的地址,用于一根总线上挂载多个传感器时,主设备搜索的依据。
配置寄存器用于选择传感器的运作方式和配置温度变化的精度,结构如下:
TM:模式测量位,该位为“0”时是工作模式,为“1”时是测量模式(默认为0,不需要改动)。
R0,R1:温度测量精度选择位。写入不同的位数,设置传感器的测量精度(默认精度为0.0625°C)
R0 | R1 | 温度测量精度 |
0 | 0 | 0.5°C |
0 | 1 | 0.25°C |
1 | 0 | 0.125°C |
1 | 1 | 0.0625°C |
高速缓存寄存器:由9个字节组成,但是本实验只需要前两个字节,传感器测量后温度会以二进制补码的形式将数据存放于前两个字节。高八位,前5位为符号位S,后11位为数据位。数据格式如下。
MS Byte:高八位
LS Byte:低八位
如果测得的温度大于 0,S位为‘ 0’,只要将测到的数值乘以 0.0625 (默认精度是 12 位)即可得到实际温度;如果温度小于 0,这 5 位为‘ 1’, 测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。温度与数据对应 关系如下:
设计思路:
通过DS18B20来测量温度,采用电源供电,然后讲数据传单片机并在数码管上实现显示。数据低位在前,高位在后。过程其实很简单,只需要记住几个时序就可以,分别为,写时序,读时序,初始化时序
测量过程:复位→发 SKIP ROM 命令(0XCC)→发开始转 换命令(0X44)→延时→复位→发送 SKIP ROM 命令(0XCC)→发读存储器命令 (0XBE)→连续读出两个字节数据(即温度)→结束
DS18B20:要实现数据的读取和命令的写入,首先需要初始化,然后是写时序和读时序。而两种时序又分别为写0、写1、读0、,读1;
初始化(传感器的一切指令起始于初始化):主机低电平,持续480us,主机高电平,延时15-60us
写时序(用于命令的写入):总时长至少60us,两次写时序之间间隔至少2us
写1:主机低电平,延时2us,主机高电平,延时60us
写0:主机低电平,延时60us,主机高电平,延时2us
读时序(用于温度数据的读取):主机输出低电平延时 2us,然后主机转入输入模式延 时 12us,然后读取单总线当前的电平,然后延时 50us。
软件内容:
DS18B20配置函数如下:
void ds18b20_reset(void)//初始化函数
{
DS=0; //拉低DQ
delay_10us(75); //拉低750us
DS=1; //DQ=1
delay_10us(2); //20US
}
u8 ds18b20_check(void)//检测函数
{
u8 time_temp=0;
while(DS&&time_temp<20) //等待DQ为低电平
{
time_temp++;
delay_10us(1);
}
if(time_temp>=20)return 1; //如果超时则强制返回1
else time_temp=0;
while((!DS)&&time_temp<20) //等待DQ为高电平
{
time_temp++;
delay_10us(1);
}
if(time_temp>=20)return 1; //如果超时则强制返回1
return 0;
}
u8 ds18b20_read_bit(void)//读取一位函数
{
u8 dat=0;
DS=0;
_nop_();_nop_();
DS=1;
_nop_();_nop_(); //该段时间不能过长,必须在15us内读取数据
if(DS)dat=1; //如果总线上为1则数据dat为1,否则为0
else dat=0;
delay_10us(5);
return dat;
}
u8 ds18b20_read_byte(void)//读取一个字节函数
{
u8 i=0;
u8 dat=0;
u8 temp=0;
for(i=0;i<8;i++)//循环8次,每次读取一位,从低到高
{
temp=ds18b20_read_bit();
dat=(temp<<7)|(dat>>1);
}
return dat;
}
void ds18b20_write_byte(u8 dat)//写命令函数
{
u8 i=0;
u8 temp=0;
for(i=0;i<8;i++)//循环8次,每次写一位,且先写低位再写高位
{
temp=dat&0x01;//选择低位准备写入
dat>>=1;//将次高位移到低位
if(temp)
{
DS18B20_PORT=0;
_nop_();_nop_();
DS18B20_PORT=1;
delay_10us(6);
}
else
{
DS=0;
delay_10us(6);
DS=1;
_nop_();_nop_();
}
}
}
void ds18b20_start(void)//开始转换函数
{
ds18b20_reset();//复位
ds18b20_check();//检查DS
ds18b20_write_byte(0xcc);//SKIP ROM
ds18b20_write_byte(0x44);//转换命令
}
u8 ds18b20_init(void)//初始化函数
{
ds18b20_reset();
return ds18b20_check();
}
float ds18b20_read_temperture(void)//温度处理函数
{
float temp;
u8 dath=0;
u8 datl=0;
u16 value=0;
ds18b20_start();//开始转换
ds18b20_reset();//复位
ds18b20_check();
ds18b20_write_byte(0xcc);//SKIP ROM
ds18b20_write_byte(0xbe);//读存储器
datl=ds18b20_read_byte();//低字节
dath=ds18b20_read_byte();//高字节
value=(dath<<8)+datl;//合并为16位数据
if((value&0xf800)==0xf800)//判断符号位,负温度
{
value=(~value)+1; //数据取反再加1
temp=value*(-0.0625);//乘以精度
}
else //正温度
{
temp=value*0.0625;
}
return temp;
}
主函数如下:
#include "public.h"
#include "smg.h"
#include "ds18b20.h"
u8 a[4]={0x01,0x02,0x04,0x08};
void main()
{
u8 i=0;
int temp_value;
u8 temp_buf[5];
ds18b20_init();//初始化DS18B20
while(1)
{
u8 j;
i++;
if(i%50==0)//间隔一段时间读取温度值,间隔时间要大于温度传感器转换温度时间
temp_value=ds18b20_read_temperture()*10;//保留温度值小数后一位
if(temp_value<0)//负温度
{
temp_value=-temp_value;
temp_buf[0]=0xbf;//显示负号
}
else
temp_buf[0]=0xff;//不显示
//temp_buf[1]=gsmg_code[temp_value/1000];//百位
temp_buf[1]=gsmg_code[temp_value%1000/100];//十位
temp_buf[2]=gsmg_code[temp_value%1000%100/10]&0x7f;//个位+小数点
temp_buf[3]=gsmg_code[temp_value%1000%100%10];//小数点后一位
//smg_display(temp_buf);
for(j=0;j<4;j++)
{
SMG_A_DP_PORT=temp_buf[j];
DIS=a[j];
delay_10us(100);
SMG_A_DP_PORT=0xff;
}
}
}
延时函数:
#include "public.h"
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
原理图: