1. Прежде всего: просто выбрать большую гармонику в DFT, это плохо для тюнера, поскольку, в зависимости от используемого инструмента, обертоны могут иметь большую амплитуду.Приличный тюнер можно сделать, используя, например, алгоритм автокорреляции.
2. Я вижу эту строку в вашем проекте:
wdogtrig(); // call often else processor will reset ;
Зачем вам нужен сторожевой таймерна первом месте?Где это настроено?На какой тайм-аут он установлен?Как вы думаете, сколько времени потребуется для выполнения обоих вложенных циклов в computeDft()
?С большим количеством операций с плавающей запятой внутри, включая вычисление синуса и косинуса на каждом шаге?На 16-МГц 8-битном MCU?Я думаю, что это займет как минимум несколько секунд, так что вообще не используйте сторожевой таймер или сбрасывайте его чаще.
3. Посмотрите на
cos(n * k * M_PI / N);
(кстати, вы уверены, что это cos(n * k * M_PI / N);
не cos(n * k * 2 * M_PI / N);
?)
, так как cos (x) = cos (x + 2 * M_PI), вы можете видеть, что эта формула может быть выражена как cos((n * k * 2) % (2 * N) * M_PI / N)
.Т.е. вы можете предварительно рассчитать все 2 * N возможных значений и поместить их в виде постоянной таблицы во флэш-память.
4. Посмотрите на вложенные циклы в computeDft()
Внутри внутреннего цикла вы каждый раз вызываете read_adc()
!
Вы хотите выбрать сигнал в буфер один раз, а затем выполнить DFT над сохраненным сигналом.Т.е. сначала вы читаете значения АЦП в массиве x [k]:
for (k = 0; k < N; k++)
{
x[k] = read_adc();
}
и только затем выполняете над ним вычисления DFT:
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
}
5. Смотритеаккуратно в двух циклах:
for (n = 0; n < N; ++n)
..
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
Здесь на каждом шаге вы вычисляете значение X1 [n], ни одно из предыдущих значений X1 не используется.
И еще один цикл ниже:
for (k = 0; k < n; k++)
{
magnitude = sqrt(X1[k].re * X1[k].re + X1[k].im * X1[k].im);
...
}
здесь вы вычисляете величину X1 [k] и никакие предыдущие из следующих значений X1 не используются.Таким образом, вы можете просто объединить их вместе:
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
magnitude = sqrt(X1[n].re * X1[n].re + X1[n].im * X1[n].im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
Здесь вы можете ясно видеть, вам не нужно оснований хранить X1[n].re
и X1[n].im
в любом массиве.Просто избавься от них!
for (n = 0; n < N; ++n)
{
float re = 0;
float im = 0;
for (k = 0; k < n; k++)
{
re += x[k] * cos(n * k * M_PI / N);
im -= x[k] * sin(n * k * M_PI / N);
}
magnitude = sqrt(re * re + im * im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
Вот и все!Вы сэкономили 6 КБ, удалив бессмысленный массив Complex X1[N]
6. В коде инициализации произошла ошибка:
ADMUX = 0b10100111; // set ADC0
Я не знаю, чтоэто «ATmega16P», я предполагаю, что он работает так же, как «ATmega16».Таким образом, наиболее значимые биты этого регистра, называется REFS1
и REFS0
используется для выбора опорного напряжения.Возможные значения:
- 00 - внешнее напряжение с контакта AREF;
- 01 - напряжение AVCC, взятое в качестве эталона
- 11 - внутренний регулятор (2,56 В для ATmega16,1,1 В для ATmega168PA)
10
- неверное значение.
7. Выходной сигнал гитары - это слабый сигнал, возможно, несколько десятков милливольт.Кроме того, это сигнал переменного тока, который может быть как положительным, так и отрицательным.Поэтому, прежде чем подавать сигнал на вход MCU, необходимо сместить его (в противном случае вы увидите только положительную полуволну) и усилить его.
Т.е. недостаточно просто подключить штекер к GND и ADC.вход, вам нужно несколько схем, которые сделают сигнал соответствующего уровня.
Вы можете Google для этого.Например это: (из Этот проект )