Embedded HandbookПриложения
Все разделы › XI. Датчики: подключение, схемы, драйверы
STM32F103, STM32F407 · уровень средний · 10 мин чтения
Открыть в интерактивном справочнике

96. Температура: NTC-термистор и DS18B20

Температуру чаще всего меряют одним из двух способов: дешёвым термистором через АЦП или цифровым датчиком DS18B20 на одном проводе. Первый — аналоговый: точность зависит от делителя, АЦП и того, как код переведён в градусы. Второй сам отдаёт число, но приносит протокол со своими ловушками. Раздел разбирает оба на F103 и F407 с одной целью — получить градусы целыми числами, без плавающей точки.

Шина 1-Wire и её CRC описаны в разделе 83.2, АЦП — в разделе 14, подключение датчиков вообще — в разделе 95.

Как это называется в документации

Оригинал Перевод
thermistor, NTC (negative temperature coefficient) терморезистор с отрицательным температурным коэффициентом
B value, beta коэффициент B термистора
Steinhart–Hart equation уравнение Стейнхарта — Харта
self-heating самонагрев током измерения
ratiometric measurement логометрическое измерение
scratchpad оперативная память датчика, 9 байт
conversion time (tCONV) время измерения
parasite power паразитное питание от линии данных
strong pull-up сильная подтяжка: линию держит ключ
ROM code, Skip ROM 64-битный адрес датчика; обращение без адреса

96.1 Два способа

NTC-термистор DS18B20
выход сопротивление → напряжение делителя число по шине 1-Wire
нужно от STM32 вход АЦП один вывод и UART или таймер
шаг зависит от температуры: 0,022 °C на код при 25 °C 0,0625 °C при 12 битах
время измерения микросекунды до 750 мс при 12 битах
диапазон задаёт таблица: здесь −40…+125 °C −55…+125 °C
несколько датчиков по входу АЦП на каждый много на одном проводе
слабое место перевод кода в градусы и самонагрев протокол и +85 °C до первого измерения

96.2 NTC: делитель и формула

Термистор в делителе: постоянный резистор к питанию, термистор к земле, конденсатор у входа АЦП

Сопротивление NTC-термистора падает с ростом температуры:

R(T) = R25 · e^(B·(1/T − 1/T25)),

где T — в кельвинах, R25 — сопротивление при 25 °C (здесь 10 кОм), B — коэффициент из документации (здесь 3950). В делителе с резистором 10 кОм к питанию и термистором к земле код АЦП равен 4095 · R / (R + 10 кОм).

Два решения в этой схеме не случайны:

Конденсатор 100 нФ у входа АЦП держит напряжение, пока конденсатор выборки забирает заряд: для АЦП делитель — источник с сопротивлением 5 кОм (два по 10 кОм параллельно при 25 °C), и без конденсатора ему пришлось бы давать большое время выборки (раздел 14.1).


96.3 Код → градусы целыми числами

Логарифм на F103 без блока плавающей точки обходится дорого, а точности от него больше, чем даёт сам термистор. Проще — таблица кодов через 5 °C и линейная интерполяция между точками:

static const uint16_t ntc_code[NTC_N] = {
    3996, 3955, 3900, 3830, 3740, 3629, 3495, 3337, 3156, 2955, 2738, 2510,
    2278, 2048, 1825, 1614, 1419, 1241, 1081,  940,  815,  707,  613,  532,
     462,  401,  350,  305,  267,  234,  206,  181,  160,  142,
};

int16_t ntc_dc(uint16_t code)
{
    if (code > ntc_code[0] || code < ntc_code[NTC_N - 1])
        return INT16_MIN;               /* обрыв, замыкание или вне −40…+125 °C */
    unsigned i = 0;
    while (code < ntc_code[i + 1])
        i++;
    int32_t span = ntc_code[i] - ntc_code[i + 1];
    int32_t part = ntc_code[i] - code;
    return (int16_t)((NTC_T0 + NTC_STEP * (int32_t)i) * 10 + (NTC_STEP * 10 * part + span / 2) / span);
}

Результат — в десятых долях градуса. Программа на компьютере прошла все коды от 142 до 3996 и сравнила таблицу с точной формулой: наибольшая ошибка — 0,203 °C, около −32 °C, где кривая гнётся сильнее всего. Код за краями таблицы — это не холод и не жара, а обрыв или замыкание: функция возвращает INT16_MIN, и показывать такое как температуру нельзя.

Сколько градусов приходится на один код, зависит от температуры:

Температура Кодов на градус Градусов на код
−20 °C 19,98 0,050
25 °C 45,49 0,022
100 °C 7,08 0,141

Шаг АЦП — ещё не точность: сам термистор с допуском сопротивления и коэффициента B ошибается сильнее, а формула с одним B — приближение. Если нужна точность выше, берут таблицу производителя вместо формулы или уравнение Стейнхарта — Харта с тремя коэффициентами; функция перевода при этом не меняется — меняется только таблица.

Самонагрев. Через делитель при 25 °C течёт 165 мкА, и термистор рассеивает 0,272 мВт — греется сам. На сколько — делят мощность на постоянную рассеяния из документации на термистор: при 1–2 мВт/°C это 0,14–0,27 °C. Если измерять раз в секунду, делитель лучше питать от вывода порта и включать только на время измерения. Но после включения конденсатор 100 нФ у входа заряжается через делитель: при 25 °C постоянная времени 5 кОм × 100 нФ = 0,5 мс, и чтобы недобор до кода 2048 стал меньше одного кода, нужно ln 2048 ≈ 7,6 постоянной — 3,8 мс. При −40 °C сопротивление источника почти 10 кОм, и ждать надо 8,1 мс. Измерили сразу — код занижен. Кроме того, вывод под током 165 мкА отдаёт чуть меньше питания — до 8 мВ, если судить по гарантии «0,4 В при 8 мА»: это 5 кодов в середине шкалы, десятая доля градуса.


96.4 DS18B20: как спросить температуру

DS18B20 на одном проводе: подтяжка 4,7 кОм и вывод UART в полудуплексе

Датчик сам измеряет и хранит результат в своей памяти из 9 байт: температура (два байта), пороги TH и TL, байт конфигурации, служебные байты и CRC. Измерение идёт в два захода — «начни» и, когда закончено, «отдай память»:

int ds_start(void)
{
    if (!ow_reset())
        return 0;                       /* на шине никого */
    ow_write(DS_SKIP_ROM);
    ow_write(DS_CONVERT);
    return 1;
}

int ds_read(int32_t *mdeg, unsigned bits)
{
    uint8_t pad[9];
    if (!ow_reset())
        return 0;
    ow_write(DS_SKIP_ROM);
    ow_write(DS_READ_PAD);
    for (unsigned i = 0; i < 9; i++)
        pad[i] = ow_read();
    if (crc8_1wire(pad, 9) != 0)
        return 0;                       /* помеха на проводе */
    uint16_t raw = (uint16_t)(pad[0] | (pad[1] << 8));
    if (raw == DS_POWER_ON)
        return 0;                       /* измерения не было: сбой питания или прочли раньше срока */
    *mdeg = ds_mdeg(raw, bits);
    return 1;
}

ow_reset, ow_write и ow_read — функции шины из раздела 83.2 (там 1-Wire сделан на UART в полудуплексе, байт на бит), crc8_1wire — оттуда же. DS_SKIP_ROM обращается к датчику, не называя адреса, — так можно, только когда он на линии один. На компьютере драйвер работал с моделью датчика: ds_start отправляет 0xCC и 0x44, ds_read — 0xCC и 0xBE, читает девять байт и отдаёт 25,063 °C.

Время измерения зависит от разрешения, а разрешение задают два бита байта конфигурации:

Разрешение Шаг Байт конфигурации Время измерения, не больше
9 бит 0,5 °C 0x1F 93,75 мс
10 бит 0,25 °C 0x3F 187,5 мс
11 бит 0,125 °C 0x5F 375 мс
12 бит 0,0625 °C 0x7F 750 мс
#define DS_CONFIG(bits)   ((uint8_t)((((bits) - 9u) << 5) | 0x1Fu))
#define DS_TCONV_US(bits) (750000u >> (12u - (bits)))

96.5 Формат: шестнадцатые доли градуса

Температура — 16-битное число со знаком в дополнительном коде, единица — 1/16 °C:

Температура Код
+125 °C 0x07D0
+85 °C 0x0550
+25,0625 °C 0x0191
+10,125 °C 0x00A2
+0,5 °C 0x0008
0 °C 0x0000
−0,5 °C 0xFFF8
−10,125 °C 0xFF5E
−25,0625 °C 0xFE6F
−55 °C 0xFC90

В тысячные доли градуса — умножить на 625 и разделить на 10; при меньших разрешениях младшие биты не определены, и их сбрасывают:

int32_t ds_mdeg(uint16_t raw, unsigned bits)
{
    int32_t v = (int16_t)(raw & (uint16_t)~((1u << (12u - bits)) - 1u));
    return (v * 625 + (v < 0 ? -5 : 5)) / 10;
}

Приведение к int16_t обязательно: без него 0xFF5E стало бы +4085,875 °C, а не −10,125. Код 0x0191 при 9 битах даёт 25,000 °C — три младших бита отброшены.


96.6 +85 °C — не температура

Сразу после включения память датчика выглядит так: 50 05 4B 46 7F FF 0C 10 1C.

Температура в ней — 0x0550, ровно +85 °C, и CRC у этих девяти байт верная: помехи нет, датчик честно отдаёт то, что лежит в памяти. Такое читают, если прочли раньше, чем закончилось измерение, или если питание датчика просело и он перезапустился. Проверка CRC эту ошибку не ловит, поэтому драйвер отдельно отвергает ровно +85,000 °C.

Если прибор и правда работает около +85 °C, такое правило не годится: там читают строго после полного времени измерения, а ровно +85,000 перемеряют.


96.7 Несколько датчиков и паразитное питание

Паразитное питание DS18B20: VDD на землю, а линию на время измерения держит ключ — сильная подтяжка


96.8 Грабли

Термистор показывает −40 или +125 и не меняется. Код ушёл за край таблицы: обрыв (код у 4095) или замыкание (у нуля). Проверять выход за таблицу и не выдавать его за температуру.

Термистор «теплее» комнаты. Самонагрев током делителя. Питать делитель только на время измерения.

Показания термистора плавают вместе с питанием. Делитель запитан не от опоры АЦП. Питать от того же напряжения (раздел 81.2).

DS18B20 отдаёт +85 °C. Прочли раньше конца измерения, или датчик перезапустился по питанию. Ждать полное время измерения; 85,000 после сброса — не температура.

Мороз превращается в тысячи градусов. Код прочитан как беззнаковый. Приводить к int16_t.

DS18B20 не отвечает. Нет подтяжки 4,7 кОм, или датчик на паразитном питании без сильной подтяжки на время измерения.

Два датчика на линии дают мусор. К ним обратились без адреса, и ответили оба сразу. Читать по адресу.


96.9 Итоги

Дальше: раздел 97 «Влажность: DHT22, SHT31 и AHT20» — однопроводный протокол с длительностью импульсов и два датчика на I²C с проверкой CRC.

Открыть в интерактивном справочнике