Урок рассказывает об использовании кремниевых датчиков температуры в системе Ардуино. Приводится рабочий проект термометра. Попутно рассматривается вопрос измерения сопротивления в системе Ардуино.
Предыдущий урок Список уроков Следующий урок
- Достоинства и недостатки термодатчиков серии KTY81.
- Кремниевые термодатчики серии KTY81.
- Измерение активного сопротивления в системе Ардуино.
- Проект Ардуино термометра на датчике температуры KTY81/120.
- Вычисление температуры по характеристике датчика , заданной таблицей.
- Работа Ардуино термометра-регистратора в составе с компьютером.
В предыдущем уроке для измерения температуры мы использовали интегральные датчики, у которых напряжение на выходе пропорционально зависит от температуры. Причем эта зависимость линейная с известным масштабным коэффициентом. Для вычисления температуры достаточно измерить напряжение и разделить его на масштабный коэффициент.
Кремниевые термодатчики в зависимости от температуры меняют свое сопротивление. Т.е. это не активные элементы, требующие питания, не интегральные микросхемы, а просто терморезисторы.
Типичными представителями кремниевых датчиков являются термодатчики серии KTY81. Я написал о них большую статью. Там есть параметры, характеристики, схемы включения. Частично я повторю эту информацию.
Достоинства и недостатки термодатчиков серии KTY81.
Датчики такого типа большинство разработчиков просто не замечают. Я, напротив, считаю, что в некоторых приложениях они незаменимы.
На мой взгляд, эти устройства имеют только один существенный недостаток – нелинейная характеристика и невысокая точность измерения. У лучших вариантов датчиков серии KTY81 нелинейность:
± 3,5 °C в диапазоне температуры до + 100 °C;
± 8,5 °C в диапазоне температуры до + 150 °C.
Но, нелинейность характеристики исправляется программой микроконтроллера. В принципе, можно и откалибровать каждый датчик индивидуально.
Но если такая точность измерения приемлема, то по остальным характеристикам и эксплуатационным возможностям KTY81 значительно превосходят интегральные термодатчики. А именно:
- У кремниевых термодатчиков широкий диапазон измеряемых температур -55 … +150 °C. Интегральные устройства, как правило, работают в диапазоне -40 … +125 °C, многие до +100 °C. Часто этих лишних 25-50 °C и не хватает, например, при контроле температуры радиаторов мощных полупроводниковых приборов.
- Для подключения к микроконтроллеру требуется всего два провода, что особенно важно, когда датчики расположены на большом расстоянии от контроллера.
- Обеспечивается высокая помехоустойчивость за счет двух проводной линии связи, симметричной для помех. Для подключения датчика KTY81 на расстоянии десятков и даже сотен метров достаточно простой витой пары.
- Высокое сопротивление KTY81 (1000 Ом и 2000 Ом) позволяет пренебречь активным сопротивлением проводов связи.
- Нет ограничений на работу с емкостной нагрузкой. Это качество позволяет использовать линии связи с большой емкостью и шунтировать термодатчик конденсатором значительной емкости для фильтрации помех. Интегральные термодатчики не работают на емкостную нагрузку более 10 нФ.
- Устройства не имеют полярности. Сколько полупроводниковых приборов было выведено из строя из-за попутки полярности!
- Низкая цена. Это одни из самых дешевых датчиков температуры.
Решайте сами, в каких проектах использовать эти датчики. В моих разработках – мощных станциях катодной защиты на каждом радиаторе установлено по термодатчику KTY81/120.
Кремниевые термодатчики серии KTY81.
Подробно об этих устройствах можно почитать по ссылке. Сейчас коротко скажу, что:
- серия включает 10 типов датчиков, отличающихся погрешностью измерения и сопротивлением;
- у всех устройств рабочий диапазон температуры – 55 … + 150 °C;
- характеристика (зависимость сопротивления от температуры) задана табличным способом;
- устройства выполнены в корпусе SOD70 (как TO-92, только два вывода).
Для вычисления реальной температуры необходимо выполнить следующие действия:
- измерить сопротивление термодатчика;
- используя табличную характеристику вычислить температуру (линеаризовать характеристику).
Измерение активного сопротивления в системе Ардуино.
Прежде всего, необходимо измерить сопротивление термодатчика. АЦП платы Ардуино измеряет напряжение. Значит необходимо преобразовать сопротивление в напряжение. Существует большое количество схем с такой функцией. В нашем случае лучшим вариантом будет простой делитель напряжения.
Между сигналом питание 5 В и землей подключен делитель напряжения из двух резисторов:
- Ro – образцовый резистор;
- Rx – измеряемый резистор.
Напряжение на выходе делителя будет:
Uвых = Uвх * Rx / ( Ro + Rx )
Отсюда:
Rx = Ro / (Uвх / Uвых - 1)
Из этой формулы видно основное преимущество такого способа измерения. Нам не важно значение напряжение питания делителя (Uвх), не важна его стабильность, т.к. в формулу входит соотношение Uвх / Uвых. А так как источником опорного напряжение для АЦП служит питание 5 В, то АЦП измеряет именно соотношение Uвх / Uвых. Совершенно не важно, какое там напряжение. Мы даже не будем задавать разрешающую способность АЦП в вольтах. Мы зададим Uвх / Uвых как 1023 / N, где 1023 – максимальное значение кода АЦП, N – код АЦП.
В результате формула вычисления сопротивления будет выглядеть так:
Rx = Ro / (1023 / N - 1),
N – код АЦП.
Точность измерения сопротивления по схеме делителя не зависит от погрешности напряжения питания схемы, а определяется только точностью сопротивления образцового резистора Ro.
Правда, у этой схемы явно нелинейная характеристика и разрешающая способность. Определим ее на конкретном примере.
Если использовать датчик KTY81/120, то надо ориентироваться на следующие параметры:
Минимальное сопротивление при – 55 °C | 490 Ом |
Максимальное сопротивление при + 150 °C | 2211 Ом |
Предельно допустимый ток при + 150 °C | 2 мА |
Выберем образцовый резистор сопротивлением 3300 Ом. Тогда ток при + 150 °C будет равным
5 В / (3300 + 2211) = 0,9 мА.
Для наглядности я построил два графика в Excel. Первый это зависимость сопротивления от кода АЦП.
Видна нелинейность преобразования. Если мы используем датчик KTY81/120, то требуется измерять сопротивление в диапазоне 490 … 2211 Ом. По графику видно, что этот диапазон соответствует коду АЦП примерно 130 … 410. Можно посчитать точнее, но сейчас в этом необходимости нет. У нас примерно 280 единиц дискретности АЦП, что вполне достаточно.
Второй график показывает зависимость сопротивления соответствующего единице дискретности АЦП от кода АЦП. Т.е. он показывает насколько меняется разрешающая способность измерителя сопротивления в рабочем диапазоне.
Видно, что худшее значение разрешения измерителя сопротивления при коде 410, и равно 9 Ом. Это соответствует примерно 1 °C и нас вполне устраивает.
В итоге схема подключения термодатчика KTY81/20 к микроконтроллеру выглядит так.
Для дальнейших расчетов определим точные границы рабочего диапазона измерителя сопротивления.
N = Rx * 1023 / ( Ro + Rx )
Граница | Сопротивление датчика, Rx |
Код АЦП, N |
Минимальная температура, – 55 °C | 490 Ом | 132 |
Максимальная температура, + 150 °C | 2211 Ом | 410 |
Проект Ардуино термометра на датчике температуры KTY81/120.
Разработаем термометр – полный функциональный аналог устройства из предыдущего урока, только с использованием термодатчика KTY81/120. Применение кремневого датчика:
- расширило диапазон измерения температуры до – 55 … + 150 °C;
- позволило размещать датчик на большом расстоянии (до сотен метров) и использовать в качестве линии связи витую пару;
- снизило точность измерения до :
- ± 5 °C в диапазоне – 55 … + 100 °C
- ± 10 °C в диапазоне + 100 … + 150 °C.
Точность измерения можно улучшить использованием термодатчиков типа KTY81/110 до:
- ± 3,5 °C в диапазоне – 55 … + 100 °C;
- ± 8,5 °C в диапазоне + 100 … + 150 °C.
Устройство:
- отображает значение температуры на LED индикаторе (в автономном режиме);
- передает значение температуры на компьютер;
- при использовании программы верхнего уровня из предыдущего урока:
- отображает текущую температуру на дисплее компьютера;
- регистрирует изменение температуры с дискретностью по времени 1 сек;
- отображает изменение температуры в графическом виде.
Схема термометра.
К плате Ардуино подключим:
- 4х разрядный LED индикатор;
- Датчик температуры KTY81/120.
Все как в предыдущем уроке.
Схема Ардуино термометра с датчиком KTY81/120.
R1 – образцовый резистор. C1 – фильтрует высокочастотные помехи, можно его увеличить до 1 мкФ. Применение электролитических конденсаторов нежелательно. У них большой ток утечки. Датчик лучше подключить к контроллеру витой парой.
На макетной плате устройство выглядит так.
Резидентная программа Ардуино термометра.
Программа должна:
- считывать значение АЦП;
- усреднять коды АЦП для повышения помехозащищенности;
- вычислять температуру, используя табличную характеристику датчика KTY81/120;
- отображать значение температуры на LED индикаторе в формате:
- знак;
- сотни;
- десятки;
- единицы °C;
- передавать значение температуры раз в секунду на компьютер.
Программа большей частью аналогична программе из предыдущего урока. Изменения коснулись только принципа вычисления температуры.
Скетч программы термометра.
// термометр, датчик KTY81/120
#include <MsTimer2.h>
#include <Led4Digits.h>
#define MEASURE_PERIOD 500 // время измерения, * 2 мс
#define RO 3300 // сопротивление образцового резистора, Ом
#define MIN_ADC 132 // минимальное значение рабочего диапазона АЦП
#define MAX_ADC 410 // максимальное значение рабочего диапазона АЦП
#define POL_NUM 24 // число полюсов характеристики
int sensTable[POL_NUM][2] = { // таблица характеристики датчика
{-55, 490}, {-50, 515}, {-40, 567}, {-30, 624}, {-20, 684}, {-10, 747},
{0, 815}, {10, 886}, {20, 961}, {25, 1000}, {30,1040}, {40, 1122},
{50, 1209}, {60, 1299}, {70, 1392}, {80, 1490}, {90, 1591}, {100, 1696},
{110, 1805}, {120, 1915}, {125, 1970}, {130, 2023}, {140, 2124}, {150, 2211}
};
int codToTempTable[MAX_ADC - MIN_ADC +1]; // таблица преобразования код в температуру
int timeCount; // счетчик времени измерения
long sumA0; // переменная суммирования кодов АЦП
long avarageTemp; // среднее значение температуры (сумма кодов АЦП, среднее значение * 500)
boolean flagTempReady; // признак готовности измерения температуры
int temperature; // рассчитанная температура, °C
// тип индикатора 1; выводы разрядов 5,4,3,2; выводы сегментов 6,7,8,9,10,11,12,13
Led4Digits disp(1, 5,4,3,2, 6,7,8,9,10,11,12,13);
void setup() {
MsTimer2::set(2, timerInterrupt); // задаем период прерывания по таймеру 2 мс
MsTimer2::start(); // разрешаем прерывание по таймеру
Serial.begin(9600); // инициализируем порт, скорость 9600
//--------- предварительный расчет значений температуры -------------
// вычисление массива codToTempTable[]
int codBeginPol=0; // код полюса начала отрезка
int codEndPol; // код полюса конца отрезка
float koeff; // коэффициент для отрезка между полюсами
// цикл перебора полюсов
for ( int p= 0; p < (POL_NUM-1); p++ ) {
// вычисление кода для следующего полюса N= Rx * 1023 / (Ro + Rx)
codEndPol = (int)(((float)sensTable[p+1][1] * 1023.) / (RO + (float)sensTable[p+1][1]) + 0.5) - MIN_ADC ;
codToTempTable[codEndPol] = sensTable[p+1][0]; // температура для следующего полюса
// вычисление коэффициента для отрезка
koeff = (float)(sensTable[p+1][0] - sensTable[p][0]) / (float)(codEndPol - codBeginPol);
// интерполяция температуры
for ( int n = codBeginPol; n < codEndPol; n++ ) {
codToTempTable[n]= sensTable[p][0] + (int)((float)(n - codBeginPol) * koeff +0.5 );
}
codBeginPol=codEndPol;
}
}
void loop() {
if ( flagTempReady == true ) {
flagTempReady= false;
// данные готовы
// вычисление температуры
temperature = (int)((float)avarageTemp / 500. + 0.5);
// проверка диапазона
if (temperature < MIN_ADC) temperature= MIN_ADC;
if (temperature > MAX_ADC) temperature= MAX_ADC;
// выборка готового значения из массива
temperature= codToTempTable[temperature - MIN_ADC];
// вывод температуры на индикатор
if (temperature >= 0) {
// температура положительная
disp.digit[3]= 0; // минус не светится
disp.print((int)(temperature), 3, 1);
}
else {
// температура отрицательная
disp.digit[3]= 0x40; // отображается минус
disp.print((int)(temperature * -1), 3, 1);
}
// передача температуры на компьютер
Serial.println(temperature);
}
}
//-------------------------------------- обработчик прерывания 2 мс
void timerInterrupt() {
disp.regen(); // регенерация индикатора
// измерение среднего значения темпрературы
timeCount++; // +1 счетчик выборок усреднения
sumA0+= analogRead(A0); // суммирование кодов канала A0 АЦП
// проверка числа выборок усреднения
if ( timeCount >= MEASURE_PERIOD ) {
timeCount= 0;
avarageTemp= sumA0; // перегрузка среднего значения
sumA0= 0;
flagTempReady= true; // признак готовности результата
}
}
Загрузить скетч программы Ардуино термометр можно по ссылке:
Должны быть подключены библиотеки Led4Digits.h и MsTimer2.h. Загрузить библиотеки можно из урока 20 и урока 10.
Вычисление температуры по характеристике датчика , заданной таблицей.
Итак, у нас есть таблица зависимости сопротивления датчика от температуры. Она взята из материалов по этой ссылке.
Температура, °C | Сопротивление, Ом |
-55 | 490 |
-50 | 515 |
-40 | 567 |
-30 | 624 |
-20 | 684 |
-10 | 747 |
0 | 815 |
10 | 886 |
20 | 961 |
25 | 1000 |
30 | 1040 |
40 | 1122 |
50 | 1209 |
60 | 1299 |
70 | 1392 |
80 | 1490 |
90 | 1591 |
100 | 1696 |
110 | 1805 |
120 | 1915 |
125 | 1970 |
130 | 2023 |
140 | 2124 |
150 | 2211 |
Зависимость задана в виде 24 точек (полюсов) с разным шагом 5 или 10 °C. Нам надо из кода АЦП вычислить температуру. Для вычисления значений между полюсами характеристики следует использовать линейную интерполяцию, т.е. соединить точки отрезками прямой.
В графическом виде интерполированная характеристика датчика KTY81/120 выглядит так.
Для вычисления температуры можно сделать следующую последовательность действий:
- считать код АЦП;
- вычислить сопротивление;
- перебрать полюса характеристики и определить, в какой отрезок попадает измеренное сопротивление;
- по линейной зависимости между полюсами более точно вычислить температуру.
Но такой способ очень сложный и медленный. Сделаем другим способом – заранее рассчитаем значения температуры для каждого значения АЦП.
В разделе об измерении сопротивления мы вычислили, что рабочий диапазон датчика в кодах АЦП 132 … 410, т.е. 279 значений. Нам надо рассчитать соответствие температуры каждому коду в этом диапазоне (132 … 410).
Предварительный расчет значений температуры происходит один раз, при запуске программы в функции setup(). В результате формируется массив
int codToTempTable[MAX_ADC - MIN_ADC +1]; // таблица преобразования код в температуру
Теперь для вычисления измеренной температуры достаточно считать значение температуры из этого массива по адресу – коду АЦП.
// выборка готового значения из массива
temperature= codToTempTable[temperature - MIN_ADC];
Простая, быстрая операция.
Лучший вариант рассчитать массив codToTempTable[] заранее программой верхнего уровня и использовать его, как инициализированный массив с модификатором const. Тогда в программе Ардуино предварительных расчетов для температуры вообще не будет. Но я предпочел рассчитать его в программе микроконтроллера для наглядности.
Несколько слов об отладке программы. Вычисления довольно сложные, легко совершить ошибку. Поэтому я сделал две проверки:
- При формировании массива codToTempTable[] через последовательный порт я вывел на компьютер таблицу со значениями код, полюс, коэффициент, температура. Значение температуры в полюсах сравнил с табличными и убедился, что между полюсами значения монотонные и примерно равномерные.
- В программе временно отключил АЦП и задал имитацию изменения кода на единицу раз в секунду. С помощью регистратора программы Thermometer убедился, что изменение значения температуры монотонное.
Работа Ардуино термометра-регистратора в составе с компьютером.
В автономном режиме термометр отображает значение температуры на LED индикаторах. При подключении к устройству компьютера можно наблюдать измеренную температуру с помощью монитора последовательного порта Arduino IDE или программы верхнего уровня Thermometer.
Это программа из предыдущего урока. Загрузить ее можно по этой ссылке:
В уроке подробно рассказано об установке и работе с программой Thermometer.
Напомню, что программа, кроме индикации температуры, выполняет функцию регистратора температуры с временной дискретностью 1 секунда.
Описанный принцип измерения температуры с помощью кремниевых датчиков KTY81/120 вполне применим для других типов датчиков с нелинейным сопротивлением.
В следующем уроке научимся работать с датчиком температуры DS18B20, разработаем термометр с этим датчиком.
Спасибо Вам за уроки, очень познавательно.
Я использую 3х разрядный 7 сегментный led индикатор. Интересует как зажечь точку и указать десятые доли температуры?
Спасибо.
Принцип вывода дробных значений на индикатор описывается в уроке 20. И есть примеры в уроках 24 и 26.
Точку я зажег, но теперь в десятых всегда горит 0, как понимаю округление до целых?
А откуда дробная часть появится, если программа для этого датчика вычисляет в целых. В уроках 24 и 26 температура вычисляется в формате с дробной частью. Там дробная часть и отображается.
Значит дробную часть не вывести с этим датчиком?
Можно вывести. Но нужны другие вычисления и для этого датчика практически не имеет смысла. У него невысокая точность и дробная часть практической информации иметь не будет. Посмотрите урок 26. Там датчик точный и термометр показывает дробную часть.
Павел, если Вас какой-нибудь вопрос интересует подробно, открывайте тему на форуме сайта. Это просто.
(повторный комент, кратко)
Очень хорсшо изложены уроки, много для себя нашел полезного, спасибо
Проще, по моему, таррировку датчика поместить в Эксель, построить график, на нем добавить линию тренда, а формулу тренда прямо в ЛООП.
Это как альтернатива при практической реализации, так как выше точность, значительно меньше код, что важно для больших программ
Можно и так, конечно. Я показал способ линейной апроксимации.
Добрый день, спасибо за уроки.
Необходимо упомянуть ещё об одном существенном недостатке терморезисторов — внесение погрешности измерительным током (происходит доп. нагрев терморезистора от рассеиваемой им мощности). При выбранных вами номиналах это ощутимо. Обычно выход — выбор номиналов в сотни кОм, но это ухудшает помехозащищённость, особенно актуально при длинных шлейфах.
Спасибо за урок! Очень помог в моём проекте, но я пошёл другим путём в пересчёте сопротивления в температуру, сэкономив в 5 раз больше оперативной памяти (всего 9%).
Эдуард, здравствуйте! При компиляции скетча_25 появляется ошибка: error: size of array ‘codToTempTable’ has non-integral type ‘double’ на строке: int codToTempTable[MAX_ADC — MIN_ADC + 1]; Не получается справиться … Не подскажете, в чём может быть ошибка? Написание программы проверял несколько раз.
Здравствуйте!
А если указать в скобках размер массива числом 279?
Извините, Эдуард, я только начинаю осваивать программирование, не подскажете точнее где поставить число 279? Я по разному подставлял в эту строку — int codToTempTable[MAX_ADC — MIN_ADC + 1]; , но никак… Странно, что у всех программа работает, судя по комментариям???…
int codToTempTable[279];
Здравствуйте Эдуард!
Можно использовать датчик Рт 100 вместо датчика КТУ81?
Спасибо Вам за Ваши уроки.
Здравствуйте!
Насколько я помню, у PT100 линейная характеристика. Нет необходимости табличного способа линеаризации. Проще сделать делитель из датчика и резистора, и вычислять температуру по формуле линейной зависимости.
Здравствуйте Эдуард!
У меня к Вам пару вопросов.
Я изменил Ваш код и подключил ЖК-экран вместо LED индикаторов. Температуру показывает
к примеру 24.45 гр. Вопрос №1. Как избавиться от двух последних символов после запятой?
С помощью энкодера регулирую яркость СИДа с выводом показании (строка disp.print(LED); //регулируем яркость СИДа) на ЖК-экран.
С максимальной яркости вращаю энкодер к минимуму переходя значения со 100 на 99 и соответственно с 10 на 9 остаются нули. Вопрос № 2. Как от этих нулей избавиться? Это надо обновлять экран, но как это делать (строка disp.clear();)?! В таком случае экран мецает
{…..
LED=encoder.position; //перегружаем «щелчки» энкодера в СИД
disp.setCursor(6,1); //устанавливаем курсор на 6й символ второй строки
disp.print(LED); //регулируем яркость СИДа
disp.clear(); // очищаем экран ????????
Подскажите пожалуйста.
Спасибо.
Здравствуйте!
disp.print(temp, 0); — второй аргумент это число цифр после запятой. По умолчанию 2 цифры. Используется только с числами типа с плавающей запятой.
Установите курсор в начальную позицию и перепишите всю строку. Пустые знакоместа заполните пробелами.
disp.print(LED); //регулируем яркость СИДа
disp.print(» «);
Если что-то упустил — переспросите.
Ваша подсказка на первый мой вопрос помогла.
А вот со вторым я не понял disp.print(» «); ?
таких кавычек на клавиатуре не обнаружил.
Повторите, объясните только более развернуто.
Спасибо.
Здравствуйте!
У вас печатается, например, 3х разрядное число, а затем поверх него 2х разрядное. А третий разряд остается от предыдущего числа. Надо его стереть пробелом. Т.е. напечатать пробел.
Как вариант, можете стирать пробелами все поле, предназначенное для числа, а затем его выводить.
А кавычки в функции обычные. В поле комментариев WordPress заменяет кавычки на другие.
Получилось.
Спасибо.
Здравствуйте! Подскажите, будет ли работать по описанной здесь схеме работать датчик 0-90 Ом? Какой величины лучше взять Ro для такой схемы?
Здравствуйте!
Возможно, работать будет, но надо сделать доработки. Такой низкоомный датчик лучше записать от более низкого напряжения 1 В. Иначе через него будет протекать слишком большой ток. Соответственно необходимо переключить АЦП на входное напряжение 1.1 В.
В идеале напряжение на датчике надо сформировать из ИОН микроконтроллера. Сделать повторитель на ОУ.
Если у вас датчик линейный, то нет необходимости в табличной линеаризации его характеристики.