传感器 · 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 读取。

原理资料参考原装 DS18B20 官方数据手册,但本产品使用国产兼容 DS18B20,不把原厂参数当成本模块的实测承诺。

Two Forms

本项目有两种使用方式

两种产品形态共用同一份固件,换产品形态不用重新烧录。

M2002 DS18B20 小长板实物图
方案一:M2002 小长板
H34 防水探头 + M1701 转接板实物图
方案二:H34 防水探头 + M1701 转接板

方案一: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 / M1701STM32F103C8T6OLED
电源VCC3.3VVCC / VDD
地GNDGNDGND
数据DATA(DQ)PA0—
时钟—PB8SCL / SCK
数据—PB9SDA

烧录:ST-Link → SWD 四线(SWDIO=PA13 / SWCLK=PA14);串口:PA9(TX) / PA10(RX) → CP2102(115200,可选)。

上拉电阻(重点,以实物确认为准)。DS18B20 的 DATA(DQ) 是开漏单总线,必须上拉到 3.3V,否则读不到设备。
产品上拉情况实测做法
M2002 小板板载一颗 10kΩ(是否 DATA→VCC 上拉待确认)先直接用板载 10kΩ 实测
H34 + M1701H34 探头无电阻;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 / 串口

关键处说明

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 变了才重画。

浮点 printf 的坑。本工程用 %.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

下载

PlatformIO 工程 ZIP 含 platformio.ini、src/main.cpp、README.md。已去掉本机构建路径,解压后 VS Code 打开即可 Build。
下载 ZIP
代码已编译通过,尚未烧录实机测试。接线以本页表格为准,M2002 板载上拉是否为 DATA→VCC、M1701 转接板线序仍待实测确认,详见下方「常见问题」。

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 线序待确认
SKUM2002(小长板)/ H34(防水探头)+ M1701(转接板)

本页所有「资料参考」内容均由我们重新整理表述,未直接复制原文。本页尚未实机验证,OLED/串口内容是代码逻辑推导,不是实拍截图,待烧录实测后会替换成真实结果。