STM32F103驱动单总线的DS18B20测量温度&多点测量
一、前言
本文基于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是否存在或是否故障,初始化函数应该有返回值。
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的典型采样时刻。
//作用:向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以上。
//作用:向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值。这里运用到两个操作指令:
读取ROM | 33H |
---|---|
匹配ROM | 55H |
读取单个单总线设备的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