В предыдущем посте Модуль рН-метра; подходит для любых линейных аналоговых датчиков я описала модуль для работы с датчиком кислотности среды (рН).
Однако уже там было сказано, что принципы, заложенные в код модуля, подходят для работы с любыми линейными аналоговыми датчиками.
Поэтому логичным развитием темы стал универсальный сервис для работы сразу с несколькими датчиками.
Достаточно откалибровать датчик по двум точкам с известными условиями, и сервис будет автоматически вычислять измеряемую величину.
Код модуля:
' ********************************************************************************************
' * *
' * 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)