Избегайте шума 1-LSB в показаниях АЦП

У меня есть потенциометр, подключенный к входу АЦП Arduino Leonardo. Разрешение АЦП 10-битное (0..1024), а мне нужно всего 7-битное (0..127).

Я использую простой фильтр, чтобы уменьшить шум, а затем map, чтобы удалить неиспользуемые биты:

#define ADC_CH                18
#define ADC_FILTER            0.90

uint16_t value;

uint16_t adc = analogRead(ADC_CH);
value = (uint16_t) ((float) ADC_FILTER * adc + (1. - ADC_FILTER) * value);

Я показываю value на ЖК-дисплее. Это очень стабильно, но может случиться так, что если показания АЦП находятся рядом с двумя значениями, нет ничего, что могло бы предотвратить изменение конечного значения, даже если шум составляет всего несколько младших разрядов.

Есть ли простой способ реализовать гистерезис для каждого значения с низким разрешением?

Таким образом, изменение между N и N+1 происходит при другом показании АЦП, чем между N и N-1, что улучшает подавление шума без увеличения задержки фильтра (это только увеличило бы интервал изменения).

, 👍1


1 ответ


Лучший ответ:

4

Когда вы уменьшаете разрешение с 10 бит до 7 бит, все возможные выход соответствует интервалу входов. Если добавить немного гистерезиса, то эти интервалы должны перекрываться. Ниже представлено представление входные интервалы, соответствующие выходам 0, 1,… 6, для ширина гистерезиса 4:

input: 0    5   10   15   20   25   30   35   40   45   50   55   60
       └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴
       ├─────0────┤
           ├───────1──────┤
                   ├───────2──────┤
                           ├───────3──────┤
                                   ├───────4──────┤
                                           ├───────5──────┤
                                                   ├───────6──────┤

Чтобы реализовать это в коде, вы должны помнить последний вывод и измените его только в том случае, если вход находится за пределами интервала, назначенного этому выход. Вот пример реализации:

const int hysteresis = 4;  // настраиваем по вкусу

/* Удалить 3 бита разрешения, добавив немного гистерезиса. */
int resample(int adc)
{
    static int value;  // последний вывод
    if (adc < (value<<3) - hysteresis) {
        value = (adc + hysteresis) >> 3;
    } else if (adc >= ((value+1)<<3) + hysteresis) {
        value = (adc - hysteresis) >> 3;
    }
    return value;
}

Обратите внимание, что локальная переменная static сохраняет свое значение между последовательными вызовы функции.

,

Я не должен этого делать, но в любом случае: отличный ответ! Хорошая графика, хорошо объяснено. :), @Nick Gammon

Ваш код работает отлично и точно соответствует примеру в моем вопросе. В любом случае, есть ли более общий подход? Например, что, если я хочу «сопоставить» вход АЦП с [min, max](всегда < 1024)?, @Mark

@Mark: Затем вы замените сдвиги (которые в основном представляют собой оптимизированные умножения и деления) на произвольные карты map(). Затем вы можете провести некоторые тесты, чтобы увидеть, как ошибки округления, вносимые картой, влияют на гистерезис., @Edgar Bonet