Данный модуль представляет собой часть проекта аквариумного контроллера и предназначен для работы с датчиком рН, в состав которого входит сам натриево-стеклянный датчик и аналоговый усилитель, превращающий ЭДС датчика в сигнал диапазона 0....Vcc, который можно подавать на вход АЦП МК.
Модуль предусматривает повышение точности замеров путем вычисления среднего арифметического 32 измерений АЦП; как ручную, так и автоматическую калибровку коэффициентов усиления и смещения по двум точкам замера; а также вычисляет тренд (растущий/падающий/нейтральный) за 10 последних измерений.
В целом этот модуль может быть легко адаптирован для работы с любыми аналоговыми датчиками с линейной характеристикой. Процедура будет точно такая же, как в случае с датчиком рН: сначала датчик (освещенности, приближения, перемещения и т.д.) помещается в одни известные условия, и вызывается _Calibrate1 с указанием измеряемой величины, которую модуль должен давать на выходе; затем датчик помещают в другие условия, и вызывают _Calibrate2 с указанием второй величины. Эта процедура завершает процесс калибровки расчетом новых коэффициента усиления и величины начального смещения, и модуль готов автоматически выдавать измеряемые значения в нужном диапазоне.
Тренд вычисляется по методу наименьших квадратичных отклонений, и может служить показателем движения измеряемой величины (растет, спадает, не меняется).
' ********************************************************************************************
' * *
' * Измеритель рН *
' * v1.001 *
' * *
' * by Alexandra Bernshtein *
' * id260853760 *
' ********************************************************************************************
'
' Этот модуль измеряет величину PH и работает с датчиком, выдающим на порт АЦП аналоговое напряжение.
' Порт АЦП должен быть настроен в основном коде, и номер канала задан в ADC_PHSENS.
'
' До того, как читать значение PH вызывом PH_GetPH или PH_GetPHString, необходимо вызывать PH_Measure,
' которая осуществляет непосредственные измерения с датчика и расчитывает величину РH.
'
' Для корректного преобразования напряжения датчика в стандатрное значение PH требуется установить
' коэффициент усиления и начальное смещение при помощи вызовов PH_SetMultiplier
' и PH_SetOffset соответственно. При замкнутых входных контактах усилителя напряжение, поступающее на АЦП, должно
' быть равно 2,5 В (регулируется потенциометром усилителя), а значение РН должно вычисляться как 7.0.
' В модуле определены константы PH_fMulDEF, PH_fOffDEF, содержащие некаблированные (примерные) базовые
' значения коэффициента усиления и начального смещения, которые могут быть загружены до процедуры точной
' калибровки датчика в буферных растворах. Однако по одной точке правильно настроить измеритель невозможно.
'
' Процедура калибровки по двум точкам автоматически вычисляет коэфициент усиления и начальное
' смещение. Для этого датчик помещается сначала в первый буферный р-р с известным РН, после чего вызывается
' PH_Calibrate1 (РН 1 р-ра), затем промывается дистиллированной водой и помещается во второй буф. р-р, за чем
' следует вызов PH_Calibrate2 (РН 2 р-ра). Процедура PH_Calibrate2 завершает калиборовку вычислением
' новых значений коэффициента усиления и начального смещения. Вычисленные значения коэффициента усиления и
' начального смещения можно прочитать при помощи PH_GetMultiplier и PH_GetOffset, например, для сохранения в EEPROM.
$nocompile
declare sub PH_SetMultiplier(byval fMul as single) ' установить коэф. усиления для вычисления стандатрного значения РН
declare function PH_GetMultiplier() as single ' получить коэф. усиления, например, для сохранения в EEPROM после калибровки
declare sub PH_SetOffset(byval fOff as single) ' установить нач. смещение для вычисления стандатрного значения РН
declare function PH_GetOffset() as single ' получить нач. смещение, например, для сохранения в EEPROM после калибровки
declare sub PH_Calibrate1(byval fPHBuff1 as single) ' начать калибровку в первом буферном р-ре
declare sub PH_Calibrate2(byval fPHBuff2 as single) ' закончить калибровку во втором буферном р-ре
declare sub PH_Measure() ' провести замеры с датчика и вычислить PH
declare function PH_GetPH() as single ' возращает стандартную величину PH в диапазоне 0.0...14.0 (среднее 7.0)
declare function PH_GetPHString() as string * 4 ' то же, в форме строки "0.00" для значений до 9.99, "00.0" более 9.99
declare function PH_GetVoltage() as single ' возращает напряжение на входе АЦП (для найстройки усилителя датчика {0...5 В}; V=2.5В при PH=7.0)
declare function PH_GetVoltageString() as string * 4 ' то же, в форме строки "0.00"
declare function PH_GetTrend() as byte ' возвращает направление тренда за последние 10 измерений = PH_TREND_UP | PH_TREND_DN | PH_TREND_NO
dim PH_fValue as single ' стандартная величина РН в диапазоне от 0.0 до 14.0
dim PH_sValue as string * 4 ' то же, в форме строки "0.00" для значений до 9.99, "00.0" более 9.99
dim PH_fVoltage as single ' напряжение на входе АЦП (для калибровки усилителя датчика {0.0...5.0 В}; V=2.5В при PH=7.0)
dim PH_sVoltage as string * 4 ' то же, в форме строки "0.00"
dim PH_fMul as single ' коэф. усиления для вычисления стандатрного значения РН
dim PH_fOff as single ' нач. смещение для вычисления стандатрного значения РН
dim PH_wADC as word ' суммарное значение для вычисления ср. арифметического значений АЦП (0...1023)
dim PH_bCount as byte ' счетчик циклов (замеры с АЦП, вычисление тренда)
dim PH_fCalibrate1 as single ' для запоминания первого калибровочного рН
dim PH_wCalibrate1 as word ' для запоминания первого калибровочного значения ADC
' для вычисления тренда
dim PH_Trend_fPH(10) as single ' значения последних 10 измерений
dim PH_Trend_fPHav as single ' среднее за 10 последних измерений
dim PH_Trend_fNPHsum as single ' сумма произведений n * PH_Trend_fPH[n], n = 1...10
dim PH_Trend_fK as single ' коэфициент при линейном уравнеии тренда; знак показывает рост или падение
' базовые коэффициенты pH-метра
const PH_fMulDEF = 0.0137
const PH_fOffDEF = 0.0
' направление тренда
const PH_TREND_NO = 0
const PH_TREND_UP = 1
const PH_TREND_DN = 2
const PH_TREND_TOLERANCE = 0.1 ' порог, выше которого тренд не считается нулыевым; физически это арктангенс угла наклона линии тренда
' для вычисления тренда из 10 замеров
const PH_Trend_Xav = 5.5 ' X average = (1 + 2 + ... + 10) = 55 / 10 = 5.5
const PH_Trend_Divider = 82.5 ' сумма всех Х (1 + 2 + ... + 10) ^ 2 - 10 * PH_Trend_Xav = 385 - 10 * 30.25 = 82.5
goto PH_METER_done ' требуется компилятору BASCOM при использовании оцпии "config submode = old"
sub PH_SetMultiplier(single fMul) ' установить коэф. усиления для вычисления стандатрного значения РН
PH_fMul = fMul
end sub
function PH_GetMultiplier() as single ' получить коэф. усиления, например, для сохранения в EEPROM после калибровки
PH_GetMultiplier = PH_fMul
end function
sub PH_SetOffset(single fOff) ' установить нач. смещение для вычисления стандатрного значения РН
PH_fOff = fOff
end sub
function PH_GetOffset() as single ' получить нач. смещение, например, для сохранения в EEPROM после калибровки
PH_GetOffset = PH_fOff
end function
sub PH_Calibrate1(single fPHBuff1) ' начать калибровку в первом буферном р-ре
PH_Measure
PH_fCalibrate1 = fPHBuff1 ' {
PH_wCalibrate1 = PH_wADC ' { запомнить параметры первой точки измерения
end sub
sub PH_Calibrate2(single fPHBuff2) ' закончить калибровку во втором буферном р-ре
' для автоматической калибровки нужно посчитать коэффициенты линейного уравнения по двум заданным точкам
' затем следует решить систему лин. уравнений:
' { pH[1] = PH_fMul * PH_wADC[1] + PH_fOff
' { pH[2] = PH_fMul * PH_wADC[2] + PH_fOff
' получается:
' PH_fMul = (pH[1] - pH[2]) / (PH_wADC[1] - PH_wADC[2])
' PH_fOff = pH[2] - PH_fMul * PH_wADC[2]
PH_Measure
PH_fValue = PH_fCalibrate1 - fPHBuff2 ' pH[1] - pH[2]
PH_wCalibrate1 = PH_wCalibrate1 - PH_wADC ' -----------------------
PH_fMul = PH_fValue / PH_wCalibrate1 ' PH_wADC[1] - PH_wADC[2] (PH_fValue исползуется тут как временная переменная)
PH_fOff = PH_wADC * PH_fMul
PH_fOff = fPHBuff2 - PH_fOff
PH_Measure ' обновить изменения
end sub
sub PH_Measure() ' провести замеры с датчика и вычислить PH
PH_wADC = 0 ' для повышения точности изменений следует сделать несколько замеров (32) и вычислить среднее арифметическое
for PH_bCount = 1 to 32 ' (максимальное число замеров без переполнения суммы формата word - 64)
PH_wADC = PH_wADC + getadc(ADC_PHSENS)
next
shift PH_wADC , right , 5 ' чтобы вычислить ср. арифм., надо поделить сумму на 32
PH_fVoltage = PH_wADC / 204.6 ' значение 1023 = 5 B
PH_sVoltage = fusing(PH_fVoltage , "0.00")
PH_fValue = PH_wADC * PH_fMul
PH_fValue = PH_fValue + PH_fOff
if PH_fValue < 10 then
PH_sValue = fusing(PH_fValue , "0.00")
else
PH_sValue = fusing(PH_fValue , "00.0")
end if
' для вычисления тренда нужно сдвинуть старые замеры и запомнить свежий:
for PH_bCount = 1 to 9 ' сдвинуть последние 9 замеров назад
PH_Trend_fPH(PH_bCount) = PH_Trend_fPH(PH_bCount + 1)
next
PH_Trend_fPH(10) = PH_fValue ' запомнить свежей замер
end sub
function PH_GetPH() as single ' возращает стандартную величину PH в диапазоне 0.0...14.0 (среднее 7.0)
PH_GetPH = PH_fValue
end function
function PH_GetPHString() as string * 4 ' то же, в форме строки "0.00" для значений до 9.99, "00.0" более 9.99
PH_GetPHString = PH_sValue
end function
function PH_GetVoltage() as single ' возращает напряжение на входе АЦП (для калибровки усилителя датчика {0.0...5.0 В}; V=2.5В при PH=7.0)
PH_GetVoltage = PH_fVoltage
end function
function PH_GetVoltageString() as string * 4 ' то же, в форме строки "0.00"
PH_GetVoltageString = PH_sVoltage
end function
function PH_GetTrend() as byte ' возвращает направление тренда за последние 10 измерений = PH_TREND_UP | PH_TREND_DN | PH_TREND_NO
PH_Trend_fPHav = 0
PH_Trend_fNPHsum = 0
for PH_bCount = 1 to 10
PH_Trend_fPHav = PH_Trend_fPHav + PH_Trend_fPH(PH_bCount)
PH_Trend_fK = PH_bCount * PH_Trend_fPH(PH_bCount) ' }
PH_Trend_fNPHsum = PH_Trend_fNPHsum + PH_Trend_fK ' } PH_Trend_fK используется тут как временная переменная
next
PH_Trend_fPHav = PH_Trend_fPHav / 10 ' вычислить ср. арифм.
PH_Trend_fK = PH_Trend_Xav * PH_Trend_fPHav ' }
PH_Trend_fK = PH_Trend_fK * 10 ' }
PH_Trend_fK = PH_Trend_fNPHsum - PH_Trend_fK ' } верхняя часть дроби
PH_Trend_fK = PH_Trend_fK / PH_Trend_Divider ' поделить на делитель
select case PH_Trend_fK ' определить направление тренда с учетом порога
case is > PH_TREND_TOLERANCE
PH_GetTrend = PH_TREND_UP
case is < -PH_TREND_TOLERANCE
PH_GetTrend = PH_TREND_DN
case else
PH_GetTrend = PH_TREND_NO
end select
end function
PH_METER_done: ' требуется компилятору BASCOM при использовании оцпии "config submode = old"
Отредактировано Cirno_9 (2019-09-12 16:49:42)