Если в программе для МК нужно использовать встроенный АЦП, то в 99% случаев вы сталкиваетесь с проблемой шумных показаний.
Встроенные в МК АЦП сильно (2-4 значащих бита из типичных 12) шумят из-за расположения на одном кристалле с цифровым ядром. Типичное решение — засыпать, проводить измерение и просыпаться, подходит только для относительно простых программ, где вы можете позволить себе длительный простой ядра.
Соответственно, показания нужно усреднять. Очевидные решения — завести кольцевой буфер, писать показания туда и каждый раз при выводе усреднять. Работает хорошо, только если размер буфера больше хотя бы 64 элементов. Для 12-битного АЦП вам нужно 128 байт памяти, что в мире МК бывает слишком расточительно. Плюс рассчет среднего на восьмибитном МК тоже может занимать слишком много времени, несмотря на отсутствие операций с плавающей точкой.
Пробовал я и добавлять цифровой фильтрации к показаниям, но результат был всегда один — если буфер меньше чем на 100 элементов, то показания будут неприятно скакать.
И однажды ко мне пришла идея: а зачем нам вообще хранить весь буфер, если мы постоянно вычисляем сумму и она является всем смыслом существования буфера? А с каждым новым значением мы изменяем всего один элемент!
Так почему бы нам просто не хранить сумму, добавляя к ней очередной элемент, а вычитая… например, среднее?!
#define ADC_VSYS_BUFFER_SIZE 100 uint32_t adc_vsys_buf = 0; // "Симулятор" буфера, который хранит сумму uint8_t adc_vsys_num = 1; // "Позиция" в буфере uint16_t adc_get_system_voltage(void) { // Функция adc_get_system_voltage_raw() должна вернуть текущее показание АЦП uint16_t current_vsys = adc_get_system_voltage_raw(); uint16_t mean_vsys; adc_vsys_buf += current_vsys; // Прибавляем к сумме текущее показание АЦП mean_vsys = adc_vsys_buf / adc_vsys_num; // И вычисляем среднее if (adc_vsys_num < ADC_VSYS_BUFFER_SIZE) { // Переменная суммы еще не хранит желаемое число "элементов" — // увеличиваем "позицию" adc_vsys_num++; { else { // В переменной суммы уже достаточно "элементов" — // вычитаем среднее adc_vsys_buf -= mean_vsys; } return mean_vsys; }
Итого: 5 байт глобально, еще 4 локально и ноль операций с плавающей точкой. И все это «симулируя» поведение буфера на 100 элементов.
А результат из реального мира — шикарное плавное изменение показаний АЦП без какого либо «дрожжания» значений. Пользуйтесь!