目录
一、DS18B20模块简介
1、DS1820数字温度计
2、DS18B20模块特性
(1)单线接口仅需要一个端口引脚进行通讯(即单总线接口方式);
(2)无需外部器件;
(3)测温范围:-55 ~ +125 ℃ ,以0.5℃递增;-67 ~ +257 ℉ ,以0.9 ℉ 递增;
(4)温度以9为数字量读出;
(5)温度数字量转换时间200ms (典型值);
(6)零待机功耗 ;
(7)分辨率调整范围:9 ~ 12 位;
(8)用户可定义的非易失性温度报警设置 ;
(9)报警搜索命令识别并标志超过程序限定温度(温度报警条件)的器件;
(10)应用包括温度控制、工业系统、消费品、温度计或任何热感测系统。
3、工作原理
(1)信号类型:复位脉冲、应答脉冲、写0、写1、读0和读1;(所有这些信号,除了应答脉冲以外,都由主机发出同步信号,并且发送所有的命令和数据都是字节的低位在前);
(2)工作时序
a、初始化时序
主机发送复位脉冲后检测从机给主机返回的存在脉冲,检测到正确脉冲表明器件初始化 成功。
主机输出产生复位脉冲(输出低电平保持时间至少480us),主机进入接收模式 (释放总线,4.7k上拉电阻将总线拉高,延时15 ~ 60 us ),低电平应答脉冲(等待拉低总线约240us内),然后由于上拉电阻作用(释放总线240us内)。
b、写时序
写时序“ 0 ” :输出低电平延时60us ,然后释放总线延时2us;
写时序“ 1 ” :输出低电平延时2us,然后释放总线延时60us。
c、读时序
主机输出低电平延时2us,再转入输入模式延时12us。读取总线电平,再延时50us。
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令 后,必须马上产生读时序,以便从机能够传输数据。
所有读时序至少需要60us。且在2次独立的读时序之间至少需要1us的恢复时间,每个读 时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。
4、数据格式
MSB最高位S为符号位,负温度S = 1 ,正温度S = 0;
从而得到以下的表格
5、功能命令集
二、单总线通信的工作原理与使用方法
1、单总线通信简介
单总线是一种半双工通信方式,采用单根信号线,既传输时钟,又传输数据,且数据传 输是双向的,具有节省I/O口线资源、结构简单、成本低廉、便于总线扩展和维护等优点。
适用于单个主机系统,能够控制一个或多个从机设备。当只有一个从机位于总线上时, 系统可按照单节点系统操作;而当多个从机位于总线上时,则系统按照多节点系统操作。
2、硬件结构
设备(主机或从机)通过一个漏极开路或三态端口,连接至该数据线,这样允许设备在不发送数据时释放数据总线,以便总线被其他设备所使用,单总线端口为漏极开路。
总线要求外接一个约 5k 的上拉电阻。这样,单总线的闲置状态为高电平,不管什么原因,如果传输过程需要暂时挂起,且要求传输过程还能够继续的话,则总线必须处于空闲状态。位传输之间的恢复时间没有限制,只要总线在恢复期间处于空闲状态(高电平)。
3、命令序列
典型的单总线命令序列如下:
第一步:初始化;
第二步:ROM命令(跟随需要交换的数据);
第三步:功能命令(跟随需要交换的数据)。
每次访问单总线器件,必须严格遵守这个命令序列,如果出现序列混乱,则单总线器件不会响应主机。但是,这个准则对于搜索ROM命令和报警搜索命令例外,在执行两者中任何一条命令之后,主机不能执行其后的功能命令,必须返回至第一步。
4、信号方式
所有的单总线器件要求采用严格的通信协议 ,以保证数据的完整性。该协议定义了几种信号类型:复位脉冲、应答脉冲、写0、写1、读0和读1。所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前,这一点与多数串行通信格式不同(多数为字节的高位在前)。
具体分析见本文1-3-2 。
三、程序设计
1、Ds1820s.c
#include "ds18b20.h"
#include "stm32f10x.h"
/**************************************************************************************
* 描 述 : 配置DS18B20用到的I/O口
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DS18B20_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
GPIO_SetBits(DS18B20_PORT, DS18B20_PIN); //DS18B20数据引脚初始化配置为高电平输出
}
/**************************************************************************************
* 描 述 : 配置使DS18B20-DATA引脚变为输入模式
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
/**************************************************************************************
* 描 述 : 配置使DS18B20-DATA引脚变为输出模式
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
/**************************************************************************************
* 描 述 : 主机给从机发送复位脉冲
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
static void DS18B20_Rst(void)
{
DS18B20_Mode_Out_PP(); //主机设置为推挽输出
DS18B20_DATA_OUT(LOW); //主机至少产生480us的低电平复位信号
Delay_us(750);
DS18B20_DATA_OUT(HIGH); //主机在产生复位信号后,需将总线拉高
Delay_us(15); //从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
}
/**************************************************************************************
* 描 述 : 检测从机给主机返回的存在脉冲
* 入 参 : 无
* 返回值 : 0:成功 1:失败
**************************************************************************************/
static u8 DS18B20_Presence(void)
{
u8 pulse_time = 0;
DS18B20_Mode_IPU(); //主机设置为上拉输入
/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号
* 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
*/
while( DS18B20_DATA_IN() && pulse_time<100 )
{
pulse_time++;
Delay_us(1);
}
if( pulse_time >=100 ) //经过100us后,存在脉冲都还没有到来
return 1; //读取失败
else //经过100us后,存在脉冲到来
pulse_time = 0; //清零计时变量
while( !DS18B20_DATA_IN() && pulse_time<240 ) // 存在脉冲到来,且存在的时间不能超过240us
{
pulse_time++;
Delay_us(1);
}
if( pulse_time >=240 ) // 存在脉冲到来,且存在的时间超过了240us
return 1; //读取失败
else
return 0;
}
/**************************************************************************************
* 描 述 : 从DS18B20读取一个bit
* 入 参 : 无
* 返回值 : u8
**************************************************************************************/
static u8 DS18B20_Read_Bit(void)
{
u8 dat;
/* 读0和读1的时间至少要大于60us */
DS18B20_Mode_Out_PP();
/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
DS18B20_DATA_OUT(LOW);
Delay_us(10);
/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
DS18B20_Mode_IPU();
if( DS18B20_DATA_IN() == SET )
dat = 1;
else
dat = 0;
/* 这个延时参数请参考时序图 */
Delay_us(45);
return dat;
}
/**************************************************************************************
* 描 述 : 从DS18B20读一个字节,低位先行
* 入 参 : 无
* 返回值 : u8
**************************************************************************************/
u8 DS18B20_Read_Byte(void)
{
u8 i, j, dat = 0;
for(i=0; i<8; i++)
{
j = DS18B20_Read_Bit(); //从DS18B20读取一个bit
dat = (dat) | (j<<i);
}
return dat;
}
/**************************************************************************************
* 描 述 : 写一个字节到DS18B20,低位先行
* 入 参 : u8
* 返回值 : 无
**************************************************************************************/
void DS18B20_Write_Byte(u8 dat)
{
u8 i, testb;
DS18B20_Mode_Out_PP();
for( i=0; i<8; i++ )
{
testb = dat&0x01;
dat = dat>>1;
/* 写0和写1的时间至少要大于60us */
if (testb)
{
DS18B20_DATA_OUT(LOW);
Delay_us(8); //1us < 这个延时 < 15us
DS18B20_DATA_OUT(HIGH);
Delay_us(58); //58us+8us>60us
}
else
{
DS18B20_DATA_OUT(LOW);
/* 60us < Tx 0 < 120us */
Delay_us(70);
DS18B20_DATA_OUT(HIGH);
/* 1us < Trec(恢复时间) < 无穷大*/
Delay_us(2);
}
}
}
/**************************************************************************************
* 描 述 : 起始DS18B20
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void DS18B20_Start(void)
{
DS18B20_Rst(); //主机给从机发送复位脉冲
DS18B20_Presence(); //检测从机给主机返回的存在脉冲
DS18B20_Write_Byte(0XCC); // 跳过 ROM
DS18B20_Write_Byte(0X44); // 开始转换
}
/**************************************************************************************
* 描 述 : DS18B20初始化函数
* 入 参 : 无
* 返回值 : u8
**************************************************************************************/
u8 DS18B20_Init(void)
{
DS18B20_GPIO_Config();
DS18B20_Rst();
return DS18B20_Presence();
}
/**************************************************************************************
* 描 述 : 从DS18B20读取温度值
* 入 参 : 无
* 返回值 : float
**************************************************************************************/
float DS18B20_Get_Temp(void)
{
u8 tpmsb, tplsb;
short s_tem;
float f_tem;
DS18B20_Rst();
DS18B20_Presence();
DS18B20_Write_Byte(0XCC); /* 跳过 ROM */
DS18B20_Write_Byte(0X44); /* 开始转换 */
DS18B20_Rst();
DS18B20_Presence();
DS18B20_Write_Byte(0XCC); /* 跳过 ROM */
DS18B20_Write_Byte(0XBE); /* 读温度值 */
tplsb = DS18B20_Read_Byte();
tpmsb = DS18B20_Read_Byte();
s_tem = tpmsb<<8;
s_tem = s_tem | tplsb;
if( s_tem < 0 ) /* 负温度 */
f_tem = (~s_tem+1) * 0.0625;
else
f_tem = (s_tem * 0.625);
//这样做的目的将小数点后第一位也转换为可显示数字
//同时进行一个四舍五入操作。
return f_tem/10.0;
}
2、Ds1820s.h
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f10x.h"
#include "delay.h"
#define HIGH 1
#define LOW 0
#define DS18B20_CLK RCC_APB2Periph_GPIOB
#define DS18B20_PIN GPIO_Pin_12
#define DS18B20_PORT GPIOB
//带参宏,可以像内联函数一样使用,输出高电平或低电平
#define DS18B20_DATA_OUT(a) if (a) \
GPIO_SetBits(GPIOB,GPIO_Pin_12);\
else \
GPIO_ResetBits(GPIOB,GPIO_Pin_12)
//读取引脚的电平
#define DS18B20_DATA_IN() GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)
typedef struct
{
u8 temp_int; //温度的整数部分
u8 temp_deci; //温度的小数部分
u8 check_sum; //校验和
}DS18B20_Data_TypeDef;
u8 DS18B20_Init(void);
float DS18B20_Get_Temp(void);
#endif /* __DS18B20_H */
3、main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Ds18b20.h"
uint8_t temp[17];
float temp_data = 0.0 ;
void TempGetNum ()
{
temp_data=DS18B20_Get_Temp();
sprintf((char*)temp,"%4.2f",temp_data);
OLED_ShowString(1,6,temp);
}
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
OLED_ShowString(1,1,"Temp:");
while (DS18B20_Init())
{
OLED_ShowString(1,1,"DS18B20 Error");
}
while (1)
{
TempGetNum();
Delay_ms(500);
}
}