用51单片机的一个IO口模拟单总线时序与温度传感器DS18B20通信【Proteus】【普中51开发板】【Keil】

用51单片机的一个I/O口模拟单总线时序与温度传感器DS18B20通信【Proteus】【普中51开发板】【Keil】

DS18B20介绍

DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。DS18B20的测量范围是−55~128℃。

实验电路

本次实验的电路如下图。其中74LS47是BCD-7段译码器/驱动器,用于将单片机P0口输出欲显示的BCD码转化成相应的数字显示的段码,并直接驱动LED数码管显示。

在这里插入图片描述

实验程序

在Keil中编写以下程序,编译出hex文件并将其导入单片机。

Proteus实验程序

#include "reg51.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int 
#define out P0
sbit smg1=out^4;
sbit smg2=out^5;
sbit DQ=P3^7;
void delay5(uchar);
void init_ds18b20(void);
uchar readbyte(void);
void writebyte(uchar);
uchar retemp(void);

void main(void)			       //主函数
{
	uchar i,temp;
	delay5(1000);
	while(1)
	{
		temp=retemp();	
		for(i=0;i<10;i++)			//连续扫描数码管10次
		{
			out=(temp/10)&0x0f;
			smg1=0;
			smg2=1;
			delay5(1000);			//延时5ms
			out=(temp%10)&0x0f;
			smg1=1;
			smg2=0;
			delay5(1000);			//延时5ms
		}
	}
}
void delay5(uchar n)			//函数功能:延时5µs	
{
	do
	{
		_nop_();
		_nop_();
		_nop_();
		n--;
	}
	while(n);
}
void init_ds18b20(void)		//函数功能:18B20初始化
{
	uchar x=0; 
	DQ =0;    
	delay5(120); 
	DQ =1;    
	delay5(16);
	delay5(80);
}
uchar readbyte(void) 			//函数功能:读取1字节数据
{
	uchar i=0;
	uchar date=0;
	for (i=8;i>0;i--)
	{
		DQ =0;
		delay5(1);
		DQ =1;			//15µs内拉释放总线
		date>>=1;
		if(DQ)
		date|=0x80;
		delay5(11);
	}
	return(date);
}
void writebyte(uchar dat) 			//函数功能:写1字节
{
	uchar i=0;
	for(i=8;i>0;i--)
	{
		DQ =0;
		DQ =dat&0x01;				//写"1" 在15µs内拉低
		delay5(12);	   			//写"0" 拉低60µs
		DQ = 1;	   
		dat>>=1;
		delay5(5);
	}
}
uchar retemp(void)			//函数功能:读取温度
{
	uchar a,b,tt;
	uint t;
	init_ds18b20();
	writebyte(0xCC); 
	writebyte(0x44);
	init_ds18b20();
	writebyte(0xCC); 
	writebyte(0xBE); 
	a=readbyte();
	b=readbyte();
	t=b;
	t<<=8;
	t=t|a;
	tt=t*0.0625;
	return(tt);
}

普中51开发板实验程序

(以下程序均来自普中开发板资料)

主函数

main.c:

#include "public.h"
#include "smg.h"
#include "ds18b20.h"

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u8 i=0;
   	int temp_value;
	u8 temp_buf[5];

	ds18b20_init();//初始化DS18B20

	while(1)
	{				
		i++;
		if(i%50==0)//间隔一段时间读取温度值,间隔时间要大于温度传感器转换温度时间
			temp_value=ds18b20_read_temperture()*10;//保留温度值小数后一位
		if(temp_value<0)//负温度
		{
			temp_value=-temp_value;
			temp_buf[0]=0x40;//显示负号	
		}
		else
			temp_buf[0]=0x00;//不显示
		temp_buf[1]=gsmg_code[temp_value/1000];//百位
		temp_buf[2]=gsmg_code[temp_value%1000/100];//十位
		temp_buf[3]=gsmg_code[temp_value%1000%100/10]|0x80;//个位+小数点
		temp_buf[4]=gsmg_code[temp_value%1000%100%10];//小数点后一位
		smg_display(temp_buf,4);
	}		
}
数码管显示

smg.c:

#include "smg.h"

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
				0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名       : smg_display
* 函数功能		 : 动态数码管显示
* 输    入       : dat:要显示的数据
				   pos:从左开始第几个位置开始显示,范围1-8
* 输    出    	 : 无
*******************************************************************************/
void smg_display(u8 dat[],u8 pos)
{
	u8 i=0;
	u8 pos_temp=pos-1;

	for(i=pos_temp;i<8;i++)
	{
	   	switch(i)//位选
		{
			case 0: LSC=1;LSB=1;LSA=1;break;
			case 1: LSC=1;LSB=1;LSA=0;break;
			case 2: LSC=1;LSB=0;LSA=1;break;
			case 3: LSC=1;LSB=0;LSA=0;break;
			case 4: LSC=0;LSB=1;LSA=1;break;
			case 5: LSC=0;LSB=1;LSA=0;break;
			case 6: LSC=0;LSB=0;LSA=1;break;
			case 7: LSC=0;LSB=0;LSA=0;break;
		}
		SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
		delay_10us(100);//延时一段时间,等待显示稳定
		SMG_A_DP_PORT=0x00;//消音
	}
}

smg.h:

#ifndef _smg_H
#define _smg_H

#include "public.h"


#define SMG_A_DP_PORT	P0	//使用宏定义数码管段码口

//定义数码管位选信号控制脚
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

extern u8 gsmg_code[17];

void smg_display(u8 dat[],u8 pos);

#endif
DS18B20相关

ds18b20.c:

#include "ds18b20.h"
#include "intrins.h"

/*******************************************************************************
* 函 数 名         : ds18b20_reset
* 函数功能		   : 复位DS18B20  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void ds18b20_reset(void)
{
	DS18B20_PORT=0;	//拉低DQ
	delay_10us(75);	//拉低750us
	DS18B20_PORT=1;	//DQ=1
	delay_10us(2);	//20US
}

/*******************************************************************************
* 函 数 名         : ds18b20_check
* 函数功能		   : 检测DS18B20是否存在
* 输    入         : 无
* 输    出         : 1:未检测到DS18B20的存在,0:存在
*******************************************************************************/
u8 ds18b20_check(void)
{
	u8 time_temp=0;

	while(DS18B20_PORT&&time_temp<20)	//等待DQ为低电平
	{
		time_temp++;
		delay_10us(1);	
	}
	if(time_temp>=20)return 1;	//如果超时则强制返回1
	else time_temp=0;
	while((!DS18B20_PORT)&&time_temp<20)	//等待DQ为高电平
	{
		time_temp++;
		delay_10us(1);
	}
	if(time_temp>=20)return 1;	//如果超时则强制返回1
	return 0;
}

/*******************************************************************************
* 函 数 名         : ds18b20_read_bit
* 函数功能		   : 从DS18B20读取一个位
* 输    入         : 无
* 输    出         : 1/0
*******************************************************************************/
u8 ds18b20_read_bit(void)
{
	u8 dat=0;
	
	DS18B20_PORT=0;
	_nop_();_nop_();
	DS18B20_PORT=1;	
	_nop_();_nop_(); //该段时间不能过长,必须在15us内读取数据
	if(DS18B20_PORT)dat=1;	//如果总线上为1则数据dat为1,否则为0
	else dat=0;
	delay_10us(5);
	return dat;
} 

/*******************************************************************************
* 函 数 名         : ds18b20_read_byte
* 函数功能		   : 从DS18B20读取一个字节
* 输    入         : 无
* 输    出         : 一个字节数据
*******************************************************************************/
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;	
}

/*******************************************************************************
* 函 数 名         : ds18b20_write_byte
* 函数功能		   : 写一个字节到DS18B20
* 输    入         : 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
		{
			DS18B20_PORT=0;
			delay_10us(6);
			DS18B20_PORT=1;
			_nop_();_nop_();	
		}	
	}	
}

/*******************************************************************************
* 函 数 名         : ds18b20_start
* 函数功能		   : 开始温度转换
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void ds18b20_start(void)
{
	ds18b20_reset();//复位
	ds18b20_check();//检查DS18B20
	ds18b20_write_byte(0xcc);//SKIP ROM
    ds18b20_write_byte(0x44);//转换命令	
}

/*******************************************************************************
* 函 数 名         : ds18b20_init
* 函数功能		   : 初始化DS18B20的IO口 DQ 同时检测DS的存在
* 输    入         : 无
* 输    出         : 1:不存在,0:存在
*******************************************************************************/ 
u8 ds18b20_init(void)
{
	ds18b20_reset();
	return ds18b20_check();	
}

/*******************************************************************************
* 函 数 名         : ds18b20_read_temperture
* 函数功能		   : 从ds18b20得到温度值
* 输    入         : 无
* 输    出         : 温度数据
*******************************************************************************/
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;
}

ds18b20.h:

#ifndef _ds18b20_H
#define _ds18b20_H

#include "public.h"

//管脚定义
sbit DS18B20_PORT=P3^7;	//DS18B20数据口定义


//函数声明
u8 ds18b20_init(void);
float ds18b20_read_temperture(void);

#endif
其它公有函数

public.c:

#include "public.h"

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : delay_ms
* 函数功能		 : ms延时函数,ms=1时,大约延时1ms
* 输    入       : ms:ms延时时间
* 输    出    	 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}

public.h:

#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;


void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif

实验结果及分析

Proteus电路仿真

实验时,点击DS18B20上的上下箭头 (↑) (↓) 增减温度。理论上在任意温度下LED数码管显示的数值与DS18B20显示的数值相等。

Proteus电路仿真结果如下。可以看到该电路的输出并不稳定,数码管显示的数值实际在15与设定数值间反复横跳。

在这里插入图片描述

普中51开发板

用手捏开发板上的DS18B20温度传感器,上方数码管显示的温度值升高。

【普中51开发板】温度传感器DS18B20运行效果

Keil波形仿真及时序分析

Proteus程序编译完成后,在Keil中点击 “Debug”,加载出调试界面后点击 “Logic Analyzer”,点击 “Setup”,添加实验信号 “DQ”,然后点击 “Run” 即开始运行,产生该信号的波形图。点击 “stop” 停止运行,同时停止产生波形图。

DQ信号的波形图如下。

在这里插入图片描述

将波形图放大,分析时长。

DS18B20手册规定的初始化时序如下图所示。

在这里插入图片描述

放大后的波形图前面是初始化过程,该过程产生的时序如下图所示。两条画线之间的时间间隔为0.30ms左右,这段时间为单总线上的初始化过程,此时主机输出低电平,并保持低电平时间超过480μs,所以产生复位脉冲。

在这里插入图片描述

如下图所示,在产生复位信号后,主机释放总线,外部上拉电阻将单总线拉高,并延时了0.25ms左右,此时进入接收模式。相比于要求延时15 ~ 60μs的规定,本次演示远远超出了规定,拉高了190 ~ 235μs,明显这是不符合规定和技术要求的。

在这里插入图片描述

如下图所示,在进入接收模式后,DS18B20会拉低单总线,并延时了35.5μs左右,同时产生了低电平应答脉冲,相比于要求延时60 ~ 240μs的规定,本次演示远远超出了规定,拉低了25 ~ 205μs,明显这是不符合规定和技术要求的。

在这里插入图片描述

波形图后面是读写过程。DS18B20手册规定的读写时序如下图所示,上者是写时序,下者是读时序。

在这里插入图片描述

写程序如下图所示。写0时序时,主机输出低电平,延时35.5μs,与规定的延时60μs不符。

在这里插入图片描述

然后释放总线并延时18.5μs,与规定的延时2μs不符。

在这里插入图片描述

如下图所示,写1时序时,主机输出低电平,延时2μs。

在这里插入图片描述

写1时序延时2μs后,主机释放单总线,同时延时52μs,略低于规定的60μs。

在这里插入图片描述

读程序如下图所示。主机发起读时序到采样总线状态总共用时35μs,其中前1μs为主机拉低总线并延时所用时间,前15μs为采样总线状态开始时间,所以采样总线完成时间为20μs。

在这里插入图片描述

如下图所示,采样总线状态后,外部上拉电阻拉高总线,并延时18μs。

在这里插入图片描述

综上,本次仿真实验的各时序如下:

初始化时序:低电平300μs—高电平250μs—低电平35.5μs—高电平18.5μs;

写时序:低电平35.5μs—高电平18.5μs—低电平2μs—高电平52μs;

读时序:低电平35μs—高电平18.5μs。

总结

在学习温度传感器中,时序是非常重要的概念,理解它尤为重要。在之后的实操中,想让传感器工作首先就得写时序,所以还需要更加深入的学习。

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简51单片机温度传感器DS18B20的温控程序: ```c #include <reg51.h> #define DQ P3_7 // 定义总线管脚连接到片机的IO口 void delay(unsigned int t) { while(t--); } void writeByte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { DQ = 0; // 发送开始信号 delay(1); DQ = dat & 0x01; // 发送数据位 dat >>= 1; delay(5); DQ = 1; // 发送结束信号 delay(1); } } unsigned char readByte() { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { DQ = 0; // 发送开始信号 delay(1); DQ = 1; // 发送读取命令 delay(3); dat >>= 1; // 读取数据位 if (DQ) { dat |= 0x80; } delay(4); } return dat; } void initDS18B20() { writeByte(0xcc); // 跳过ROM命令 writeByte(0x4e); // 发送写Scratchpad命令 writeByte(0x4b); // 设置温度上限为75度 writeByte(0x00); // 设置温度下限为0度 writeByte(0x1f); // 设置分辨率为12位 writeByte(0x00); // 发送结束信号 } void readTemperature(unsigned char *temp) { unsigned char i; writeByte(0xcc); // 跳过ROM命令 writeByte(0x44); // 发送温度转换命令 delay(500); // 等待转换完成 writeByte(0xcc); // 跳过ROM命令 writeByte(0xbe); // 发送读取Scratchpad命令 for (i = 0; i < 2; i++) { temp[i] = readByte(); // 读取温度值 } } void main() { unsigned char temp[2]; initDS18B20(); // 初始化DS18B20传感器 while(1) { readTemperature(temp); // 读取温度值 if (temp[1] > 0x7f) // 判断温度的符号位 { temp[1] = ~temp[1] + 1; // 如果温度为负数,进行补码运算 temp[0] = ~temp[0]; temp[0]++; if (temp[0] == 0x00) { temp[1]++; } } // 这里可以根据实际需求,进行温度控制操作 } } ``` 以上程序基于OneWire协议与DS18B20传感器进行通信,实现了DS18B20传感器的初始化和温度读取功能。在主函数中,可以根据实际需求进行温度控制操作。需要注意的是,读取温度值的函数返回的温度值是一个两字节的整数,需要进行符号位和补码运算才能得到正确的温度值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值