传感器 · DS18B20 · 1-Wire 温度
DS18B20 温度传感器 STM32F103 接线与代码
DS18B20 就是给单片机装一支「温度计」——它把温度直接变成一个数字,STM32 一根 DATA 线读回来就能显示到 OLED。本页是接线与代码资料:代码已完成、编译通过,实机烧录测试还没做,页面会诚实标出哪些是代码逻辑推导、哪些是真机验证过的。
Quick Facts
- 接口:1-Wire 单总线,一根 DATA 线通信
- 接线:DATA→PA0,与 OLED/串口/ST-Link 无冲突
- 产品形态:M2002 小长板 / H34 探头+M1701 转接板,共用一份固件
- 上拉:DATA 必须上拉到 3.3V(开漏总线)
- 代码:v1.0,
pio run编译通过 - 实机测试:待做
Status
当前状态
仅资料整理,未实物验证 OLED / 串口显示内容来自代码逻辑,不是实拍截图,待实测后替换成实机照片。
| 步骤 | 状态 |
|---|---|
| 代码 | ✅ 完成(v1.0) |
| 编译 | ✅ pio run 通过 |
| 烧录 | ⏸️ 待做 |
| 实机测试 | ⏸️ 待做 |
| 网页资料 | ✅ 本页 |
Preview
结果先看(预期,待实测)
代码推导 下面内容是根据代码逻辑推出来的预期表现,不是实拍截图。
DS18B20 → STM32 → OLED 实时温度。正常时 OLED 显示:
DS18B20
──────────
TEMP
25.6 C <- 大字号
串口输出(115200,约 750ms 一行):
DS18B20 demo v1.0 starting...
OLED 0x3C: found
DS18B20: 1 device(s) found
DS18B20 Temperature: 25.6 C
异常情况(没接 / 上拉没接好):
OLED:DS18B20 / NOT FOUND
串口:DS18B20 Temperature: NOT FOUND (no device on 1-Wire bus)
What it does
这是什么(大白话)
DS18B20 是数字温度传感器——测温、数字输出(不用 ADC 采样查表)、走 1-Wire 单总线,一根 DATA 数据线完成通信,可以直接由 STM32 读取。
Two Forms
本项目有两种使用方式
两种产品形态共用同一份固件,换产品形态不用重新烧录。
方案一:SKU M2002 小长板
- DS18B20 小长板模块
- 板载 10kΩ 电阻(R1=103,是否为 DATA→VCC 上拉待万用表确认)
- 三针接口:GND / DQ / VCC,直接接 STM32 使用
M2002 VCC -> 3.3V
M2002 DATA -> PA0
M2002 GND -> GND
方案二:SKU H34 防水探头 + M1701 转接板
- H34:不锈钢防水探头(DS18B20 封装在探头里,带三芯长线)
- M1701:转接小板(黑色 PCB + 绿色螺丝端子 + 排针)
- H34 插到 M1701,再从 M1701 引出三线接 STM32
M1701 VCC -> 3.3V
M1701 DATA -> PA0
M1701 GND -> GND
Wiring
接线
DS18B20 DATA 固定使用 PA0(与 OLED / 串口 / ST-Link 无冲突)。
| 信号 | M2002 / M1701 | STM32F103C8T6 | OLED |
|---|---|---|---|
| 电源 | VCC | 3.3V | VCC / VDD |
| 地 | GND | GND | GND |
| 数据 | DATA(DQ) | PA0 | — |
| 时钟 | — | PB8 | SCL / SCK |
| 数据 | — | PB9 | SDA |
烧录:ST-Link → SWD 四线(SWDIO=PA13 / SWCLK=PA14);串口:PA9(TX) / PA10(RX) → CP2102(115200,可选)。
| 产品 | 上拉情况 | 实测做法 |
|---|---|---|
| M2002 小板 | 板载一颗 10kΩ(是否 DATA→VCC 上拉待确认) | 先直接用板载 10kΩ 实测 |
| H34 + M1701 | H34 探头无电阻;M1701 上拉以实际电路为准 | 无上拉则 DATA→3.3V 外接 4.7kΩ |
How it works
工作原理
资料参考 来自公开数据手册,不是本项目实测结论。
- 1-Wire 单总线:只用一根 DATA 线双向通信,靠拉低电平时间长短传 0/1。
- 开漏上拉:DQ 只能拉低不能拉高,必须外接上拉电阻保持空闲高电平。
- 测温流程:Reset → Presence → Skip ROM → Convert T(0x44) → 等待转换(12bit≈750ms)→ Read Scratchpad。
- 64-bit ROM:每个芯片唯一 ID,一根总线可挂多个,各读各的。
- 分辨率:9~12bit 可调,12bit 对应 0.0625℃。
原厂参考规格(ADI/Maxim DS18B20):-55~+125℃、±0.5℃@-10~+85℃、3.0~5.5V。本模块为国产兼容,实测指标以实际测试为准。
Code
完整代码
编译通过 · 未实机验证 下面是当前 src/main.cpp 的完整内容(v1.0),一字未改。环境:VS Code + PlatformIO + Arduino(STM32duino)。
程序逻辑
DS18B20 → 1-Wire(DATA=PA0) → DallasTemperature 库 → STM32 → OLED / 串口
requestTemperatures()阻塞约 750ms(12bit 转换),整机读取周期 ≈ 750ms(约 1.3Hz)getTempCByIndex(0)读第 0 个设备温度- 库在「未接/通信失败」时返回
DEVICE_DISCONNECTED_C(-127.0) - 量程过滤 -55~+125℃,挡掉异常值,不显示假温度
- OLED 只在「显示内容变了」才重画,避免无谓刷新/闪烁
- 状态:0=正常温度 / 1=NOT FOUND(没检测到设备)/ 2=READ ERROR(读到异常值)
关键处说明
1. 1-Wire 引脚。constexpr uint8_t ONE_WIRE_PIN = PA0;,DATA 固定接 PA0。
2. 初始化。sensors.begin() 后 getDeviceCount() 统计设备数,0 = 没接。
3. 读温度。requestTemperatures() 阻塞约 750ms,然后 getTempCByIndex(0)。
4. 过滤异常。DEVICE_DISCONNECTED_C(-127) 和量程 -55~+125℃ 双重过滤。
5. OLED 重映射。Wire.setSDA(PB9) / Wire.setSCL(PB8) 先设再 begin()。
6. 只画变化的帧。g_lastDrawnTemp10 / g_lastDrawnState 变了才重画。
%.1f 格式化温度,STM32 用的 newlib-nano 默认不链接浮点版 printf/scanf,不处理会导致 %f 输出空白(只剩单位文字,数字消失)。已在 platformio.ini 里加 build_flags = -Wl,-u,_printf_float 强制链接浮点 printf 实现来解决,代码本身不用改写成 dtostrf 之类的写法。
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <math.h>
#define FIRMWARE_VERSION "v1.0"
// =========================
// GPIO 配置
// =========================
// DS18B20(1-Wire 单总线,DATA 为双向数据线,需上拉到 3.3V)
// DATA -> PA0(本工程专用,与 OLED / 串口 / ST-LINK 均无冲突)
// VCC -> 3.3V(DS18B20 供电范围 3.0~5.5V,3.3V 供电即可)
// GND -> GND
// 上拉电阻:见 README「上拉电阻」一节。
// M2002 小板:板载 10kΩ 上拉,直接使用。
// H34 防水探头:本项目配合 M1701 转接小板使用。
// M1701 的上拉配置以实际板卡电路确认结果为准。
constexpr uint8_t ONE_WIRE_PIN = PA0;
// OLED 使用 I2C1 重映射引脚:SCL=PB8 / SDA=PB9(与 01~09 系列公共标准一致)
constexpr uint8_t I2C_SDA_PIN = PB9;
constexpr uint8_t I2C_SCL_PIN = PB8;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr int OLED_RESET = -1;
// =========================
// 读取周期
// =========================
// requestTemperatures() 为阻塞式:12bit 分辨率一次转换约 750ms,
// 所以整机读取周期 ≈ 750ms(约 1.3Hz)。对温度显示足够,且不会闪烁。
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// 状态
bool g_hasOled = false; // OLED 是否初始化成功
uint8_t g_deviceCount = 0; // 1-Wire 总线上找到的 DS18B20 数量(0 = 没接)
float g_tempC = 0.0f; // 最近一次有效温度
bool g_tempValid = false; // 当前温度是否有效(无效时不显示)
// 上次已画到屏幕上的内容(显示出来的 0.1°C 或状态没变就不重画,避免无谓刷新/闪烁)
static int16_t g_lastDrawnTemp10 = INT16_MIN; // 上次显示的 0.1°C 整数(INT16_MIN = 未画过)
static uint8_t g_lastDrawnState = 0xFF; // 0=正常 1=NOT FOUND 2=READ ERROR
// =========================
// 温度有效性判断
// =========================
// DallasTemperature 在「未接 / 通信失败」时返回 DEVICE_DISCONNECTED_C(-127.0)。
// DS18B20 实际量程 -55~+125°C,据此过滤异常值,避免把错误值当正常温度显示。
static bool isValidTemperature(float t) {
if (t == DEVICE_DISCONNECTED_C) {
return false; // 通信失败/未接,库返回 -127.0
}
return (t >= -55.0f) && (t <= 125.0f); // 正常量程 -55~+125°C
}
// 当前屏幕状态:0=正常温度 1=没检测到设备 2=读到异常值
static uint8_t currentState() {
if (g_tempValid) return 0;
if (g_deviceCount == 0) return 1;
return 2;
}
// 屏幕只显示 1 位小数,转成 0.1°C 整数,方便「显示内容变了才重画」比较
static int16_t tempToDisplay10(float t) {
return (int16_t)lroundf(t * 10.0f);
}
// =========================
// 读取温度(阻塞式)
// =========================
// requestTemperatures() 发送转换命令并等待完成(12bit ≈ 750ms)。
// getDeviceCount() 在 begin() 时统计总线设备数,0 表示没接。
static void readTemperature() {
sensors.requestTemperatures(); // 阻塞约 750ms
float t = sensors.getTempCByIndex(0);
if (t == DEVICE_DISCONNECTED_C) {
// 通信失败:可能没接/被拔掉,重新扫描总线刷新设备数(支持热插拔)
g_deviceCount = sensors.getDeviceCount();
g_tempValid = false;
return;
}
if (!isValidTemperature(t)) {
g_tempValid = false; // 读到其它异常值,不当正常温度
return;
}
g_deviceCount = 1; // 只支持 1 个 DS18B20,读到有效值即在线
g_tempC = t;
g_tempValid = true;
}
// =========================
// OLED UI 绘制
// =========================
static void drawOledUi() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextWrap(false);
// 顶部标题 + 分隔线
display.setTextSize(1);
display.setCursor(0, 0);
display.print(F("DS18B20"));
display.drawFastHLine(0, 9, 128, WHITE);
if (!g_tempValid) {
// 异常状态:不显示假温度
display.setTextSize(2);
display.setCursor(0, 28);
if (g_deviceCount == 0) {
display.print(F("NOT FOUND"));
} else {
display.print(F("READ ERROR"));
}
} else {
// 正常:TEMP 标签 + 大字号温度
display.setTextSize(1);
display.setCursor(0, 16);
display.print(F("TEMP"));
char buf[10];
snprintf(buf, sizeof(buf), "%.1f C", g_tempC);
display.setTextSize(3);
display.setCursor(0, 28);
display.print(buf);
}
display.display();
}
// =========================
// 串口输出
// =========================
static void printSerialLine() {
if (!g_tempValid) {
if (g_deviceCount == 0) {
Serial.println(F("DS18B20 Temperature: NOT FOUND (no device on 1-Wire bus)"));
} else {
Serial.println(F("DS18B20 Temperature: READ ERROR (invalid value)"));
}
return;
}
Serial.printf("DS18B20 Temperature: %.1f C\n", g_tempC);
}
void setup() {
Serial.begin(115200);
delay(100);
Serial.printf("DS18B20 demo %s starting...\n", FIRMWARE_VERSION);
// OLED:I2C1 重映射 PB8=SCL / PB9=SDA,必须先 setSDA/setSCL 再 begin()
Wire.setSDA(I2C_SDA_PIN);
Wire.setSCL(I2C_SCL_PIN);
Wire.begin();
Wire.setClock(100000); // I2C 100kHz,与 01~09 系列公共标准一致
if (display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
g_hasOled = true;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.display();
Serial.println(F("OLED 0x3C: found"));
} else {
Serial.println(F("OLED 0x3C: NOT found (continue with serial only)"));
}
// DS18B20:扫描 1-Wire 总线,统计设备数量
sensors.begin();
g_deviceCount = sensors.getDeviceCount();
if (g_deviceCount == 0) {
Serial.println(F("DS18B20: NOT found (0 device, check wiring + pull-up)"));
} else {
Serial.printf("DS18B20: %u device(s) found\n", g_deviceCount);
}
// 首次读取 + 画首帧
readTemperature();
if (g_hasOled) drawOledUi();
}
void loop() {
readTemperature(); // 阻塞 ~750ms,读一次温度
// 显示出来的 0.1°C 或状态变了才重画,避免无谓刷新/闪烁
uint8_t st = currentState();
int16_t t10 = g_tempValid ? tempToDisplay10(g_tempC) : INT16_MIN;
if (g_hasOled && (st != g_lastDrawnState || t10 != g_lastDrawnTemp10)) {
drawOledUi();
g_lastDrawnState = st;
g_lastDrawnTemp10 = t10;
}
printSerialLine();
}
依赖库:OneWire、DallasTemperature、Adafruit SSD1306、Adafruit GFX Library,由 PlatformIO 按 platformio.ini 自动下载。
Download
下载
Video
视频
芯片 + 产品形态演示动画已完成(154 秒,1920×1080),实机接线 / 代码 / 实测视频待烧录实拍后补充。
Troubleshooting
常见问题
代码推导 下面是根据代码逻辑和硬件原理整理的排查方向,还没有真实故障案例(本项目尚未实机测试)。
读到 NOT FOUND
查 DATA 上拉(M2002 用板载 10kΩ;H34+M1701 无上拉则外接 4.7kΩ)、VCC/GND 接反、共地。
OLED 不亮
查 PB8/PB9、setSDA/setSCL 顺序、地址 0x3C、共地。
数值不更新
requestTemperatures() 阻塞 750ms,等约 1 秒再看。
串口看不到
pio device list 查 COM,本模块不看串口也能用(OLED 直接看温度)。
References
资料来源
本页信息来源声明
| 内容 | 来源 |
|---|---|
| 接线、代码、OLED UI、串口输出 | 本项目代码 v1.0,编译通过,待实机测试 |
| DS18B20 原理、量程、精度、分辨率、64-bit ROM、1-Wire 时序、开漏上拉 | 资料参考 原厂数据手册 |
| OneWire / DallasTemperature 库行为 | 资料参考 开源库文档 |
| M2002 / H34 / M1701 产品外形、上拉电阻 | 资料参考 模块厂家资料,以实物为准 |
| M2002 板载 10kΩ 上拉是否为 DATA→VCC、M1701 线序 | 待确认 |
| SKU | M2002(小长板)/ H34(防水探头)+ M1701(转接板) |
本页所有「资料参考」内容均由我们重新整理表述,未直接复制原文。本页尚未实机验证,OLED/串口内容是代码逻辑推导,不是实拍截图,待烧录实测后会替换成真实结果。