STM32F103驱动单总线的DS18B20测量温度(含多点测量)

本文详细描述了如何使用STM32F103微控制器驱动DS18B20单总线温度传感器进行单点和多点温度测量,包括时序协议、驱动步骤和多点测量的实现方法,以及在实际应用中的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

本文基于STM32F103C8T6来驱动单总线传感器DS18B20来测量环境的温度,介绍了单点测量和多点测量的实现方法。
温度测量在生活中具有重要性。首先,温度是人们日常生活中的重要参考指标之一。我们需要测量室内室外的温度来确定穿衣搭配,合理调节室内温度,选择合适的季节性活动等。其次,温度测量对于食品安全和疾病预防也非常关键。在食品加工和储存过程中,正确测量和控制食品的温度可以防止食物腐败和细菌滋生。此外,在医疗领域,温度的测量可以帮助医生判断人体是否存在发热、感染等疾病。

二、DS18B20

说明

DS1820数字温度计提供9位温度读数,指示器件的温度。信息经过单线接口送入DSI820或从DS1820送出,因此从中央处理器到DSI820仅需连接一条线(和地)。读、写和完成温度变换所需的电源可以由数据线本身提供,而不需要外部电源。因为每一个DS1820有唯一的系列号(silicon serial number),因此多个DSI820可以存在于同一条单线总线上。这允许在许多不同的地方放置温度灵敏器件。此特性的应用范围包括HVAC环境控制,建筑物、设备或机械内的温度检测,以及过程监视和控制中的温度检测。
DS18B20的接线方法为:
VDD<------->MCU的3.3V
GND<------->MCU的GND
D0 <------->MCU的PA4,同时接一个上拉电阻

在这里插入图片描述

特性

1. 独特的单线接口,只需1个接口引脚即可通信,数据的传送是低位先行
2. 单总线实现多点测量
3. 可用数据线供电,不需备份电源
4. 测量范围从-55℃至+125℃,增量值为0.5℃
5. 以9位数字值方式读出温度
6. 在1秒(典型值)内把温度变换为数字·用户可定义的,非易失性的温度告警设置
7. 告警搜索命令识别和寻址温度在编定的极限之外的器件(温度告警情况)
8. 应用范围包括恒温控制,工业系统,消费类产品,温度计或任何热敏系统

三、驱动步骤

经过单线接口访问DS18B20的时序协议包含:复位和响应、写0、写1,读0、读1

(一)时序协议准备

1、复位和响应

单总线上的所有处理均从初始化开始的,初始化包括总线的主机(STM32F103)发出一个复位脉冲,再由从属器件发出存在脉冲。
初始化的时序如下图。首先,STM32的IO应该设置为推挽输出模式并拉低480~960us,IO拉高15us,再配置为浮空输入模式;然后等待高电平结束(DS18B20将总线拉低),最后再等待低电平结束(上拉电阻释放总线)。为了检测DS18B20是否存在或是否故障,初始化函数应该有返回值。
图1.初始化时序
temp.h文件内容如下:

#ifndef __TEMPER_H
#define __TEMPER_H

#include "stm32f10x.h"
#include "delay.h"

#define DS18B20_IO     GPIO_Pin_4
#define DS18B20_PORT   GPIOA
#define DS18B20_SET_IO(x)  (x) ? GPIO_SetBits(DS18B20_PORT, DS18B20_IO) : GPIO_ResetBits(DS18B20_PORT, DS18B20_IO)
#define DS18b20_READ_IO     GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_IO)
#define DS18B20_SKIP_ROM		0xcc	//跳过ROM的操作
#define DS18B20_GET_ROM	    	0x33	//读单个rom
#define DS18B20_MATCH_ROM	    0x55	//匹配rom
#define DS18B20_TEST_TEMP	    0x44	//启动温度变换
#define DS18B20_READ_TEMP	    0xBE	//读取温度

void init_led(void);
int rst_ds18b20(void);
float ds18b20_get_temper(void);
float ds18b20_get_tempers(u8 *rom);
void read_ds18b20_rom(void);
#endif

以下是函数,包含在temper.c 文件中。

//作用:初始化ds18b20的io为输出
//无返回值
//无输入参数
void init_ds18b20_out(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能B端口时钟
	GPIO_InitStructure.GPIO_Pin = DS18B20_IO;	//GPIOA4
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);	  //初始化GPIOA4
	
 	GPIO_SetBits(DS18B20_PORT, DS18B20_IO);	
}

//作用:初始化ds18b20的io为输入
//无返回值
//无输入参数
void init_ds18b20_in(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能B端口时钟
	GPIO_InitStructure.GPIO_Pin = DS18B20_IO;	//GPIOA4
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 		 //浮空输入
	
 	GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);	  //初始化GPIOA4
}

//作用:复位ds18b20和接收响应
//返回值:有响应返回0,无响应返回-1
int rst_ds18b20(void)	
{
	u16 wait = 0;  //等待响应的时间
	init_ds18b20_out();	//配置为输出
	DS18B20_SET_IO(0);	//主机拉低
	delay_us(600);	
	
	DS18B20_SET_IO(1);  //主机释放总线
	delay_us(40);
	
	init_ds18b20_in();	//io配置为输入
	
	while(DS18b20_READ_IO == 1)		//等待响应的低电平到来,等待时间为240us
	{
		wait ++;
		delay_us(2);
		if(wait >= 120)
			return -1;	//没有响应,终止
	}
	
	wait = 0;	//清零,为等待响应高电平做准备
	while(DS18b20_READ_IO == 0)		//等待响应的高电平到来,等待时间为240us
	{
		wait ++;
		delay_us(2);
		if(wait >= 100)
			return -1;	//没有响应,终止
	}
	return 0;	//有相应,返回0
}

2、写0和写1

由于DS18B20的单总线是低位先行的,写0和写1时,应该由低位到高位依次写入。首先STM32的IO要配置为推挽输出,从写0的角度思考:IO的电平拉低50us,然后再拉高1us以上;从写1的角度思考:IO的电平拉低1us以上,然后再拉高50us。图中的DS18B20 SAMPLES的TYP为DS18B20的典型采样时刻。
图2. 写0和写1时序

//作用:向ds18b20写入一个字节
//无返回值
//输入:@data,以十六进制写一个字节
void ds18b20_write(u8 data)	//写数据
{
	u8 temp = 0, i;
	init_ds18b20_out();	//先拉低时序,初始为高电平
	
	for(i = 0; i < 8; i ++)
	{
		temp = data & 0x01;   //提取最低位的数据
		if(!temp)	//写0
		{
			DS18B20_SET_IO(0);	//主机拉低
			delay_us(50);	
			DS18B20_SET_IO(1);	//主机拉高
			delay_us(2);
		}
		
		else	//写1
		{
			DS18B20_SET_IO(0);	//主机拉低
			delay_us(2);
			DS18B20_SET_IO(1);	//主机拉高
			delay_us(50);
		}
		data = (data >> 1);	//将次低位移动到最低位
	}
}

3、读0和读1

同样的,由于单总线的传送为低位先行。读取完数据后应该特别注意它的数据存储。读1/0时序:配置为推挽输出,拉低IO 2us,然后拉高IO 12us (释放总线,将主权给DS18B20。因为主机读取的最佳时刻在15us,拉低和拉高的时间在15us以内),将IO配置为浮空输入读取总线的电平(此时总线的电平由1820决定),最后再延时45us以上。
图3. 读0和读1时序



//作用:向ds18b20读一个字节
//返回值:temp,读取ds18b20的温度数据
//无输入
u8 ds18b20_read(void)	//低位先行
{
	u8 temp = 0, i, temp_data = 0;
	
	for(i = 0; i < 8; i ++)
	{
		init_ds18b20_out();	//配置为输出
		DS18B20_SET_IO(0);	//拉低
		delay_us(2);	//延时时序
		
		DS18B20_SET_IO(1);	//拉高释放,将主权给ds18b20
		delay_us(12);	//延时时序
		init_ds18b20_in();	//配置为输入
		if(DS18b20_READ_IO)	//读1, if可以去掉
		{
			temp_data = DS18b20_READ_IO;
			temp =  temp | (temp_data << i);	//将每次读到的数据往高移i位再加上temp
		}
		delay_us(50);
	}
	return temp;
}

(二)驱动操作步骤

1. 初始化
2. ROM操作命令
3. 存储器操作命令
4. 处理数据

单个DS18B20的初始化&ROM操作指令:

//作用:初始化ds18b20,发送一个指令前都要给一个复位信号,并发送rom指令
//无返回
//无输入
void init_ds18b20(void)
{
	rst_ds18b20();
	ds18b20_write(DS18B20_SKIP_ROM);	//跳过扫描rom指令
}

存储器操作命令&数据处理:

/*当总线上只有一个ds18b20时,单独读取其温度*/
//作用:当总线上只有一个ds18b20时,单独读取其温度
//返回值:@temp,返回完成扫描一次的温度值
//无输入
float ds18b20_get_temper(void)
{
	u8 temp_h, temp_l;
	float temp;
	u16 value;
	
	init_ds18b20();	//发送指令前先初始化

	ds18b20_write(DS18B20_TEST_TEMP);	//启动温度转换
	
	init_ds18b20();	//发送指令前先初始化
	
	ds18b20_write(DS18B20_READ_TEMP);	//发送读取指令
	
	temp_l = ds18b20_read();
	temp_h = ds18b20_read();
	value = (temp_h << 8) | temp_l;
	temp = (float) value / 16.0f;  //16.0f为单精度浮点型,占4byte;16.0为双精度浮点型,占8byte
	return temp;
}

(三)多点测量

DS18B20的单总线具备连续的64位ROM,且每个DS8B20的ROM是唯一的,可以当作设备的地址。因此要实现多点温度测量时需要对相应的DS18B20进行ROM匹配,匹配前要对每个单总线设备读取对应的ROM存储在数组中,读取哪个设备的温度就要匹配哪个的ROM值。这里运用到两个操作指令:

读取ROM33H
匹配ROM55H

读取单个单总线设备的ROM的函数如下:
调用以下函数就可以逐个读取DS18B20的ROM值,打印在 串口中,然后存储在数组中。

//作用:读取单个DS18B20的ROM值
//返回值:通过串口连续打印8个十六进制的ROM值
//无输入
void read_ds18b20_rom(void)
{
	u8 rom[8], i;
	rst_ds18b20();
	ds18b20_write(DS18B20_GET_ROM);	//读取单个ds18b20的rom
	for(i = 0; i < 8; i ++)
	{
		rom[i] = ds18b20_read();
		printf(" %#x", rom[i]);
	}
	printf("\r\n");
}

匹配单个单总线设备的ROM的函数如下:

//作用:匹配单个DS18B20的ROM
//输入:@rom,需要读取的设备温度的ROM地址
//无返回值
void ds18b20_match(u8 *rom)
{
	u8 i;
	rst_ds18b20();	//复位ds18b20
	ds18b20_write(DS18B20_MATCH_ROM);	//匹配ROM
	for(i = 0; i < 8; i ++)
	{
		ds18b20_write(* rom++);
	}
}

读取多总线设备的单个设备温度数据的函数:

/*当总线上多个ds18b20时,输入ROM读取对应设备的温度*/
//作用:总线有多个设备时使用
//返回值:@temp,返回完成扫描一次的温度值
//输入:需要读取的设备温度的ROM地址
float ds18b20_get_tempers(u8 *rom)
{
	u8 temp_h, temp_l;
	float temp;
	u16 value;
	
	ds18b20_match(rom);
	ds18b20_write(DS18B20_TEST_TEMP);	//启动温度转换
	
	ds18b20_match(rom);
	ds18b20_write(DS18B20_READ_TEMP);	//发送读取指令
	
	temp_l = ds18b20_read();
	temp_h = ds18b20_read();
	value = (temp_h << 8) | temp_l;
	temp = (float) value / 16.0f;  //16.0f为单精度浮点型,占4byte;16.0为双精度浮点型,占8byte
	return temp;
}

(四)程序效果图

将DS18B20的设备1和设备2检测的温度显示在0.91寸的OLED上。具体项目文件已上传链接:DS18B20测量多点温度project
在这里插入图片描述

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值