В прошлый раз, было описано как измерить температуру с помощью термопары. Термопара позволяет производить измерение в довольно широком диапазоне, но в случае, когда нужна высокая стабильность измерения и чуствительность к малым изменениям — применяют термосопротивления.
Термосопротивления работают в меньшем диапазоне (до 600 градусов в пределе), зато они описываются полиномом второй степени (третьей, если включать отрицательный диапазон), имеют очень высокую стабильность считываемого показания (поскольку сигнал измеряется десятками Ом на градус, а не милливольтами).
Чтобы измерить температуру с помощью термосопротивления, нужно замерить его сопротивление, и по его передаточной характеристике определить температуру. Для измерения сопротивления через резистор пропускают ток и с помощью АЦП замеряют напряжение на резисторе. Не вдаваясь в схемотехнику, примем следующее: к паре AIN1-AIN2 подведены выводы с термосопротивления, к паре AIN3-AIN4 подведены выводы с опорного стабильного сопротивления. Через оба резистора идёт один и тот же ток. Теперь, чтобы получить текущее сопротивление терморезистора нужно сделать следующее:
1. Измеряем напряжение на опорном сопротивлении. Uo=I*Ro (где Uo — напряжение опоры, Ro — опорное сопротивление).
2. Измеряем напряжение на термосопротивлении. Uт=I*Rт.
3. I = Uo/Ro, Rт = Uт/I = Uт/Uo * Ro
Таким образом, чтобы выяснить текущее сопротивление нам нужно измерить отношение напряжений между опорным и измерительным резисторами и домножить на сопротивление опорного резистора. Поскольку операция деления является черезвычайно дорогой, воспользуемся тем, что пара AIN3-AIN4 может использоваться как опора. При этом, снимаемый код будет ни чем иным, как «какое напряжение имеет сигнал с пары, считая что опора на REFIN(или REFIN2) равна 2.5В». То есть, измеренный сигнал приводится к «известной» опоре.
При этом следует помнить, что мы так же можем включить умножение измеряемого сигнала такое, чтобы полный диапазон температур использовал все доступные эффективные биты ацп. Следует так же помнить, что напряжение с опорного сопротивления желательно иметь близким, но не большим чем 2.5В.
В моем случае, опорное сопротивление составляет 5КОм (2 резистора по 10КОм в параллель), напряжение с опорного сопротивления около 2В.
При этом, включив делитель измерения 0.064, полный диапазон температур (до 600 градусов) умещается во все 24 разряда АЦП.
В результате исследования шумов измерительного тракта, оказалось, что «осмысленными» являются толькол 14.5 бит измеряемого сигнала, что означает, что из измеряемых 24 разрядов можно использовать только старшие 16, так как младшие представляют собой только шум.
Итак, у нас есть опорное напряжение около 2В на AIN3-AIN4, термосопротивление (платина 100Ом) подключено к паре AIN1-AIN2, и мы хотим узнать температуру в градусах. Первым делом, нам нужно измерить отношение напряжения на термосопротивлении к опорному напряжению. Для этого
MOV ADC0CON2, #10001010b ; Измерение AIN1-AIN2 относительно AIN3-AIN4 (REFIN2) MOV ADC0CON1, #00100011b ; Измерение 0...160mV (REF*0.064) MOV ADCSF, #REQ_SF MOV ADCMODE, #00100100b ; Калибровка нуля MOV ADCMODE, #00100101b ; Калибровка максимума MOV ADCMODE, #00100010b ; Запросить 1 измерение на основном АЦП
настраиваем основной ацп на измерение отношения к REFIN2, включаем подходящее усиление, настраиваем ацп (загружая заводские настройки) и запускаем измерение. После измерения, будет вызвано прерывание от АЦП (вектор 33H), где
; INTERRUPT from ADC ORG 0033H ADCInt: MOV DataH, ADC0H ; Термопару сохраняем в DataH/M/L MOV DataM, ADC0M MOV DataL, ADC0L SETB ADCReady ; И ставим бит ADCReady. Бит ADCBusy сбросит основная программа CLR RDY0 RETI
,
считываем код ацп и выходим. Итак, у нас есть измеренное с АЦП значение (Кт), которое представляет собой Uт, считая что Uо=2.5В, еще и усиленное.
В числах, после удаления делителей, некратных степени двойки, получаем:
8000*Rт = 2*Rо*Кт / 256
Взяв 2*Rо за калибровочный коэффициент (в случае использования идеальных компонент 2Rо = 10000), построим таблицу соответствия 8000*Rт => Температура. Для этого накидаем маленькую простенькую программку, которая через каждые 65536 значений для 8000*Rт выдаст соответствующую температуру. Аналогично прошлому разу, температуру будем задавать в квантах по 1/32 градуса (фиксированная двоичная точка в 5 бит).
В сухом остатке, у нас есть: таблица из 40 точек термосопротивления, между каждой из которых 65536 отсчетов, а сама таблица содержит линейную температуру в кельвинах (чтобы работать с беззнаковыми числами), код с АЦП и калибровочный коэффициент наклона. Дальше вычисления просты:
1. Перемножаем 16бит кода АЦП на 16бит коэффициент. Из результата оставляем только 3 старших байта (для деления на 256 нужно отбросить младший байт).
2. Из полученных 24 бит, старшие 8 бит — точка входа в таблицу (она построена через 65536 значения)
3. Из таблицы выбираем пару чисел в точке входа, выбираем следующую пару и вычисляем разницу между следующей парой и текущей — получаем температурную длину диапазона
4. Умножаем разницу по таблице на младшие 16бит произведения, считая только старшие 16 бит результата — получаем фактическую добавку температуры внутри диапазона
5. Прибавляем к фактической добавке начало диапазона — мы определили температуру.
В коде это выглядит примерно так:
CalcMainRes: ; Вычислить как термосопротивление ; В Data1 - напряжение на терморезисторе, относительно опоры ; U0 = I*R0; } I=U0/R0 ; U1 = I*R1; } R1=U1/U0*R0 ; где Data1 < => U1, R0 = 5K, U0=2.5В ; ================================= Таблица температур в 40 точках через ; == 8000*R1 = [2R0]*Data1 / 256 == 65535 от 8000*R1 ; ================================= Калибровочный 2R0 изначально 10000 ; R2:R1:R0 = Data1*Coeff/256 MOV A, Data1H ; R2:R1 = Data1H*CoeffH MOV B, CoeffH MUL AB MOV R2, B MOV R1, A ; --- MOV A, Data1M ; R1:R0 = Data1M*CoeffH MOV B, CoeffH MUL AB MOV R0, A MOV A, B ADD A, R1 MOV R1, A JNC CMRNoOv1 INC R2 CMRNoOv1: ; --- MOV A, Data1H ; R1:R0 = Data1H*CoeffL MOV B, CoeffL MUL AB ADD A, R0 MOV R0, A MOV A, B JNC CMRNoOv2 INC A JNZ CMRNoOv2 INC R2 CMRNoOv2: ADD A, R1 MOV R1, A JNC CMRNoOv3 INC R2 CMRNoOv3: ; --- MOV A, Data1M ; R0:Tmp = Data1M*CoeffL MOV B, CoeffL MUL AB JNB ACC.7, CMRNoOv4 INC R0 CJNE R0, #0, CMRNoOv4 INC R1 CJNE R0, #0, CMRNoOv4 INC R2 CMRNoOv4: MOV A, B ADD A, R0 JNC CMRNoOv5 INC R1 CJNE R1, #0, CMRNoOv5 INC R2 CMRNoOv5: ; R2:R1:R0 = [2R0]*Data1/256. ; R2 = Вход в таблицу термосопротивления ; R1:R0 = интерполянт в таблице MOV A, R2 ADD A, R2 ; A=2*R2 ADD A, #LOW(RTD_100_385) MOV DPL, A MOV A, #HIGH(RTD_100_385) ADDC A, #0 MOV DPH, A ; DPTR=таблица CLR A MOVC A, @A+DPTR MOV R4, A CLR A MOVC A, @A+DPTR MOV R5, A ; R5:R4 = текущая точка таблицы CLR A MOVC A, @A+DPTR CLR C SUBB A, R4 MOV R6, A ; R7:R6 = разница следующей и текущей точек таблицы CLR A MOVC A, @A+DPTR SUBB A, R5 MOV R7, A ; ThermoHL = R5:R4+(R7:R6*R1:R0)/65536 ; R7*R1 => ThermoHL MOV A, R7 MOV B, R1 MUL AB MOV ThermoH, B MOV ThermoL, A ; R6*R1 => ThermoL:Tmp MOV A, R6 MOV B, R1 MUL AB MOV Tmp1, A MOV A, B ADD A, ThermoL MOV ThermoL, A JNC CRMTNoOv1 INC ThermoH CRMTNoOv1: ; R7*R0 => ThermoL:Tmp MOV A, R7 MOV B, R0 MUL AB ADD A, Tmp1 JNB ACC.7, CRMTNoOv2 INC ThermoL MOV A, ThermoL JNZ CRMTNoOv2 INC ThermoH CRMTNoOv2: MOV A, B ADDC A, ThermoL ;MOV ThermoL, A JNC CRMTNoOv3 INC ThermoH CRMTNoOv3: ; THermoHL += R5:R4 ADD A, R4 MOV ThermoL, A JNC CRMTNoOv4 INC ThermoH CRMTNoOv4: MOV A, R5 ADD A, ThermoH MOV ThermoH, A ; ThermoH вычислено JMP CalcEnd
Отдельного внимания, конечно, заслуживает генератор таблицы… Однако, поскольку «математик» у меня только слово в дипломе, пытаться вывести обратный полином я не стал, а нарисовал просто итерационный процесс, который пробегает по одному градусу весь диапазон, на границах диапазона уточняя числа до 1e-5.
Подробно приводить код не буду, смотрите в приложенном gen_rtd.pl