Короче, мы что-то далеко ушли от темы.
Коты. Не слушайте никого. Если инструмент просит матерно выражаться:
Код: Выделить всё
uint16_t voltage = (uint32_t)(ADC * (uint32_t)(VREF_VALUE + 1)) >> 10;
выражайтесь, если это решает проблему. Все остальное - придирки, попытки запугать указателями и обозвать друг-друга ламерами.
WatchCat
Мною приведенная формула - это формула из даташита (Atmega328P).
24.7 ADC conversion result
Просто я ее преобразовал для вычисления напряжения, поступающего на вход АЦП и привел к такому виду:
U = ADC * (Uref + 1) / 1024 ,где
ADC = значение АЦП после измерения
Uref - величина опорного напряжения, поданного на ногу AREF или внутреннего источника опоры (если выбран внутренний).
Формула справедлива для любых AVR.
Тут всего два действия - умножение и деление.
Умножать таки придется либо аппаратно, если это Atmega, либо программно - если это Tiny. Все это - головная боль компилятора. И только отчасти - программиста, если с производительностью будут проблемы.
А с делением все просто. Число 1024 - это два в десятой степени.
Значит, результат умножения можно поделить на 2^10 логическим сдвигом вправо на 10 разрядов.
Это уже аппаратно на любом AVR. Правда, оба действия не выполнятся за такт, так как числа большие, а разрядность МК маленькая.
И все равно, на ATMega это будет быстрее, чем на ATTiny.
Теперь о десятичном выражении. Не нужен тут тип float совсем. Достаточно числа с фиксированной точкой.
Что это такое? Это просто большое десятичное число, часть младших разрядов которого нужно воспринимать как дробную часть.
В том примере, что я приводил - число 100 соответствует 1,00 вольт. А число 101 - это 1,01 вольт.
VREF_VALUE 500ul - это 5,00 вольт опорного напряжения. Соответственно, АЦП при такой величине опоры, будет измерять от 0 до 5 вольт. А дальше - искусство схемотехники. Если нужно измерять большие величины напряжений, значит строится делитель напряжения на резисторах, который приведет масштаб измеряемых напряжений к масштабу 0...Uref. VREF_VALUE в этом случае можно задать в программе таким, как будто мы подали на AREF самое большое напряжение. Например - VREF_VALUE 1000ul, если нам нужно мерить от 0 до 10 вольт. Всю остальную работу по промежуточным преобразованиям выполнит аналоговый тракт. Главное, чтобы делитель напряжения привел шкалу 0..10 вольт к шкале 0..Uref вольт.
Если нужно мерить меньшие напряжения. Например, 0...75мВ от стандартного токового шунта, то тут тоже все просто.
Формула та же, расчет усилителя на то, чтобы наш диапазон входных напряжений 0...75мВ был приведен к диапазону 0...Uref выходных напряжений. В качестве значения VREF_VALUE задаем номинал шунта. Если он на 50 ампер, значит значение будет равным 5000ul.
Все, теперь у нас амперметр на 0...50А
Так как и делитель и усилитель практически всегда нельзя получить с нужными коэффициентами, тогда в дело вступает калибровка.
Просто определяем реальную шкалу и задаем соответствующее реальности значение VREF_VALUE. Если аналоговый тракт достаточно линеен, то просто подаем на вход прибора заранее известное напряжение и подгоняем VREF_VALUE таким образом, чтобы прибор выдал правильное показание.
Если операционный усилитель, собака такая, дает некое смещение на выходе, то вариантов тут два - либо мы жестко вбиваем его в программу и всегда вычитаем его из результата, либо делаем замер в тот момент, когда уверены, что ничего на вход усилителя не подается, запоминаем и всегда вычитаем из последующих результатов.
Все это простые методы. Если нужны очень высокие точности, то тут уже будет все сложнее.