
В этом тексте я бы хотел написать про своеобразный простенький фильтр нижних частот. Про гистерезисный фильтр на триггерах Шмитта (ТШ).
Постановка задачи
В микроконтроллерной системе аналого-цифровой преобразователь измеряет напряжение на канале. Это напряжение надо конвертировать в дискретный уровень, в зависимости от значения. Сигнал зашумлен. Надо реализовать программный гистерезис. Напишите функцию, которая выполнит эту работу.
float hysteresis(float input_percent);
На графике это выглядит так

Теория
Триггеры Шмитта можно рассматривать как однобитные ячейки памяти. Получается, что фильтр из четырех триггеров формально может пребывать в (2^4=16) шестнадцати состояниях. В предельных случаях каждый новый семпл переключает каждый триггер.
Когда поступает очередной семпл мы подаем его на вход каждому триггеру: 1-му, 2-му, 3-му, 4-му. Где-то произойдет переключение состояние, а где-то нет. Теоретически регистр фильтра может быть в 16ти состояниях.

Для простоты рассуждений положим, что гистерезис равен нулю. То есть триггеры Шмитта вырождаются в компараторы. Надо сразу отметить, что некоторые состояния фильтра просто невозможны. Например не может быть входной семпл одновременно меньше 12 и больше 87. Вот перед Вами все запретные состояния фильтра.

Фильтр будет работать только в пяти состояниях термокода. Термокод - это способ двоичного кодирования чисел при который просто увеличивается количество непрерывных в ряд единиц.
dec | термокод | hex |
0 | 000000 | 0x0 |
1 | 100000 | 0x1 |
2 | 110000 | 0x3 |
3 | 111000 | 0x7 |
4 | 111100 | 0xF |
5 | 111110 | 0x1F |
Вот рабочий диапазон значений триггеров

В общем таблица состояний получается вот такая.

Реализация
Перед вами псевдокод программной реализации фильтра на триггерах Шмитта. Код пере-использует готовый программный компонент отдельного триггера Шмитта в четырех экземплярах.
static uint8_t SchTrigStateToU8(const SchmittTriggerState_t state) { uint8_t val = 0; switch (state) { case SCHMITT_TRIGGER_STATE_UP: val = 1; break; case SCHMITT_TRIGGER_STATE_DOWN: val = 0; break; default: break; } return val; } static const int32_t StateValLUT[16] = { 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, }; static int32_t hist_filter_state_to_out( HistFilterHandle_t* const Node) { int32_t out_sample = 0; Node->state.tgrigger0 = SchTrigStateToU8(Node->SchmittTrigger[0].state); Node->state.tgrigger1 = SchTrigStateToU8(Node->SchmittTrigger[1].state); Node->state.tgrigger2 = SchTrigStateToU8(Node->SchmittTrigger[2].state); Node->state.tgrigger3 = SchTrigStateToU8(Node->SchmittTrigger[3].state); Node->state.res = 0; out_sample = StateValLUT[Node->state.byte]; return out_sample; } int32_t hist_filter_proc_sample(uint8_t num, const float in_sample) { int32_t out_sample = 0; HistFilterHandle_t *Node = HistFilterGetNode(num); if (Node) { uint32_t i = 0; for (i = 0; i < 4; i++) { schmitt_trigger_proc_val_ll(&Node->SchmittTrigger[i], in_sample); } out_sample = hist_filter_state_to_out(Node); LOG_DEBUG(HIST_FILTER, "HIST_FILTER_%u,in:%f,Out:%d",num ,in_sample,out_sample); } return out_sample; }
LUT реализует вот эту таблицу

Решение №2
На самом деле задачу можно решить проще на основе конечного автомата из пяти состояний. Шаг 1. Перечисляем все состояния. За состояние примем выходной сигнал.

Шаг второй: перечисляем все входные воздействия. Интерес представляют промежутки однозначности (gap) и зоны гистерезиса (hist).

Шаг третий: строим таблицу переходов. Вот в таблицу переходов мы и заложим эффект триггера Шмитта.

Выход автомата это его состояние. По сути получился автомат Мура. Реализация на основе одного LUT + классификация входа x . Вот так просто и не затейливо.
Проверка работы фильтра
Как можно заметить фильтр убирает высокочастотную составляющую входного сигнала.

Достоинства фильтра на ТШ в его простоте реализации и малом количестве операций на один семпл ( в сравнении с FIR фильтрами). Фильтр на триггерах Шмитта стабильный и не склонен к генерации как IIR фильтры.
Итог
Вот такой простой фильтр. Можно и дальше наращивать квантование добавляя количество отдельных триггеров Шмитта. Надеюсь этот алгоритм пригодится кому-нибудь в разработке трактов обработки сигналов.
Сокращение | Расшифровка |
LUT | LookUpTable |
ТШ | триггер Шмитта |
FIR | finite impulse response |
IIR | Infinite impulse response |
Источники
Название | URL |
Медианный фильтр на двух бинарных кучах | |
Синтез Цифрового БИХ Фильтра Низких Частот | |
Триггер Шмитта | |
https://docs.google.com/spreadsheets/d/1dAsB0f5b-lm0g4HAjEvebUA9jxHLjEZ7dJht3QiOLoY/edit?gid=0#gid=0 |
