В предыдущем посте Модуль рН-метра; подходит для любых линейных аналоговых датчиков я описала модуль для работы с датчиком кислотности среды (рН).
Однако уже там было сказано, что принципы, заложенные в код модуля, подходят для работы с любыми линейными аналоговыми датчиками.
Поэтому логичным развитием темы стал универсальный сервис для работы сразу с несколькими датчиками.
Достаточно откалибровать датчик по двум точкам с известными условиями, и сервис будет автоматически вычислять измеряемую величину.

Код модуля:

Код:
' ********************************************************************************************
' *                                                                                          *
' *                               ANALOG SENSORS SERVICE                                     *
' *                                      v1.001                                              *
' *                                                                                          *
' *                              by Alexandra Bernshtein                                     *
' *                                    id260853760                                           *
' ********************************************************************************************
'
' Этот сервис работает с линейными датчиками, выдающими на порт АЦП аналоговое напряжение.
' Он автоматически пересчитывает напряжение с датчиков в требуемую величину, а также вычисляет тренд.
'
' Перед компиляцией укажите число используемых каналов аналоговых измерений в ASENS_COUNT. Чем больше
' каналов используется, тем больше ОЗУ требуется для работы модуля. Поэтому не следует указывать
' больше каналов, чем требуется в конкретном проекте.
'
' Перед началом работы каждому логическому измерительному каналу в ASENS_bADCChan(лог.канал) нужно
' сопоставить физический канал АЦП.
'
' Измеренное значение можно получить в формате с плав. точкой через ASENS_GetValue, или в строковом
' формате через ASENS_GetValueString. Однако прежде, чем читать измеренные значения, необходимо вызывать
' процедуру ASENS_Measure, которая осуществляет непосредственные замеры с АЦП и расчитывет все величины.
'
' Для корректного преобразования напряжения, поступающего от датчика на вход АЦП, в измеряемую величину
' требуется установить коэффициент усиления и начальное смещение при помощи вызовов ASENS_SetMultiplier
' и ASENS_SetOffset соответственно. Если датчик ранне уже был откалиброван, их можно загрузить из EEPROM или
' добавленных в код констант, например, для датчика pH это константы PH_fMulDEF и PH_fOffDEF, содержащие
' базовые значения коэффициента усиления и начального смещения.
'
' Для калибровки датчика "по двум точкам" датчик сначала помещается в первые известные условия,
' после чего вызывается ASENS_Calibrate1 (значение в первых условиях). Затем датчик помещается во вторые
' известные условия, и вызывается ASENS_Calibrate2 (значение во вторых условиях).
' Например, датчик температуры помещается сначала в условия 0°С, и вызывается ASENS_Calibrate1(SENS_THERMO, 0.0),
' затем в условия +25°С, после чего вызывается ASENS_Calibrate2(SENS_THERMO, 25.0). Процедура ASENS_Calibrate2
' завершает калиборовку, автоматически рассчитывая значения коэффициента усиления и начального смещения.
' Вычисленные значения можно прочитать через ASENS_GetMultiplier и ASENS_GetOffset, например, для сохранения
' результатов калибровки в EEPROM.
'
' Для аппаратной настройки некоторых датчиков, оборудованных усилителем, необходимо знать
' напряжение на входе АЦП. Для этих целей предусмотрены ASENS_GetVoltage и ASENS_GetVoltageString.
'
' ASENS_GetTrend возвращает направление тренда за последние 10 измерений = ASENS_TREND_UP | ASENS_TREND_DN | ASENS_TREND_NO
' Порог, при котором тренд считается не горизонтальным, задается константами ASENS_TREND_TOLERANCE_ UP|DN

$nocompile

const ASENS_COUNT = 4                                                           ' число каналов аналоговых измерений
const ASENS_PHMETER = 1                                                         ' именованные каналы

' базовые коэффициенты для pH-метра
const PH_fMulDEF = 0.0137
const PH_fOffDEF = 0.0

' направление тренда
const ASENS_TREND_NO = 0
const ASENS_TREND_UP = 1
const ASENS_TREND_DN = 2
const ASENS_TREND_TOLERANCE_UP = 0.1                                            ' +порог, выше которого тренд не считается нулыевым; физически это арктангенс угла наклона линии тренда
const ASENS_TREND_TOLERANCE_DN = -0.1                                           ' -порог, выше которого тренд не считается нулыевым; физически это арктангенс угла наклона линии тренда
' для вычисления тренда из 10 замеров
const ASENS_Trend_Xav = 5.5                                                     ' X average = (1 + 2 + ... + 10) = 55 / 10 = 5.5
const ASENS_Trend_Divider = 82.5                                                ' сумма всех Х (1 + 2 + ... + 10) ^ 2 - 10 * ASENS_Trend_Xav = 385 - 10 * 30.25 = 82.5

declare sub ASENS_SetADCChannel(byval bChannel as byte , byval bADCChannel as byte)       ' присвоить каналу измерений канал АЦП
declare sub ASENS_SetMultiplier(byval bChannel as byte , byval fMul as single)  ' установить коэф. усиления
declare function ASENS_GetMultiplier(byval bChannel as byte) as single          ' получить коэф. усиления
declare sub ASENS_SetOffset(byval bChannel as byte , byval fOff as single)      ' установить нач. смещение
declare function ASENS_GetOffset(byval bChannel as byte) as single              ' получить нач. смещение
declare sub ASENS_Calibrate1(byval bChannel as byte , byval fValue1 as single)  ' начать калибровку - по первой точке
declare sub ASENS_Calibrate2(byval bChannel as byte , byval fValue2 as single)  ' закончить калибровку  - по второй точке
declare sub ASENS_Measure(byval bChannel as byte)                               ' провести замеры с датчика и вычислить измеряемые величины
declare function ASENS_GetValue(byval bChannel as byte) as single               ' возращает измеряемую величину
declare function ASENS_GetValueString(byval bChannel as byte) as string * 4     ' то же, в форме строки "0.00" для значений до 9.99, "00.0" более 9.99
declare function ASENS_GetVoltage(byval bChannel as byte) as single             ' возращает напряжение на входе АЦП (для найстройки усилителя датчика)
declare function ASENS_GetVoltageString(byval bChannel as byte) as string * 4   ' то же, в форме строки "0.00"
declare function ASENS_GetTrend(byval bChannel as byte) as byte                 ' возвращает направление тренда за последние 10 измерений = ASENS_TREND_UP | ASENS_TREND_DN | ASENS_TREND_NO

dim ASENS_bADCChan(ASENS_COUNT) as byte                                 ' сопоставление лог. канала измерений и физического канала АЦП
dim ASENS_fValue(ASENS_COUNT) as single                                         ' измеряемая величина
dim ASENS_sValue(ASENS_COUNT) as string * 4                                     ' то же, в форме строки "0.00" для значений до 9.99, "00.0" более 9.99
dim ASENS_fVoltage(ASENS_COUNT) as single                                       ' напряжение на входе АЦП (для калибровки усилителя)
dim ASENS_sVoltage(ASENS_COUNT) as string * 4                                   ' то же, в форме строки "0.00"
dim ASENS_fMul(ASENS_COUNT) as single                                           ' коэф. усиления для вычисления измеряемого значения
dim ASENS_fOff(ASENS_COUNT) as single                                           ' нач. смещение  для вычисления измеряемого значения
dim ASENS_wADC as word                                                          ' суммарное значение для вычисления ср. арифметического значений АЦП (0...1023)
dim ASENS_bCount , ASENS_bTMP as byte                                           ' счетчик циклов (замеры с АЦП, вычисление тренда); временная переменная
dim ASENS_fCalibrate1 as single                                                 ' для запоминания первого калибровочного значения измеряемой величины
dim ASENS_wCalibrate1 as word                                                   ' для запоминания первого калибровочного значения ADC
' для вычисления тренда
dim ASENS_Trend_fValue(ASENS_COUNT , 10) as single                              ' значения последних 10 измерений
dim ASENS_Trend_fValueAV as single                                              ' среднее за 10 последних измерений
dim ASENS_Trend_fNValueSUM as single                                            ' сумма произведений n * ASENS_Trend_fValue[n], n = 1...10
dim ASENS_Trend_fK as single                                                    ' коэфициент при линейном уравнеии тренда; знак показывает рост или падение

goto ASENS_METER_done                                                           ' требуется компилятору BASCOM при использовании оцпии "config submode = old"

sub ASENS_SetADCChannel(byte bChannel , byte bADCChannel)                       ' присвоить логическому каналу измерений физический канал АЦП
   ASENS_bADCChan(bCHannel) = bADCChannel
end sub

sub ASENS_SetMultiplier(byte bChannel , single fMul)                            ' установить коэф. усиления
   ASENS_fMul(bChannel) = fMul
end sub

function ASENS_GetMultiplier(byte bChannel) as single                           ' получить коэф. усиления
   ASENS_GetMultiplier = ASENS_fMul(bChannel)
end function

sub ASENS_SetOffset(byte bChannel , single fOff)                                ' установить нач. смещение
   ASENS_fOff(bChannel) = fOff
end sub

function ASENS_GetOffset(byte bChannel) as single                               ' получить нач. смещение
   ASENS_GetOffset = ASENS_fOff(bChannel)
end function

sub ASENS_Calibrate1(byte bChannel , single fValue1)                            ' начать калибровку по первой точке
   ASENS_Measure bChannel                                                       ' провести изменение
   ASENS_fCalibrate1 = fValue1                                                  ' {
   ASENS_wCalibrate1 = ASENS_wADC                                               ' { запомнить параметры первой точки измерения
end sub

sub ASENS_Calibrate2(byte bChannel , single fValue2)                            ' закончить калибровку по второй точке
   ASENS_Measure bChannel                                                       ' провести изменение
   ASENS_fValue(bChannel) = ASENS_fCalibrate1 - fValue2
   ASENS_wCalibrate1 = ASENS_wCalibrate1 - ASENS_wADC
   ASENS_fMul(bChannel) = ASENS_fValue(bChannel) / ASENS_wCalibrate1
   ASENS_fOff(bChannel) = ASENS_wADC * ASENS_fMul(bChannel)
   ASENS_fOff(bChannel) = fValue2 - ASENS_fOff(bChannel)
   ASENS_Measure bChannel                                                       ' обновить изменение
end sub

sub ASENS_Measure(byte bChannel)                                                ' провести замеры с АЦП и вычислить измеряемые величины
   ASENS_wADC = 0                                                               ' для повышения точности изменений следует сделать несколько замеров (32) и вычислить среднее арифметическое
   for ASENS_bCount = 1 to 32                                                   ' (максимальное число замеров без переполнения суммы формата word - 64)
      ASENS_wADC = ASENS_wADC + getadc(ASENS_bADCChan(bCHannel))                ' накапливать измерения из канала АЦП
   next
   shift ASENS_wADC , right , 5                                                 ' чтобы вычислить ср. арифм., надо поделить сумму на 32
   ASENS_fVoltage(bChannel) = ASENS_wADC / 204.6                                ' значение 1023 = 5 B
   ASENS_sVoltage(bChannel) = fusing(ASENS_fVoltage(bChannel) , "0.00")
   ASENS_fValue(bChannel) = ASENS_wADC * ASENS_fMul(bChannel)
   ASENS_fValue(bChannel) = ASENS_fValue(bChannel) + ASENS_fOff(bChannel)
   if ASENS_fValue(bChannel) < 10 then
      ASENS_sValue(bChannel) = fusing(ASENS_fValue(bChannel) , "0.00")
   else
      ASENS_sValue(bChannel) = fusing(ASENS_fValue(bChannel) , "00.0")
   end if
   ' для вычисления тренда нужно сдвинуть старые замеры и запомнить свежий:
   for ASENS_bCount = 1 to 9
      ASENS_bTMP = ASENS_bCount + 1
      ASENS_Trend_fValue(bChannel , ASENS_bCount) = ASENS_Trend_fValue(bChannel , ASENS_bTMP)       ' сдвинуть последние 9 замеров назад
   next
   ASENS_Trend_fValue(bChannel , 10) = ASENS_fValue(bChannel)                   ' запомнить свежей замер
end sub

function ASENS_GetValue(byte bChannel) as single                                ' возращает измеряемую величину
   ASENS_GetValue = ASENS_fValue(bChannel)
end function

function ASENS_GetValueString(byte bChannel) as string * 4                      ' то же, в форме строки "0.00" для значений до 9.99, "00.0" более 9.99
   ASENS_GetValueString = ASENS_sValue(bChannel)
end function

function ASENS_GetVoltage(byte bChannel) as single                              ' возращает напряжение на входе АЦП (для калибровки усилителя датчика)
   ASENS_GetVoltage = ASENS_fVoltage(bChannel)
end function

function ASENS_GetVoltageString(byte bChannel) as string * 4                    ' то же, в форме строки "0.00"
   ASENS_GetVoltageString = ASENS_sVoltage(bChannel)
end function

function ASENS_GetTrend(byte bChannel) as byte                                  ' возвращает направление тренда за последние 10 измерений = ASENS_TREND_UP | ASENS_TREND_DN | ASENS_TREND_NO
   ASENS_Trend_fValueAV = 0
   ASENS_Trend_fNValueSUM = 0
   for ASENS_bCount = 1 to 10
      ASENS_Trend_fValueAV = ASENS_Trend_fValueAV + ASENS_Trend_fValue(bChannel , ASENS_bCount)
      ASENS_Trend_fK = ASENS_bCount * ASENS_Trend_fValue(bChannel , ASENS_bCount)       ' }
      ASENS_Trend_fNValueSUM = ASENS_Trend_fNValueSUM + ASENS_Trend_fK          ' } ASENS_Trend_fK используется тут как временная переменная
   next
   ASENS_Trend_fValueAV = ASENS_Trend_fValueAV / 10                             ' вычислить ср. арифм.

   ASENS_Trend_fK = ASENS_Trend_Xav * ASENS_Trend_fValueAV                      ' }
   ASENS_Trend_fK = ASENS_Trend_fK * 10                                         ' }
   ASENS_Trend_fK = ASENS_Trend_fNValueSUM - ASENS_Trend_fK                     ' } верхняя часть дроби

   ASENS_Trend_fK = ASENS_Trend_fK / ASENS_Trend_Divider                        ' поделить на делитель

   select case ASENS_Trend_fK                                                   ' определить направление тренда с учетом порога
      case is > ASENS_TREND_TOLERANCE_UP
         ASENS_GetTrend = ASENS_TREND_UP
      case is < ASENS_TREND_TOLERANCE_DN
         ASENS_GetTrend = ASENS_TREND_DN
      case else
         ASENS_GetTrend = ASENS_TREND_NO
   end select
end function

ASENS_METER_done:                                                               ' требуется компилятору BASCOM при использовании оцпии "config submode = old"

Отредактировано Cirno_9 (2019-09-13 10:32:29)