Данный модуль представляет собой часть проекта аквариумного контроллера и предназначен для работы с датчиком рН, в состав которого входит сам натриево-стеклянный датчик и аналоговый усилитель, превращающий ЭДС датчика в сигнал диапазона 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)