Что касается скользящего усреднения w / N = 10, chux - Reinstate Monica предоставила решение. Chux - Reinstate Monica также рекомендует взглянуть на целочисленную версию фильтра нижних частот. Мне лично нравится экспоненциально взвешенное скользящее среднее (EWMA), потому что его довольно просто кодировать, и для усреднения требуется всего несколько значений. Это по сравнению с необходимостью держать 10 в массиве в вашем случае. Для этого я бы порекомендовал главу 12 «Программирование AVR Эллиота Уильямса». Если у вас нет легкого доступа к этому, EWMA, как описано в Make AVR, начинается с
y_current = (1/16) * x_current + (15/16) * y_previous
, где в нашем случае y_current - это обновленное значение EWMA, x_current - новейшая выборка из вашей AD C, а y_previous - последнее значение EWMA. Выбор 16 также может быть изменен вместе с весами, 1 и 15. Однако, как вы увидите, важно сохранять степень 2. Как показано в книге Эллиота Уильямса, вы умножаете на 16 и компенсируете проблемы округления и получаете следующее:
16 * y_current = x_current + 16 * y_previous - (16 * y_previous - 8) /16.
Теперь я знаю, что это выглядит некрасиво, но то, что мы имеем, масштабируется на 16 средних значений, которые являются целыми числами и зависят только от сложения целых чисел (16 * y_previous хранится как одно значение, поэтому вы не выполняете умножение) и битовый сдвиг; Вот почему в EWMA была выбрана степень 2, деление на 16 - это то же самое, что сдвиг вправо в 4. Хорошо, так как это среднее значение выглядит в коде:
// Snippet from Make: AVR Programming
uint16_t x_current; // ADC value.
uint16_t y_current; // Average ADC value.
// Get the EWMA.
y_current = x_current + y_current - ((y_current - 8) >> 4);
// Send the value over USART (assuming it's wired up). Remember that
// y_current is scaled by 16.
printf("%d\n",(y_current>>4));
Выше приведен только EWMA, который вы можете использовать в своем коде, и пример его отправки, который является напоминанием о том, что значение масштабируется. Помните, что это только усредненное значение AD C. Вероятно, вы захотите использовать значение AD C в качестве входа для функции, чтобы получить значение некоторой измеренной величины. Вместо того, чтобы фактически использовать функцию и вычислять значения, вы можете создать справочную таблицу, где индекс - это значение AD C, а запись массива с этим индексом - предварительно рассчитанное значение.
С точки зрения вашего другой код, вещи, которые можно исправить / упростить, находятся в ваших ISR. В ISR (TIMER0_OVF_vect) у вас есть некоторые битовые операции, которые являются постоянными и могут быть предварительно рассчитаны, так что вы не будете делать это каждый раз, когда срабатывает ISR (TIMER0_OVF_vect).
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110));
становится
PORTB = ((PORTB & 0b11110001)|(0b00000000)); // Saves a bit inversion and '&'
который показывает, что ваш ORing, |, не влияет на результат, потому что вы ORing против всех нулей.
Наконец, в вашем ISR (ADC_vect) вы используете побитовое, &, а не логическое и, &&. Вы получаете тот же результат, но это все равно, что использовать гаечный ключ для забивания гвоздя. Я знаю, что это много, но я надеюсь, что это поможет, и дайте мне знать, если вам нужны разъяснения.