Как подключить термопару к микроконтроллеру
AVR.RU 
Рисунок 1. Устройство термопары.
Термопары дешевы и работают с большим диапазоном температур; различают несколько их типов в зависимости от металлов, из которых состоит термопара: например, термопары k-типа – наиболее распространённые в бытовых приборах – состоят из хромеля и алюмеля; термопары J-типа — железо-константановые, E-типа — хромель-константановые и т.д.
Однако есть у термопары и недостатки – во-первых, результат измерения относителен – это разница температур – а во-вторых, в общем случае зависимость между температурой и термоЭДС нелинейна. На рисунке ниже представлен график зависимости ТЭДС и температуры горячего спая (при температуре холодного спая 0 ˚C) для термопары k-типа и линейная аппроксимация этой зависимости; при этом мы взяли температуру горячего спая в границах от 0 до 300 ˚C – именно с такой температурой мы будем работать.
Проведём нехитрые исчисления и заметим, что линейная аппроксимация достаточно точна – погрешность не выходит за рамки среднего изменения ТЭДС на градус.
Ещё одна проблема: наш результат получается в мВ – соответственно, при сравнении значения с опорным напряжением 5В у нас получаются какие-то крошечные величины, которые похожи друг на друга; тут было решено действовать «физически» — использовать операционный усилитель AD8541.
Опытным путём было выяснено, что при подключении термопары к операционному усилителю классическими способами возникают несколько проблем:
Первая проблема: операционный усилитель работает не очень-то линейно при напряжениях входа, близких к минусу или, наоборот, плюсу питания – появляется некоторая погрешность, которая зависит как раз от этой близости к минусу или плюсу; при этом усилитель пытается сделать на управляемом входе (входе, к которому подключена обратная связь) такое же напряжение, как на другом входе + некоторая константа.
Вторая проблема – это то, что если мы собираемся использовать усилитель при однополярном питании (V+ — 5 В, V- — земля, 0 В), то на выходе не может получиться отрицательное напряжение, что делает невозможным использование схем с инверсным подключением.
Ну и третья проблема, связанная конкретно с подключением термопары к усилителю: при подключении термопары одним концом к земле (или даже немного «приподняв» над землей один конец), а другим концом к ОУ получается так, что второй конец у нас как бы болтается в воздухе – за счёт того, что входное сопротивление операционного усилителя очень большое. И термопара при этом становится антенной, которая ловит все возможные шумы с окрестности. И именно эти шумы и подаются на вход усилителю, и именно шумы наш усилитель и усиливает – иногда перебивая сам сигнал. Непорядок!
Поэтому мы решили, а почему бы не подать на оба конца термопары землю? И вот что получилось:
При такой схеме на отрицательный вход от термопары поступает небольшое отрицательное напряжение, и ОУ пытается через выход и резистор подать такое же положительное напряжение. При этом первая проблема «превращается» в константное значение, поэтому эта погрешность в последствии уйдет при калибровке. Также нужно заметить, что что на инверсный вход подаётся очень небольшое отрицательное значение – таким образом, мы как бы сглаживаем вторую проблему. Ну и главное преимущество этой схемы – избавление от шумов!
Теперь разберемся с «техникой» — а именно как микроконтроллер получит информацию от термопары. Для этого будем использовать АЦП – аналого-цифровой преобразователь (на вход получаем аналоговый сигнал, а на выход выдаём цифровой). АЦП у нашей микросхемы 10-разрядный, при этом абсолютная погрешность — ±2 младших бита, то есть по сути получаем 8 «чистых» разрядов; максимальное быстродействие – 15000 выборок/с. Однако при такой скорости результат будет не очень точный, да и нам не требуется молниеносно отслеживать изменение входящего сигнала; наибольшая точность АЦП достигается, когда тактовая частота модуля находится в диапазоне 50-200 кГц; при настройке АЦП можно выбрать предделитель (минимум 2, так как АЦП не может работать быстрее, чем ½ частоты процессора, максимум 128) – нас устроит предделитель 128, тогда частота АЦП будет 8000 / 128 = 62.5 кГц, иными словами, преобразование будет производиться раз в 16 мс – этого вполне достаточно.
Входящий сигнал сравнивается с неким опорным: это может быть напряжение питания АЦП, внешний источник опорного напряжения (должен быть не выше напряжения питания) или внутренний источник опорного напряжения (1.1 V, однако многие ругают этот вариант и говорят, что напряжение на внутреннем ИОН «скачет», делая результат АЦП непредсказуемым); по результатам сравнения выдается «значение» уровня сигнала – от минимального, «равного» GND – 0x0000, до максимального, равного опорному напряжению (0x3FFF).
Нужно отметить, что напряжение питания АЦП (AVCC) не должно отличаться от напряжения питания микроконтроллера больше, чем на ±0.3 V. Подробнее про АЦП можно почитать в замечательных книгах Евстифеева Андрея Викторовича.
Попробуем «завести» АЦП: для этого нам нужно его инициализировать (включить и установить предделитель), включить (непрерывное преобразование), сделать функцию прерывания, которая будет записывать значение АЦП в некоторую переменную и отсылать посылку «Пришло новое значение АЦП», а также функцию, которая будет возвращать это самое значение АЦП по запросу: мы не можем этого сделать напрямую, потому что наша переменная будет двухбайтовая, и пока мы читаем первый байт, может вызываться прерывание, которое изменить значение нашей переменной, и второй байт мы прочтём уже от следующего значения АЦП; поэтому сначала придется ждать прихода посылки, потом запрещать прерывания, считывать значение в некоторую промежуточную переменную, разрешать прерывания и, наконец, возвращать значение из промежуточной переменной.
В общем, получаем следующее:
Заголовочный файл ADCTemperature.h:
#ifndef ADC_TEMPERATURE_H #define ADC_TEMPERATURE_H #include "ioavr.h" #include "inavr.h" #include "stdint.h" extern volatile uint8_t OnADCNewValue; //посылка о приходе нового значения АЦП //инициализация АЦП void ADCInit(); //прерывание по получению нового значения АЦП #pragma vector = ADC_vect __interrupt void GetADCValue(void); //включение АЦП void ADCOn(); //выключение АЦП void ADCOff(); //получение нового значения АЦП uint16_t ADCGetNewValue(); #endif
#include "ADCTemperature.h" volatile uint16_t ADCNewValue; volatile uint8_t OnADCNewValue; //инициализация АЦП void ADCInit() < ADCNewValue = 0; OnADCNewValue = 0; __disable_interrupt(); ADMUX = (1 //включение АЦП void ADCOn() < ADCSRA |= (1 //выключение АЦП void ADCOff() < ADCSRA &= (˜((1 //прерывание по получению нового значения АЦП #pragma vector = ADC_vect __interrupt void GetADCValue(void) < uint8_t ADCHigh; ADCNewValue = ADCL; //сохранили результат в "промежуточную" переменную, видную только внутри библиотеки ADCHigh = ADCH; ADCNewValue += (ADCHigh //получение нового значения АЦП uint16_t ADCGetNewValue() < uint16_t Result; while (!OnADCNewValue); //ждём, когда придёт новое значение OnADCNewValue = 0; //очищаем посылку __disable_interrupt(); Result = ADCNewValue; //запоминаем текущее значение АЦП __enable_interrupt(); return Result; >
Видно, что переменные ADCNewValue и OnADCNewValue объявлены volatile – они изменяются не только в основной программе, но и в прерываниях, поэтому мы запрещаем компилятору как-либо оптимизировать код с участием этих переменных – иначе он может посчитать, что они не изменяются, и просто убрать их из конечного кода.
Основной файл, функция main (остальное можно не трогать):
void main( void ) < uint16_t ADCValue; DDRB = 0; PORTB = 0; DDRD = 0; PORTD = 0; DDRC = 0; PORTC = 0; DisplayInit(); ADCInit(); FastTimerInit(); DisplayShowHEX(0); ADCOn(); while(1) < ADCValue = ADCGetNewValue(); DisplayShowHEX(ADCValue); __delay_cycles(4000000); >>
Рассмотрим теперь технические недочёты того, что получилось. Во-первых, самое неприятное, что может случиться при такой библиотеке – то, что мы где-нибудь выключим АЦП (например, в спящем режиме), а затем забудем запустить и попробуем считать значение через функцию ADCGetNewValue – и она, разумеется, подвесит нам всю программу на ожидании посылки OnADCNewValue. Вообще любые подобные циклы while нужно использовать с крайней осторожностью, и если при их использовании программа зависает, то такие участки кода – первые претенденты на проверку.
Кроме того, при использовании виртуальных машин вообще не желательно делать такие while-ы – никто не может гарантировать, что ожидание не затянется; даже 16 мс – это уже много. Поэтому в таком случае лучше заменить ожидание на проверку условия – если посылка пришла, то считываем значение в некоторую переменную и возвращаем «true» — считывание прошло успешно – а иначе возвращаем «false», и все.
Ещё одна проблема – малая разрядность нашего АЦП; два младших байта по сути своей являются шумом, и нам остаётся только 8 разрядов – и при этом мы измеряем температуру от, например, 20 градусов до 300 – это никак не укладывается в 256 значений! Придётся как-то «математически» увеличивать градацию температуры; и несмотря на то, что точность термопары k-типа составляет ±1.5 ˚C, нам нужно постоянно отслеживать именно изменение температуры, поэтому попробуем усреднять и сглаживать получаемое значение.
Подробней о технологии сглаживания и усреднения в этой статье.
Воспользуемся конечными формулами:
(1) Pi+1 = Pi — (Pi / (N * M)) + Xi * M
(2) Ki+1 = (Pi+1 / N) / M
N и M – числа, являющиеся степенями двойки; N – коэффициент инерции фильтра («если сигнал Х в какой-то момент изменил уровень, то каждое N раз измерений значение фильтра будет в e (2,718..) раз ближе к сигналу Х (грубо — на 63%)»)
M же является коэффициентом увеличения разрядности (на сколько разрядов увеличиваем разрядность АЦП).
При этом операции с N и умножение с M мы можем проводить целочисленно, то есть сдвигом, а вот последнее деление на M делаем с плавающей точкой. При этом выполнять расчёт формулы (1) нужно каждый раз, когда мы получаем код АЦП (то есть, раз в 16 мс), а вот получать сглаженное значение – то есть выполнять трудоёмкое деление с плавающей точкой – достаточно по запросу.
Дальше будем переводить коды АЦП в градусы. Мы решили, что будем считать, что перевод линейный – то есть, нам нужны только угловой коэффициент и коэффициент сдвига; далее при необходимости мы просто умножаем сглаженное значение на одну константу и прибавляем другую (естественно, всё это нецелочисленное).
Теперь о большууущем косяке: о компенсации холодного спая. Её мы не реализовали. Дело в том, что когда мы спохватились об этой проблеме, плата была уже разведена, а свободных ножек не осталось. Возможно, когда-нибудь мы всё исправим, но пока будем надеяться, что наша плата не будет особо нагреваться. Путей решения этой проблемы несколько: во-первых, мы можем отказаться от кварца и переписать работу с часовым таймером; на освободившиеся ножки можно навесить какой-нибудь датчик температуры, который будет считывать температуру около платы. Таким датчиком, теоретически, может быть и всем известный ds1820, однако следует помнить, что 1-wire крайне требовательна ко времени (ссылка на статью), и при общении через этот протокол нужно запрещать прерывания – а значит, будут страдать как индикация на дисплее, так и «реакция» контроллера на нажатие на кнопки или вращение энкодера. Помимо ds1820, можно использовать, например, терморезистор (однако у него нелинейная зависимость между сопротивлением и температурой);
Ещё один вариант — хорошее, но дорогое решение – использование специальных усилителей с компенсацией холодного спая (например, AD8495).
В общем, на данный момент – пока проект считается учебным — решения с компенсацией холодного спая пока нет, но мы работаем над этим.
Вернемся же к программе – попробуем сделать красивую реализацию считывания, усреднения кодов АЦП и перевод их в температуру.
Внимание! Наша программа становится достаточно большой, поэтому ей нужен больший объём стека! Увеличим его с стандартного «0x20» — 32 байт — до «0х40» — 64 байт, чтоб с запасом было. Настройки меняются в Project->Options.. (смотрите, чтобы в области «Workspace» был выбран не какой-нибудь конкретный файл, а весь проект)->General Options->System; меняем значение Data stack (CSTACK).
Заголовочный файл станет таким:
#ifndef ADC_TEMPERATURE_H #define ADC_TEMPERATURE_H #include "ioavr.h" #include "inavr.h" #include "stdint.h" #define ADC_DEFAULT_ANGULAR_COEF 1 #define ADC_DEFAULT_OFFSET_COEF 0 typedef struct < volatile uint8_t atOnNewADCValue; //посылка о приходе нового значения АЦП //переменные для перевода значения температуры из кода АЦП в градусы (линейная аппроксимация) double atAngularCoef; double atOffsetCoef; volatile uint16_t atADCCurrValue; volatile uint16_t atADCAvgValue; volatile uint32_t atADCAccumulativeSum; double atCurrTemperature; >TADCTemperature; extern TADCTemperature ADCTemperature; //инициализация АЦП void ADCInit(); //прерывание по получению нового значения АЦП #pragma vector = ADC_vect __interrupt void GetADCValue(void); //включение АЦП void ADCOn(); //выключение АЦП void ADCOff(); //получение нового значения температуры (записывается в структуру), возвращает 1 в случае успешного получения нового значения, и 0 - в случае ошибки uint8_t ADCGetNewTemperatureValue(); //получение кода АЦП uint8_t ADCGetNewCode(); #endif
#include "ADCTemperature.h" //параметры АЦП-преобразования const uint8_t ADCAvgDegree = 13; //степень двойки у "усреднения" (наше N) const uint8_t ADCExpansionDegree = 2; //степень двойки у "расширения" (из 10-битного АЦП в 12-битное) (наше M) const uint16_t ADCExpansionDivider = 4; //2ˆADCExpansionDegree TADCTemperature ADCTemperature; //инициализация АЦП void ADCInit() < ADCTemperature.atADCAccumulativeSum = 0; ADCTemperature.atADCCurrValue = 0; ADCTemperature.atADCAvgValue = 0; //АЦП инициализируется до определения коэффициентов из EEPROM! ADCTemperature.atAngularCoef = ADC_DEFAULT_ANGULAR_COEF; ADCTemperature.atOffsetCoef = ADC_DEFAULT_OFFSET_COEF; ADCTemperature.atCurrTemperature = 0; ADCTemperature.atOnNewADCValue = 0; __disable_interrupt(); ADMUX = (1 //включение АЦП void ADCOn() < ADCTemperature.atOnNewADCValue = 0; __disable_interrupt(); ADCSRA |= (1 //выключение АЦП void ADCOff() < __disable_interrupt(); ADCSRA &= (˜((1 //прерывание по получению нового значения АЦП #pragma vector = ADC_vect __interrupt void GetADCValue(void) < uint8_t ADCHigh; uint32_t currSum; ADCTemperature.atADCCurrValue = ADCL; //сохранили результат ADCHigh = ADCH; ADCTemperature.atADCCurrValue += (ADCHigh > (ADCExpansionDegree + ADCAvgDegree)); currSum += ADCTemperature.atADCAccumulativeSum; ADCTemperature.atADCAccumulativeSum = (ADCTemperature.atADCCurrValue //получение нового значения температуры (записывается в структуру), возвращает 1 в случае успешного получения нового значения, и 0 - в случае ошибки uint8_t ADCGetNewTemperatureValue() < if (ADCTemperature.atOnNewADCValue) //если пришло новое значение АЦП < ADCTemperature.atOnNewADCValue = 0; __disable_interrupt(); ADCTemperature.atADCAvgValue = ADCTemperature.atADCAccumulativeSum >> ADCAvgDegree; //усредняем по формуле значение АЦП __enable_interrupt(); //увеличиваем разрядность АЦП ADCTemperature.atCurrTemperature = (double)ADCTemperature.atADCAvgValue / ADCExpansionDivider; //вычисляем значение температуры по линейной формуле ADCTemperature.atCurrTemperature = ADCTemperature.atAngularCoef * ADCTemperature.atTemperatureValue + ADCTemperature.atOffsetCoef; return 1; > else return 0; > //получение нового значения температуры (записывается в структуру), возвращает 1 в случае успешного получения нового значения, и 0 - в случае ошибки uint8_t ADCGetNewCode() < if (ADCTemperature.atOnNewADCValue) //если пришло новое значение АЦП < ADCTemperature.atOnNewADCValue = 0; __disable_interrupt(); ADCTemperature.atADCAvgValue = ADCTemperature.atADCCurrValue; //считываем реальное значение АЦП //ADCTemperature.atADCAvgValue = ADCTemperature.atADCAccumulativeSum >> ADCAvgDegree; //считываем усредненное значение АЦП __enable_interrupt(); return 1; > else return 0; >
Добавляем в библиотеку семисегментного дисплея функцию отображения температуры:
//перевод числа с плавающей точкой в строку; точность - десятые доли; [-999.9; 9999.9]; занимает 6 символов, в конец '\0' не пишет void DoubleThousandsWithTenthToStr(double Value, char* buff) < uint16_t intValue, definitionNumbers; if (Value < 0) //на первый сегмент пишем "-", если число отрицательно < buff[0] = '-'; intValue = (uint16_t)(Value * (-1)); Value = Value * (-1); >else //или сотни положительного числа, или просто " " < intValue = (uint16_t)Value; if (intValue >= 1000) < definitionNumbers = (intValue / 1000) % 10; buff[0] = definitionNumbers + '0'; >else buff[0] = DispBlankSymbol; > if (intValue >= 100) < definitionNumbers = (intValue / 100) % 10; buff[1] = definitionNumbers + '0'; >else buff[1] = DispBlankSymbol; //пишем десятки, если они есть, или пробел if (intValue >= 10) < definitionNumbers = (intValue / 10) % 10; buff[2] = definitionNumbers + '0'; >else buff[2] = DispBlankSymbol; buff[3] = intValue % 10 + '0'; //записали единицы buff[4] = '.'; buff[5] = ((uint16_t)(Value * 10)) % 10 + '0'; //записали десятичные > //перевод числа с плавающей точкой в строку; точность - десятые доли; [-99.9; 999.9]; занимает 5 символов, в конец '\0' не пишет void DoubleHundredsWithTenthToStr(double Value, char* buff) < uint16_t intValue, definitionNumbers; if (Value < 0) //на первый сегмент пишем "-", если число отрицательно < buff[0] = '-'; intValue = (uint16_t)(Value * (-1)); Value = Value * (-1); >else //или сотни положительного числа, или просто " " < intValue = (uint16_t)Value; if (intValue >= 100) < definitionNumbers = (intValue / 100) % 10; buff[0] = definitionNumbers + '0'; >else buff[0] = DispBlankSymbol; > //пишем десятки, если они есть, или пробел if (intValue >= 10) < definitionNumbers = (intValue / 10) % 10; buff[1] = definitionNumbers + '0'; >else buff[1] = DispBlankSymbol; buff[2] = intValue % 10 + '0'; //записали единицы buff[3] = '.'; buff[4] = ((uint16_t)(Value * 10)) % 10 + '0'; //записали десятичные > //отображение значения температуры на дисплее void DisplayShowTemperatureValue(double Value) < char buff[9]; DoubleThousandsWithTenthToStr(Value, buff); //перевели число //buff[5] = DispBlankSymbol; buff[6] = 'C'; buff[7] = DispPointSymbol; buff[8] = 0; DisplayShowStr(buff); >
Пока — до калибровки — будем отображать температуру начиная с тысяч; потом уже, когда научимся калибровать значение, будем отображать температуру до сотен.
Основной файл, функция main:
void main( void ) < DDRB = 0; PORTB = 0; DDRD = 0; PORTD = 0; DDRC = 0; PORTC = 0; DisplayInit(); ADCInit(); FastTimerInit(); DisplayShowHEX(0); ADCOn(); while(1) < if (ADCGetNewTemperatureValue()) //если есть новое значение АЦП < DisplayShowTemperatureValue(ADCTemperature.atCurrTemperature); >__delay_cycles(8000000); > >
Подключение термопары к микроконтроллеру
В данной статье речь пойдет о подключении термопары к микроконтроллеру Atmega8. Термопара представляет собой два проводника из разных металлов спаянных в одной точке. В этой точке при разных температурах возникает термоэдс. Метталлы берутся такими чтобы зависимость термоэдс от температуры была наиболее линейна. Это снижает погрешность измерений и облегчает расчет температуры.
Термопары испольщуются там где нам нужно измерить высокую температуру до 2000 градусов. При таких температурах цифровые датчики сразу бы вышли из строя. Есть много разных видов теромопар, но наибольшей популярностью пользуются термопары типа K (хромель-алюминий), это связано с их практически линейным графиком изменения теромоэдс. Такие термопары устанавливаются в различные виды водонагревателей, паяльных станций, их используют в установках по плавке металла.
График зависимости термоэдс от температуры для термопар типа K практически линейный на всем диапазоне температуры.
Измеренно значение термоэдс нужно преобразовать в температуру. Преобразование осуществляется при помощи коэффициента который постояннен для всего диапазано измерения температуры.
Для измерения термоэдс будем использовать АЦП (аналого-цифровой преобразователь). Для того чтобы подлючить термопару к микрокнтроллеру используется ОУ (операционный усилитель) который включается по неинвертирующей схеме. Дело в том что значение эдс очень мало и его необходимо усилить при помощи ОУ.\
Для того чтобы найти отношение входного и выходного напряжения нужно воспользоваться формулой:
От номинала сопротилений R1 и R2 которые выполняют функцию обратной связи, зависит отношение входного и выходного напряжения. Уселение сигнала должно выбирать исходя из выбранного ИОН — источника опорного напряжения. Например если в качестве ИОН выбрано напряжения в 5 В, а максимальный предел измеряемой температуры 1000 градусов, при такой температуре термоэдм состовит 41.3 мВ. Это напряжение необходимо будет преобразовать в 5 В на входе в АЦП. Т.е нам нужно чтобы при такой температуре на входе в АЦП было напряжение в 5 В. Коэффициент усиления получился равным 120.
Подключение термопары к микроконтроллеру
В результате получилась такой модуль:
Схема подключения двухстрочного дисплея к микрокнтроллеру
А так выглядит теомапара которая шла в комплекте с мультиметром
Код программы
$regfile = "m8def.dat" $crystal = 8000000 Dim W As Integer 'подключение двухстрочного дисплея Config Lcdpin=Pin,Rs=Portb.0,E=Portd.7,Db4=Portd.6,Db5=Portd.5,Db6=Portb.7,Db7=Portb.6 Config Lcd = 16 * 2 Cursor Off Cls 'считывание значения с АЦП по прерыванию от таймера Config Timer1 = Timer , Prescale = 64 On Timer1 Acp 'конфигурация АЦП Config Adc = Single , Prescaler = Auto , Reference = Avcc Enable Interrupts Enable Timer1 Do Cls Rem Температура: Lcd "Teјѕepaїypa:" Lowerline Lcd W Waitms 200 Loop 'работа с АЦП Acp: Start Adc 'запуск АЦП W = Getadc(1) W = W / 1.28 'подгоняем замеры под действ. температуру Return End
Число 1.28 бы подогнато опытным путем. В качестве эталонной температуры была температура кипения воды 100 градусов. Зная температуру и подгоняя коэффициент добиваемся аналогичных показаний на дисплее.

После того как выставил показания, измерил температуру в пламени зажигалки, прибор показал значение в 700 градусов. При комнатной температуре 25 градусов прибор почему то показывал 50.
Как подключить термопару к микроконтроллеру
Термопара – это один из видов температурных датчиков, который может применяться в измерительных устройствах и системах автоматизации. Ей присущи определенные преимущества: дешевизна, высокая точность, широкий по сравнению с термисторами и микросхемами цифровых датчиков температуры диапазон измерения, простота и надежность. Однако выходное напряжение термопары мало и относительно, а схема измерителя на термопаре сложна, так как предъявляются жесткие требования к прецизионному усилению сигнала с термопары и к схеме компенсации. Для разработки таких устройств существуют специализированные микросхемы, интегрирующие схему преобразования и обработки аналогового сигнала. С помощью этих микросхем можно построить достаточно компактный измеритель температуры с термопарой в качестве датчика (Рисунок 1).

Рисунок 1. Внешний вид цифрового измерителя температуры с термопарой в качестве датчика.
Принципы
Википедия определяет принцип действия термопары следующим образом:
Принцип действия основан на эффекте Зеебека или, иначе, термоэлектрическом эффекте. Между соединенными проводниками имеется контактная разность потенциалов. Если стыки связанных в кольцо проводников находятся при одинаковой температуре, сумма таких разностей потенциалов равна нулю. Когда же стыки находятся при разных температурах, разность потенциалов между ними зависит от разности температур. Коэффициент пропорциональности в этой зависимости называют коэффициентом термо-ЭДС. У разных металлов коэффициент термо-ЭДС разный и, соответственно, разность потенциалов, возникающая между концами разных проводников, будет различной. Помещая спай из металлов с отличными коэффициентами термо-ЭДС в среду с температурой Т1, мы получим напряжение между противоположными контактами, находящимися при другой температуре Т2, которое будет пропорционально разности температур Т1 и Т2 (Рисунок 2).

Рисунок 2. Генерирование термо-ЭДС в термопаре, образованной двумя разнородными проводниками.
Существует несколько типов термопар, в зависимости от используемой пары материалов (чистый металл или сплав). В нашем проекте мы используем термопару K-типа (хромель-алюмель), которая часто применяется в промышленных инструментах и приборах. Выходное напряжение термопары K-типа составляет приблизительно 40 мкВ/°С, следовательно, потребуется схема усиления сигнала с небольшим смещением напряжения по входу.
Как упоминалось выше, термо-ЭДС пропорциональна разности температур между холодным и горячим спаем. Это означает, что температура холодного спая должна быть известна для вычисления фактического значения температуры горячего спая. Для этого потребуется схема компенсации холодного спая, которая будет автоматически вводить поправку к измеренной термо-ЭДС (Рисунок 3).

Рисунок 3. Схема компенсации холодного спая, введение поправки к измеренной термо-ЭДС.
Чтобы получить значение температуры с помощью термопары потребуется аналоговая схема, например прецизионный операционный усилитель и схема компенсации холодного спая. Однако, существует несколько видов специализированных микросхем со встроенным интерфейсом термопары. Эти микросхемы интегрируют указанные выше аналоговые схемы и значительно упрощают проект. В нашем случае мы выбрали микросхему MAX31855 компании Maxim. Она содержит аналоговую схему и аналого-цифровой преобразователь, следовательно, на выходе микросхемы мы получим цифровые данные. Перед покупкой микросхемы необходимо заранее определить тип термопары, которая будет использоваться в устройстве.
Основные характеристики микросхемы MAX31855:
- Диапазон измерения температуры: от –270 °С до +1800 °С;
- Разрешение: 14 бит, шаг 0.25 °С;
- Простой SPI-совместимый интерфейс (режим чтения данных);
- Схема компенсации опорного спая термопары;
- Схема детектирования замыкания проводов термопары на шину питания и общую шину;
- Схема детектирования разрыва в измерительной цепи;
- Исполнения для термопар типов K, J, N, T и E;
- 8-ми выводной корпус.
Как подключить термопару к микроконтроллеру
Термопары широко применяются там где необходимо точно померить высокие температуры, т емпературы вплоть до 2500°C. То есть там, где цифровые датчики бы сразу сдохли от перегрева, применяются термопары. Разновидностей термопар существует достаточно много, но самое большое распространение получили хромель-алюмелевые (тип К) термопары, из-за своей дешевизны и практически линейному изменению термоэдс. Этот вид термопар ставятся в водонагреватели и другие бытовые приборы с контролем температуры, их повсеместно используют для контроля температуры при плавке металла, с помощью этих термопар контролируется нагрев жала в паяльной станции. Поэтому будет весьма полезно познакомиться с ними поближе.
Термопара это два проводника из разных металлов и имеющих общую точку контакта (спай). В точке этого контакта возникает разность потенциалов. Эта разность потенциалов зовется термоэдс и напрямую зависит от температуры, в которой находится спай. Металлы подбираются таким образом, чтобы зависимость термоэдс от температуры нагрева была наиболее линейна. Это упрощает расчет температуры и сокращает погрешность измерений.
Так широко применяемые хромель-алюмелевые термопары имеют достаточно высокую линейность и стабильность показаний на всем диапазоне измеряемых температур.
Ниже приведен график для хромель-алюмелевых термопар (тип К) показывающий, зависимость возникающей термоэдс от температуры спая (в конце статьи будет ссылка на график с большим разряшением):
Таким образом значение термоэдс достаточно умножить на нужный коэффициент и получить температуру, не заморачиваясь с табличными значениями и аппроксимацией — один коэффициент на весь диапазон измерений. Очень просто и понятно.
Но встает вопрос о подключении термопары к микроконтроллеру. Понятно что если на выходе термопары напряжение, тогда задействуем АЦП, но разность потенциалов на выходе термопары слишком мала, чтобы уловить хоть что-то. Поэтому прежде его нужно увеличить, например, применив операционный усилитель.
Берём стандартную схему неинвертирующего включения операционного усилителя:
Отношение входного и выходного напряжений описывается простой формулой:
V out /V in = 1 + (R2/R1)
От значений резисторов обратной связи R1 и R2 зависит коэффициент усиления сигнала. Величину усиления сигнала нужно подбирать с учетом того, что будет использоваться в качестве опорного напряжения.
Допустим опорным будет напряжение питания микроконтроллера 5V. Теперь необходимо определится с диапазоном температур, которые собираемся измерять. Я взял пределом измерения 1000 °C. При этом значении температуры на выходе термопары будет потенциал примерно 41,3мВ. Это значение должно соответствовать напряжению в 5 вольт на входе АЦП. Поэтому операционник должен иметь коэффициент усиления не менее 120. В итоге родилась такая схема:
В загашнике у меня нашлась давно собранная плата с этим операционником, собирал как предусилитель для микрофона, ее я и применил:
Собрал на бредборде такую схему подключения двухстрочного дисплея к микроконтроллеру:
Термопара тоже валялась без дела долгое время — она шла в комплекте с моим мультиметром. Спай закрыт в металлическую гильзу.
Код Bascom-AVR для работы с термопарой:
$regfile = «m8def.dat»
$crystal = 8000000
Dim W As Integer
‘подключение двухстрочного дисплея
Config Lcdpin = Pin , Rs = Portb . 0 , E = Portd . 7 , Db4 = Portd . 6 , Db5 = Portd . 5 , Db6 = Portb . 7 , Db7 = Portb . 6
Config Lcd = 16 * 2
Cursor Off
Cls
‘считывание значения с АЦП по прерыванию от таймера
Config Timer1 = Timer , Prescale = 64
On Timer1 Acp
‘конфигурация АЦП
Config Adc = Single , Prescaler = Auto , Reference = Avcc
Enable Interrupts
Enable Timer1
Do
Cls
Rem Температура:
Lcd «Teјѕepaїypa:»
Lowerline
Lcd W
Waitms 200
Loop
‘работа с АЦП
Acp :
Start Adc ‘запуск АЦП
W = Getadc ( 1 )
W = W / 1 . 28 ‘подгоняем замеры под действ. температуру
Return
End
Число 1,28 в знаменателе получил опытным путем, подгоняя значение считанное с АЦП в известное значение температуры.
Коротко расскажу как это происходило у меня. В качестве эталона замера температуры выступил пар в кипящем чайнике. Для чистоты эксперимента сначала замерил температуру пара мультиметром, подсоединив к нему термопару. Удостоверившись в правильности показаний, замерил температуру уже новоиспеченным девайсом и подгоняя коэффициент деления, установил значение 100°C.

После выставления первой контрольной точки, хорошо было бы повторить вышеописанное при другой известной температуре, но д альше экспериментировать не стал. В пламени зажигалки измерил ~700 °C (что похоже на правду), а вот при комнатной температуре девайс выдавал под 50°C, наверно дело в мусоре младших разрядов АЦП. Но думаю собрать, например, терморегулятор для паяльника вполне сгодится.
Порядок вывода комментариев:
0
Спам
15 romani (28.12.2013 01:11) [Материал]
Огромнейшее спасибо за объяснение! Вы помогли мне защитить курсач! и написать его)))
0
Спам
14 miolk (27.12.2013 22:49) [Материал]
Для паяльной станции предпочтительней термопара ХК (хромель-копель). У неё термоэдс в несколько раз выше, соответственно измерения точнее. ХА используют при более высоких температурах, т.к. для ХК предел — около 400 градусов Цельсия.
+1
Спам
13 forter (24.12.2013 20:20) [Материал]
Я уже писал, что у ОУ имеется напряжение смещения. Так вот, есть еще и токи смещения, они незначительные, но при измерении таких малых напряжений, как у термопары, и они играют роль нескомпенсированных ошибок. В общем, чтобы их хоть как — то скомпенсировать входы ОУ нагружаются резистрами. В первом приближении можно считать, что токи смещения одинаковы для инвертирующего и неинвертирующего входов. Соответственно, резисторы по входам должны быть одинаковые. На инвертирующем входе, в нашем случае, параллельно подключен резистор 1 кОм и 5,1 + 120 кОм, таким образом, эквивалентное сопротивление на этом входе 1 кОм, поэтому, строго говоря, на неинвертирующем входе должен быть резистор R4 не 10 кОм, а 1 кОм.
Ну, и, конечно, резистор играет некоторую защитную роль.
Аналоговая техника, особенно измерение малых величин токов и напряжений, требует очень аккуратного и грамотного подхода.
0
Спам
12 romani (23.12.2013 22:53) [Материал]
какую фнкцию выполняет резистор R4?
0
Спам
11 exersizze (10.08.2013 23:54) [Материал]
forter, благодарю за разъяснения!
+1
Спам
10 forter (09.08.2013 15:50) [Материал]
Здравствуйте. коллеги!
По поводу того, зачем нужен R2?
Цепь R2C1 — просто низкочастотный фильтр. Как известно у ОУ имеются свои шумы, наконец, на его вход тоже может попасть наводка. Вот чтобы избавиться или, по крайней мере, ослабить эти эффекты и применяют такие цепи. Но, по — моему, правильнее было бы применить резистор с номиналом, допустим, в 100 КОм, а емкость конденсатора увеличить до 0,1 мкф. Правда, нельзя слишком сильно уменьшать сопротивление — может возникнуть самовозбуждение ОУ.
Есть еще замечание — изменяя коэффициент усиления, мы просто изменяем результирующий наклон зависимости напряжения на выходе ОУ от температуры, не учитывая, что в ОУ есть еще одна очень неприятный эффект — смещение нуля. Это означает, что даже при отсутствии сигнала на выходе (можно считать, что на входе) всегда есть постоянное смещение. Если взять другой ОУ, имеющий выводы балансировки нуля (например, как отечественный 140УД6), от этого можно избавиться — между выводами балансировки подключается многооборотный резистор, а его движок подключается к питанию, но это только для двухполярного питания. Этим эффектом страдают все ОУ. Все бы ничего, но, будучи сбалансированным при одной температуре, ОУ разбалансируется, если его температура изменится — это, так называемый, температурный дрейф нуля. Если кто — то работал с электронно — лучевыми осциллографами, тот знает, что через несколько минут после его включения луч обязательно смещается вверх или вниз. Этот эффект и есть дрейфа нуля.
По этой причине и применяют, особенно при измерении очень малых напряжений (как в случае с термопарой) прецизионные ОУ, у которых этот эффект намного меньше, но стоят они намного дороже обыкновенных ОУ
0
Спам
9 gadz (08.08.2013 21:04) [Материал]
Почитайте дополнительно о компенсация холодного спая. Здесь показано для чего это
http://www.compeljournal.ru/enews/2007/15/10
0
Спам
8 exersizze (06.04.2012 23:45) [Материал]
Операционник все равно дает очень маленький выходной ток, на память десятки микроампер. А выход операционника отличается очень маленьким сопротивлением. И поэтому при уменьшении R2 разницы не будет, имхо.
а вообще я этот резюк поставил в соответствии со схемой на радиокоте про цифровую паялку, там похожая схема усилителя и стоит именно 1МОм, решил идти по проверенной схеме )
0
Спам
7 Andrew (06.04.2012 14:47) [Материал]
У МЕНЯ ВОПРОС, ДЛЯ ЧЕГО НУЖЕН РЕЗИСТОР R2 1Мом ? ЕСЛИ В ДАТАШИТАХ НА АВР УКАЗАНО ЧТО:
The ADC is optimized for analog signals with an output impedance of approximately 10 kΩ or less.
Я ТАК ПОНИМАЮ ВЫХОДНОЕ СОПРОТИВЛЕНИЕ ДОЛЖНО БЫТЬ МЕНШЕ 10КОМ, А У ВАС АЖ 1МОМ ?
0
Спам
6 exersizze (28.03.2012 09:04) [Материал]
Вы правы поиск рулит) Доходчиво и просто расписано здесь
0
Спам
5 tenevikus (27.03.2012 22:41) [Материал]
в поделках они нужны. расскажите нам.
0
Спам
4 tenevikus (27.03.2012 22:40) [Материал]
спасибо за статью огромное. но есть одна просьба. раз все ходят к вам за информацией. то расскажите в следующем материале в вашей доходчивой форме про эти опер.усилители. понятно что книги и поиск рулят. но мы же у вас столуемся и куча тонкостей нам лишняя.
0
Спам
3 exersizze (26.03.2012 19:53) [Материал]
Спасибо за совет, интересная ссылка за спам не считается)
На моей платке стоит подстроечник на 5 кОм, возможно им получится подрегулироват коэффициент усиления операционника, а вообще да, муторное это дело. Под каждую термопару нужно проводить индивидуальную калибровку.
Еще при большой разнице горячего и холодного спаев (точки соединения металлов и свободные концы) может возникать погрешность в десятки градусов, поэтому для точного измерения высоких температур нужно городить схему компенсации холодного спая. И самое главное, что десятибитного АЦП микроконтроллера явно не достаточно для измерения диапазона температур выше 1000 град.
0
Спам
2 4ester (26.03.2012 13:43) [Материал]
кстати забыл сказать что в схеме есть ошибка соединения в месте R6? r8. быть не должно.
0
Спам