实验七:基于89C51和DS18B20的温度采集与显示

      一、实验目的

学习使用DS18B20数字温度传感器采集温度数据。
使用4位共阳极数码管显示温度数据,显示精度到小数点后两位。
熟悉89C51单片机的I/O口操作和位选控制。

二、实验器材

89C51单片机开发板
DS18B20数字温度传感器
4位共阳极数码管
三极管8550(用于位选驱动)
电阻、电容等辅助元件
杜邦线若干

三、硬件连接

(1)数码管连接:

P1.0 – P1.7:分别连接数码管的A – G段和DP(小数点)。

P2.4、P2.5、P2.6、P2.7:分别控制数码管的不同位显示。

使用8550(PNP型)作为位选驱动,控制数码管位选通。

(2)DS18B20连接

DQ端连接89c51的P2.3

四、软件设计

DS18B20的工作步骤可以分为三步:

初始化DS18B20
执行ROM指令
执行DS18B20功能指令

      其中第二步执行ROM指令,也就是访问每个DS18B20,搜索64位序列号,读取匹配的序列号值,然后匹配对应的DS18B20,如果我们仅仅使用单个DS18B20,可以直接跳过ROM指令。而跳过ROM指令的字节是0xCC。

五、实验结果

通过上述代码,数码管应能正确显示温度数值,包括小数点后一位,满足实验要求。

 

 

六、注意事项

确保硬件连接正确,尤其是位选控制(P2.4 – P2.7)和电流放大(8550驱动)。
定期检查硬件连接,确保功能正常。

提交:(1)C51代码实现  (2)仿真或实验图 (3)实验总结

七、补充材料

 (1)DS18B20温度传感器特点

使用电压范围更宽,电压范围:3.0V ~ 5.5V,在寄生电源方式下可由数据线供电。
独特的单线接口方式,DS18B20在与微处理器连接时仅需要一条口线。
DS18B20支持多点组网功能,多个DS18B20可以并联8 个在唯一的三线上,实现组网多点测温。
DS18B20在使用中不需要任何外围元件,全部传感器元件及转换电路集成在一只集成电路内。
温度范围支持-55°C~+125°C,在-10 ~ +85°C时精度为±0.5°C。
可编程的分辨率为9 ~ 12位,对应的可分辨温度分别为0.5°C、0.25°C、0.125°C和0.0625°C
在9位分辨率时最多在93.75ms内,12位分辨率时最多在750ms内把温度值转换为数字。
测量结果直接输出数字温度信号,以“一根总线”串行传送给CPU,同时可传送CRC校验码。
 掉电保护功能,内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度。
在DS18B20温度传感器掉电以后仍可保存分辨率及报警温度的设定值。
 DS18B20温度传感器返回16位二进制数代表此刻温度值,其高五位(同时为0或1)代表正负。
后面11位数据代表温度绝对值,将其转换为十进制数值之后,再乘以0.0625 即可获得此时的温度值。
负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。

 

(2)DS18B20温度传感器引脚说明

DS18B20的温度转换规则

上电状态下默认精度是12位。DS18B20启动后保持低功耗等待状态,当需要执行温度测量和AD转换时,总线控制器必须发出[44h]命令。转换完以后,产生的温度数据以两个字节的形式被存储到告诉暂存器的温度寄存器中,DS18B20继续保持等待状态。读取温度寄存器中的数据,再根据精度就可以得到当前测得的温度。

计算温度

DS18B20温度传感器的内部存储器包括一个高速的暂存器RAM和一个非易失性的可电擦除的EEPROM,后者存放高温度和低温度触发器TH、TL和结构寄存器。

配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:

低五位是1。TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式。DS18B20出厂时该位被设置为0,用户不需要去改动。R1和R0用来设置DS18B20的精度(分辨率),可设置为9、10、11或12位,对应的分辨率温度是0.5°C、0.25°C、0.125°C和0.0625°C。R0和R1配置如下:

 

初始状态默认精度是12位,即R1R0=11。

高速暂存存储器由9个字节组成,分配如下:

 

当温度转换命令(44H)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。存储的两个字节,高字节的前5位是符号S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后。

如果测得的温度大于0,符号位S=0,将测试得到的数值乘以0.0625(默认精度)即可得到实际温度;

如果测得的温度小于0,符号位S=1,将测到的数据取反加1再乘以0.0625即可得到实际温度。

温度与数据对应关系如下:

 

上电复位时温度寄存器默认值为+85°C。

比如测试到的数据为0x0550,高5位是0表示测试到的温度是正温度,0x0550十进制是1360,将1360乘以0.0625等于85就是得到的温度值,即0x0550对应的是85°C。如果保留小数位,数值要乘以10。

初始化DS18B20

任何器件想要使用,首先就是需要初始化,对于DS18B20单总线设备,首先初始化单总线为高电平,然后总线开始也需要检测这条总线上是否存在DS18B20这个器件。如果这条总线上存在DS18B20,总线会根据时序要求返回一个低电平脉冲,如果不存在的话,也就不会返回脉冲,即总线保持为高电平。

4.1 初始化具体时序:

单片机拉低总线至少480us,产生复位脉冲,然后释放总线(拉高电平)等待15~60us。
这时DS8B20检测到请求之后,会拉低信号,大约60~240us表示应答。
DS8B20拉低电平的60~240us之间,单片机读取总线的电平,如果是低电平,那么表示初始化成功
DS18B20拉低电平60~240us之后,会释放总线。

 

4.2 代码如下:

bit Init_Ds18b20(void)

{

bit flag;

//复位ds18b20

DQ=0; //拉低DQ

Delay_10us(60); //60*9.3us=560us 拉低480-960us

DQ=1;         //DQ=1

Delay_10us(6); //5*9.3us=46us   等待15-60US

//检测DS的存在

presence = DQ; // 检测存在脉冲(0表示存在)

Delay_10us(20); // 等待剩余时间(总计480us)

return flag;

}

写时序

5.1 操作步骤

总线控制器通过控制单总线高低电平持续时间从而把逻辑1或0写DS18B20中。每次只传输1位数据
单片机想要给DS18B20写入一个0时,需要将单片机引脚拉低,保持低电平时间要在60~120us之间,然后释放总线
单片机想要给DS18B20写入一个1时,需要将单片机引脚拉低,拉低时间需要大于1us,然后在15us内拉高总线.
在写时序起始后15μs到60μs期间,DS18B20处于采样单总线电平状态。如果在此期间总线为高电平,则向DS18B20写入1;如果总线为低电平,则向DSl8B20写入0。

注意:2次写周期之间至少间隔1us

5.2 写一个字节时序

每个字节由8位组成,从最低位(LSB)开始发送
主机拉低总线至少1us
根据要写入的数据位(0或1)保持总线状态
对于写0:保持总线低电平60-120us
对于写1:拉低总线后很快释放(约15us)

 

代码示例:

// 向DS18B20写入一个字节

void  DS18B20_WriteByte(unsigned char dat) {

    unsigned char i;

    for(i=0; i<8; i++) {

        DQ = 0;

    _nop_();  //拉低持续1us ,不能超过15us

        DQ = dat & 0x01;

        delay_us(10);  //保持总线60-120us

        DQ = 1;

        dat >>= 1;

    }

}

5.3 写入的功能命令

ROM指令:
采用多个DS18B20时,需要写ROM指令来控制总线上的某个DS18B20
如果是单个DS18B20,直接写跳过ROM指令0xCC即可

RAM指令

温度转换 0x44

开启温度读取转换,读取好的温度会存储在高速暂存器的第0个和第一个字节中

读取温度 0xBE
读取高速暂存器存储的数据

读时序

主机拉低总线至少1us
然后释放总线
在15us内采样总线状态
每个读周期至少需要60us

 

代码示例:

// 从DS18B20读取一个字节

unsigned char DS18B20_ReadByte() {

    unsigned char i, byte = 0;

    for(i=0; i<8; i++) {

        DQ = 0;

        byte >>= 1;

        DQ = 1;

        if(DQ)  byte |= 0x80;

        delay_us(15);

    }

    return byte;

}

温度转换:

如果测得的温度大于0,符号位S=0,将测试得到的数值乘以0.0625(默认精度)即可得到实际温度;如果测得的温度小于0,符号位S=1,将测到的数据取反加1再乘以0.0625即可得到实际温度。

常用命令流程

复位
发送跳过ROM命令(0xCC)
发送启动温度转换命令(0x44)
等待转换完成(30ms)
复位
发送跳过ROM命令(0xCC)
发送读取暂存器命令(0xBE)
读取两个温度数据字节(低位在前,高位在后)
合并高低位temp=(MSB<<8)+LSB

代码示例:

float ds18b20_read_temperture(void)

{

    unsigned char LSB, MSB;//温度的高低字节

    int Temp;

    float temperature; //在外部定义为全局变量

    if(DS18B20_Init()) //复位   flag=DQ==1   失败

return -99;  // 初始化失败

ds18b20_write_byte(0xcc);//SKIP ROM

ds18b20_write_byte(0x44);//启动温度转换

delay_ms(30); // 等待转换完成

if(DS18B20_Init()) return -99; // 复位,初始化失败

ds18b20_write_byte(0xcc);//SKIP ROM

    ds18b20_write_byte(0xbe);//读存储器

LSB=ds18b20_read_byte();//低字节先读

MSB=ds18b20_read_byte();//高字节后读

temp=(MSB<<8)+LSB;//合并为16位数据

    if((MSB & 0xF8) == 0xF8) { // 温度为负

            flag_negative = 1;

            temp = ~temp + 1; // 取补码

           }

 else {

             flag_negative = 0;

             }

        temperature = temp * 0.0625; // 转换为实际温度  

  return temperature;

}

#include <reg51.h>
#include <intrins.h>

// 定义端口
sbit DQ = P2^3;       // DS18B20数据线接P2.3
sbit DIG1 = P2^4;     // 数码管位选1
sbit DIG2 = P2^5;     // 数码管位选2
sbit DIG3 = P2^6;     // 数码管位选3
sbit DIG4 = P2^7;     // 数码管位选4

// 数码管段码表(共阳数码管,0-9,带小数点)
unsigned char code seg_table[] = {
	0xC0, // 0
	0xF9, // 1
	0xA4, // 2
	0xB0, // 3
	0x99, // 4
	0x92, // 5
	0x82, // 6
	0xF8, // 7
	0x80, // 8
	0x90, // 9
	0xBF, // -
	0xff  //无
};

// 数码管位选表
unsigned char code dig_table[] = {
	0xef, //  - DIG1
	0xdf, //  - DIG2
	0xbf, //  - DIG3
	0x7f  //  - DIG4
};

// 全局变量
float temperature = 0.0;
bit flag_negative = 0;  // 温度是否为负
unsigned char temp_digits[4] = {0}; // 存储4位数码管显示的数字

// 延时函数 10us
void delay_us(unsigned int us) {
	while(us--)
		_nop_();
}

void delay_ms(unsigned int ms) {
	unsigned int i, j;
	for(i=0; i<ms; i++)
		for(j=0; j<110; j++);
}

// DS18B20初始化
bit DS18B20_Init() {
	bit flag;
	DQ = 0;
	delay_us(60);//600us
	DQ = 1;
	delay_us(5);//50us
	flag = DQ;
	delay_us(20);//200us  wait 
	return flag;
}

// 从DS18B20读取一个字节
unsigned char DS18B20_ReadByte() {
	unsigned char i, byte = 0;
	for(i=0; i<8; i++) {
		DQ = 0;
		byte >>= 1;
		DQ = 1;
		if(DQ) byte |= 0x80;
		delay_us(15);
	}
	return byte;
}

// 向DS18B20写入一个字节
void DS18B20_WriteByte(unsigned char dat) {
	unsigned char i;
	for(i=0; i<8; i++) {
		DQ = 0;
		_nop_();  //拉低持续1us ,不能超过15us
		DQ = dat & 0x01;
		delay_us(10);  //保持总线60-120us
		DQ = 1;
		dat >>= 1;
	}
}

// 读取温度值
float DS18B20_ReadTemp() {
	unsigned char LSB, MSB;
	int temp;
	// float temperature;  //定位为全局变量
	
	if(DS18B20_Init()) return -99; // 初始化失败
	
	DS18B20_WriteByte(0xCC); // 跳过ROM
	DS18B20_WriteByte(0x44); // 启动温度转换
	
	delay_ms(30); // 等待转换完成
	
	if(DS18B20_Init()) return -99; // 初始化失败
	
	DS18B20_WriteByte(0xCC); // 跳过ROM
	DS18B20_WriteByte(0xBE); // 读取温度寄存器
	
	LSB = DS18B20_ReadByte();
	MSB = DS18B20_ReadByte();
	
	temp=(MSB<<8)+LSB;//合并为16位数据
	
	if((MSB & 0xF8) == 0xF8) { // 温度为负
		flag_negative = 1;
		temp = (~temp) + 1; // 取补码
	} else {
		flag_negative = 0;
	}
	
	temperature = temp * 0.0625; // 转换为实际温度
	
	return temperature;
}

// 数码管显示函数
void Display_Temp() {
	unsigned char i;
	int int_temp;
	
	// 获取温度值
	temperature = DS18B20_ReadTemp();
	
	// 处理温度显示
	if(flag_negative) {
		// 负温度
		temp_digits[0] = 10; // 显示"-"
	} else {
		// 正温度
		temp_digits[0] = 11; // 不显示
	}
	int_temp = (int)(temperature*10); // 放大10倍,小数点后显示一位
	
	// 分解数字
	temp_digits[3] = int_temp % 10;       // 个位
	temp_digits[2] = (int_temp / 10) % 10; // 十位
	temp_digits[1] = (int_temp / 100) % 10; // 百位(小数点前)
	
	// 数码管动态扫描显示
	for(i=0; i<4; i++) {
		switch(i) {
			case 0: // 最高位(符号或"无")
			P1 = seg_table[temp_digits[i]];
			P2 = dig_table[i];
			break;
			case 1: // 百位(整数部分)
			P1 = seg_table[temp_digits[i]];
			P2 = dig_table[i];
			break;
			case 2: // 十位(小数点前)
			P1 = seg_table[temp_digits[i]] & 0x7f; // 显示小数点
			P2 = dig_table[i];
			break;
			case 3: // 个位(小数点后)
			P1 = seg_table[temp_digits[i]];
			P2 = dig_table[i] ;
			break;
		}
		delay_ms(2); // 短暂延时
		P1 = 0xFF;   // 关闭数码管,防止鬼影
	}
}

// 主函数
void main() {
	while(1) {
		
		Display_Temp(); // 持续显示温度
	}
}

 

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容