不介绍原理,自行百度或必应,可参考 DS18B20温度传感器原理详解
数据手册:DS18B20(嘉立创) 中文版:DS18B20(中文版)
文件结构参考正点原子即可
1 硬件连接
2 部分代码示例
2.1 宏定义与头文件
#ifndef __DS18B20_H
#define __DS18B20_H
#include "public.h"
#define DS18B20_GPIO GPIOB
#define DS18B20_PIN_TX GPIO_Pin_10
#define DS18B20_PIN_RX GPIO_Pin_11
typedef enum
{
DS18B20_RES_9BITS = 0x1F, // 温度转换分辨率 9 bits
DS18B20_RES_10BITS = 0x3F,
DS18B20_RES_11BITS = 0x5F,
DS18B20_RES_12BITS = 0x7F,
} DS18B20_RES_E_TYPE;
typedef struct
{
u8 temp_lsb;
u8 temp_msb;
u8 th_reg_ub1;
u8 tl_reg_ub2;
u8 config_reg;
u8 reserved;
u8 user_byte3;
u8 user_byte4;
u8 crc;
} DS18B20_SCRATCHPAD_S_TYPE;
typedef struct
{
u8 negative_flag;
float temperature;
DS18B20_SCRATCHPAD_S_TYPE scratchpad;
} DS18B20_STATUE_S_TYPE;
void DS18B20_Init(void);
void DS18B20_GetTemperature(void);
void DS18B20_Test(void);
#endif
#include "ds18b20.h"
#include "lcd.h"
#define DS18B20_SET_BUS GPIO_SetBits(DS18B20_GPIO, DS18B20_PIN_TX)
#define DS18B20_RESET_BUS GPIO_ResetBits(DS18B20_GPIO, DS18B20_PIN_TX)
#define DS18B20_READ_BUS GPIO_ReadInputDataBit(DS18B20_GPIO, DS18B20_PIN_RX)
DS18B20_STATUE_S_TYPE ds18b20 = { 0 };
2.2 时序
2.2.1 复位信号
void DS18B20_ResetSig(void)
{
DS18B20_RESET_BUS;
delay_ms(1);
DS18B20_SET_BUS;
}
2.2.2 存在信号
u8 DS18B20_PresenceSig(void)
{
u16 i = 0;
u16 time = 0;
// u16 delay = 0;
delay_us(10); // 调试后,按实际延时等待时间
for (i = 0; i < 480; i++)
{
if (!DS18B20_READ_BUS)
{
time++;
// if (!delay) { delay = i; } // delay 变量用于确定等待时间
}
delay_us(1);
}
if ((time >= 30) && (time <= 240)) { return TRUE; } // 调试后,按实际确定 "存在信号" 持续时间
return FALSE;
}
2.2.3 写位
void DS18B20_WriteBit(u8 bit)
{
delay_us(2); // 读/写 时序的间隔
DS18B20_RESET_BUS; // 下降沿, 开始 "写" 时序
if (bit) // 写 "1"
{
delay_us(5); // 15 us 内拉高总线
DS18B20_SET_BUS; // 写 "1", 释放总线
delay_us(60); // 时序至少持续 60 us
}
else // 写 "0"
{
delay_us(60); // 时序至少持续 60 us
DS18B20_SET_BUS; // 释放总线
}
}
2.2.4 读位
u8 DS18B20_ReadBit(void)
{
u8 bit = 0;
delay_us(2); // 读/写 时序的间隔
DS18B20_RESET_BUS; // 下降沿, 开始 "读" 时序
delay_us(2); // 至少保持 1 us
DS18B20_SET_BUS; // 释放总线
delay_us(15); // 从下降沿开始, 至少保持 15 us
bit = DS18B20_READ_BUS; // 读取总线
delay_us(50); // 时序至少持续 60 us
return bit;
}
3 代码汇总
3.1 ds18b20.c
#include "ds18b20.h"
#define DS18B20_SET_BUS GPIO_SetBits(DS18B20_GPIO, DS18B20_PIN_TX)
#define DS18B20_RESET_BUS GPIO_ResetBits(DS18B20_GPIO, DS18B20_PIN_TX)
#define DS18B20_READ_BUS GPIO_ReadInputDataBit(DS18B20_GPIO, DS18B20_PIN_RX)
DS18B20_STATUE_S_TYPE ds18b20 = { 0 };
/**************************************************
* Desc: DS18B20 复位信号
**************************************************/
static void DS18B20_ResetSig(void)
{
DS18B20_RESET_BUS;
delay_ms(1);
DS18B20_SET_BUS;
}
/**************************************************
* Desc: DS18B20 存在信号
* Return: TRUE: 检测到 DS18B20, FALSE: 未检测到
**************************************************/
static u8 DS18B20_PresenceSig(void)
{
u16 i = 0;
u16 time = 0;
// u16 delay = 0;
delay_us(10); // 调试后,按实际延时等待时间
for (i = 0; i < 480; i++)
{
if (!DS18B20_READ_BUS)
{
time++;
// if (!delay) { delay = i; } // delay 变量用于确定等待时间
}
delay_us(1);
}
if ((time >= 30) && (time <= 240)) { return TRUE; } // 调试后,按实际确定 "存在信号" 持续时间
return FALSE;
}
/**************************************************
* Desc: DS18B20 写位
* Para: In: bit: 位值, 0 或 1
**************************************************/
static void DS18B20_WriteBit(u8 bit)
{
delay_us(2); // 读/写 时序的间隔
DS18B20_RESET_BUS; // 下降沿, 开始 "写" 时序
if (bit) // 写 "1"
{
delay_us(5); // 15 us 内拉高总线
DS18B20_SET_BUS; // 写 "1", 释放总线
delay_us(60); // 时序至少持续 60 us
}
else // 写 "0"
{
delay_us(60); // 时序至少持续 60 us
DS18B20_SET_BUS; // 释放总线
}
}
/**************************************************
* Desc: DS18B20 读位
* Return: bit: 读到的位值, 0 或 1
**************************************************/
static u8 DS18B20_ReadBit(void)
{
u8 bit = 0;
delay_us(2); // 读/写 时序的间隔
DS18B20_RESET_BUS; // 下降沿, 开始 "读" 时序
delay_us(2); // 至少保持 1 us
DS18B20_SET_BUS; // 释放总线
delay_us(15); // 从下降沿开始, 至少保持 15 us
bit = DS18B20_READ_BUS; // 读取总线
delay_us(50); // 时序至少持续 60 us
return bit;
}
/**************************************************
* Desc: DS18B20 写字节
* Para: In: data: 要写入的数据
**************************************************/
void DS18B20_WriteByte(u8 data)
{
u8 i = 0;
for (i = 0; i < 8; i++)
{
DS18B20_WriteBit(data & 0x01);
data >>= 1;
}
}
/**************************************************
* Desc: DS18B20 读字节
* Return: data: 读到的数据
**************************************************/
u8 DS18B20_ReadByte(void)
{
u8 i = 0;
u8 data = 0;
for (i = 0; i < 8; i++)
{
data = data | (DS18B20_ReadBit() << 7);
data = (i == 7) ? data : (data >> 1);
}
return data;
}
/**************************************************
* Desc: DS18B20 开始一次温度转换
**************************************************/
static void DS18B20_StartConversion(void)
{
DS18B20_ResetSig();
if (!DS18B20_PresenceSig()) { return; }
DS18B20_WriteByte(0xCC); // 跳过 ROM
DS18B20_WriteByte(0x44); // 开始一次温度转换
while (!DS18B20_ReadBit()); // 等待转换完成
}
/**************************************************
* Desc: DS18B20 写入 SCRATCHPAD
* Other: 写入顺序: TH reg, TL reg, config reg,
* config reg, user byte3, user byte4
* 如果未使用报警功能, 则TH reg, TL reg
* 当做用户字节使用
**************************************************/
static void DS18B20_WriteScratchPad(void)
{
u8 i = 0;
u8 *addr;
u8 max_write_byte = 5; // SCRATCHPAD 最大 5 字节可写入
/* 要写入的数据 */
ds18b20.scratchpad.th_reg_ub1 = 0x50; // 温度报警上限: 0x50(+80°C)
ds18b20.scratchpad.tl_reg_ub2 = 0x8A; // 温度报警下限: 0x8A(-10°C)
ds18b20.scratchpad.config_reg = DS18B20_RES_12BITS; // 温度转换分辨率: 12 bits
ds18b20.scratchpad.user_byte3 = 0x00; // 用户字节, 按需使用
ds18b20.scratchpad.user_byte4 = 0x00;
DS18B20_ResetSig();
if (!DS18B20_PresenceSig()) { return; }
DS18B20_WriteByte(0xCC); // 跳过 ROM
DS18B20_WriteByte(0x4E); // 写 SCRATCHPAD
/* 由于 config reg 和 user byte 3 之间存在 reserved, 因此循环数必须 + 1 */
for (i = 0; i < (max_write_byte + 1); i++)
{
if (i == 3) { continue; } // 跳过 reserved 字节, 该字节不可被写入
addr = &ds18b20.scratchpad.th_reg_ub1 + i;
DS18B20_WriteByte(*addr);
}
}
/**************************************************
* Desc: DS18B20 读取 SCRATCHPAD
**************************************************/
static void DS18B20_ReadScratchPad(void)
{
u8 i = 0;
u8 *addr;
u8 max_read_byte = 9; // SCRATCHPAD 最大 9 字节可读取
DS18B20_ResetSig();
if (!DS18B20_PresenceSig()) { return; }
DS18B20_WriteByte(0xCC); // 跳过 ROM
DS18B20_WriteByte(0xBE); // 读 SCRATCHPAD
for (i = 0; i < max_read_byte; i++)
{
addr = &ds18b20.scratchpad.temp_lsb + i;
*addr = DS18B20_ReadByte();
}
}
/**************************************************
* Desc: DS18B20 将数据从 SCRATCHPAD 复制到 EEPROM
* Other: 功能为断电保存, 下次上电时自动从 EPPROM 读取配置
**************************************************/
static void DS18B20_CopyScratchPad(void)
{
DS18B20_ResetSig();
if (!DS18B20_PresenceSig()) { return; }
DS18B20_WriteByte(0xCC); // 跳过 ROM
DS18B20_WriteByte(0x48); // 开始复制 SCRATCHPAD
while (!DS18B20_ReadBit()); // 等待复制完成
}
/**************************************************
* Desc: DS18B20 将数据从 EEPROM 复制到 SCRATCHPAD
**************************************************/
static void DS18B20_RecallE2(void)
{
DS18B20_ResetSig();
if (!DS18B20_PresenceSig()) { return; }
DS18B20_WriteByte(0xCC); // 跳过 ROM
DS18B20_WriteByte(0xB8); // 开始复制 SCRATCHPAD
while (!DS18B20_ReadBit()); // 等待复制完成
}
/**************************************************
* Desc: DS18B20 初始化
**************************************************/
void DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN_TX;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DS18B20_PIN_RX;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DS18B20_GPIO, &GPIO_InitStructure);
GPIO_SetBits(DS18B20_GPIO, DS18B20_PIN_TX);
}
/**************************************************
* Desc: DS18B20 获取一次温度
**************************************************/
void DS18B20_GetTemperature(void)
{
u8 shift_size = 0;
u8 neg_flag = 0;
u8 temp_msb = 0, temp_lsb = 0;
u16 temp_reg = 0;
float temperature = 0.0;
DS18B20_StartConversion();
DS18B20_ReadScratchPad();
temp_msb = ds18b20.scratchpad.temp_msb;
temp_lsb = ds18b20.scratchpad.temp_lsb;
if (temp_msb > 7) // 零下温度, 取反后 + 1
{
temp_msb = ~temp_msb;
temp_lsb = ~temp_lsb;
temp_reg = ((temp_msb << 8) | temp_lsb) + 1;
temp_reg &= 0x07FF;
neg_flag = 1;
}
else
{
temp_reg = (temp_msb << 8) | temp_lsb;
temp_reg &= 0x7FF;
}
shift_size = (ds18b20.scratchpad.config_reg >> 5); // 计算分辨率对应运算值
temperature = (float)temp_reg / (2 << shift_size);
ds18b20.negative_flag = neg_flag;
ds18b20.temperature = temperature;
}
/**************************************************
* Desc: DS18B20 测试
**************************************************/
void DS18B20_Test(void)
{
DS18B20_GetTemperature();
delay_ms(1000);
}
3.2 ds18b20.h
#ifndef __DS18B20_H
#define __DS18B20_H
#include "stm32f10x.h"
#include "delay.h"
#define DS18B20_GPIO GPIOB
#define DS18B20_PIN_TX GPIO_Pin_10
#define DS18B20_PIN_RX GPIO_Pin_11
#define FALSE 0
#define TRUE 1
typedef enum
{
DS18B20_RES_9BITS = 0x1F, // 温度转换分辨率 9 bits
DS18B20_RES_10BITS = 0x3F,
DS18B20_RES_11BITS = 0x5F,
DS18B20_RES_12BITS = 0x7F,
} DS18B20_RES_E_TYPE;
typedef struct
{
u8 temp_lsb;
u8 temp_msb;
u8 th_reg_ub1;
u8 tl_reg_ub2;
u8 config_reg;
u8 reserved;
u8 user_byte3;
u8 user_byte4;
u8 crc;
} DS18B20_SCRATCHPAD_S_TYPE;
typedef struct
{
u8 negative_flag;
float temperature;
DS18B20_SCRATCHPAD_S_TYPE scratchpad;
} DS18B20_STATUE_S_TYPE;
void DS18B20_Init(void);
void DS18B20_GetTemperature(void);
void DS18B20_Test(void);
#endif
3.3 main.c
#include "ds18b20.h"
#include "stm32f10x.h"
int main(void)
{
SystemInit();
delay_init();
DS18B20_Init();
while (1)
{
DS18B20_Test();
}
}
4 备注
240506:内容大概会不断完善
240507:存在信号时间调试方法
1. 在 if ((time >= 30) && (time <= 240)) { return TRUE; } 这一行加入断点
2. 调试->运行,到该断点时,在Call Stack 里查看变量值
3. 29行 delay_us() 小于 变量值
4. 40行 time最小值 计算方式按照图中方法,小于该值即可
5. 调试一次即可(大概)