我正在通过 ATmega16p 和 CodeVisionAVR 设计一个吉他调谐器,但我无法将我的代码设为 运行
I'm designing a guitar tuner through ATmega16p and CodeVisionAVR and i just can't get my code to run
我正在通过 atmel mega16 处理器和 CodeVisionAVR 为我大学的第二个项目设计吉他调谐器。我已将单声道插孔连接到处理器的 PINA.7(ADC 转换器)和 GND。我有 7 个 LED (PORTB.0..6),它们应该根据信号基频的频率通过一系列 if/elseif 打开。
我正在通过 800 个样本的 DFT(我知道有更快的 FT,但我们的大学告诉我们应该使用 DFT,他们知道为什么)获取信号的基础。在选择的 800 个样本中,它计算频谱。然后下一个for是用来计算每个频率的绝对值,取最大的,所以可以作为吉他调音师的一个很好的参考。
暂时,我在主函数中加入了一个大频率条件来查看 LED 是否亮起,但它没有。
我尝试在整个代码中将 LED 从 0 切换到 6,它似乎停在 F = computeDft();
,所以我删除了变量,只让 computeDft();
运行 , 但下一个 LED 没有亮起。该函数永远不会被调用吗?我已经用生成的余弦函数尝试了 Visual Studio 中的函数,它运行良好。它始终检测基波。为什么它在 CVAVR 中不起作用?
#define M_PI 3.1415926f
#define N 800
unsigned char read_adc(void)
{
ADCSRA |= 0x40; //start conversion;
while (ADCSRA&(0x40)); //wait conversion end
return (float)ADCH;
}
typedef struct
{
float re;
float im;
} Complex;
float computeDft()
{
unsigned char x[N] = {0};
float max = 0;
float maxi = 0;
float magnitude = 0;
Complex X1[N] = {0};
int n = N;
int k;
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
x[k] = read_adc();
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
}
for (k = 0; k < n; k++)
{
magnitude = sqrt(X1[k].re * X1[k].re + X1[k].im * X1[k].im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
return max;
}
/*
* main function of program
*/
void main (void)
{
float F = 0;
Init_initController(); // this must be the first "init" action/call!
#asm("sei") // enable interrupts
LED1 = 1; // initial state, will be changed by timer 1
L0 = 0;
L1 = 0;
L2 = 0;
L3 = 0;
L4 = 0;
L5 = 0;
L6 = 0;
ADMUX = 0b10100111; // set ADC0
ADCSRA = 0b10000111; //set ADEN, precale by 128
while(TRUE)
{
wdogtrig(); // call often else processor will reset ;
F = computeDft();
if (F > 50 && F < 200)
{
L3 = 1;
}
}
}// end main loop
我想要达到的结果是来自 phone 或计算机的信号(可能是一个人调吉他的 YouTube 视频)通过插孔发送到 AD 转换器中的处理器(PINA.7)。主函数调用 computeDft;
函数,它会要求 read_adc();
将通过电缆发送的电压值加到 x[k],然后计算它的 Dft。然后相同的函数选择基频(绝对值最高的那个),然后 returns 它。在 main 函数中,一个变量将被赋予基本值,并通过一系列的 ifs,它将它的值与标准吉他弦频率 82.6、110 等进行比较...
1. 首先:在 DFT 中只选取较大的谐波作为调音器并不好,因为根据所演奏的乐器,泛音可能具有较大的振幅.体面的调谐器可以通过使用例如来完成。 auto-correlation算法。
2. 我在你的项目中看到这一行:
wdogtrig(); // call often else processor will reset ;
为什么首先需要看门狗?它在哪里配置?它设置了什么超时?你怎么想,在 computeDft()
中执行两个嵌套循环需要多长时间?里面有很多浮点运算,包括每一步计算正弦和余弦?在 16MHz 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)
。 IE。您可以预先计算所有 2*N 个可能的值,并将它们作为常量 table 放入闪存中。
4. 查看 computeDft()
中的嵌套循环
在内部循环中,您每次都在调用 read_adc()
!
你想把信号挑一次存入缓冲区,然后对保存的信号进行DFT。 IE。首先将 ADC 值读入 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] 的值,none 将使用之前的 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;
}
}
就是这样!您通过删除无意义的 Complex X1[N]
数组
节省了 6 KB
6.你的初始化代码有错误:
ADMUX = 0b10100111; // set ADC0
我不知道什么是 "ATmega16P",我认为它的工作原理与 "ATmega16" 相同。所以这个寄存器的最高有效位,称为 REFS1
和 REFS0
用于 select 参考电压。可能的值是:
- 00 - 来自 AREF 引脚的外部电压;
- 01 - AVCC 电压作为参考
- 11 - 内部稳压器(ATmega16 为 2.56V,ATmega168PA 为 1.1V)
10
是一个不正确的值。
7.吉他输出的是小信号,可能有几十毫伏。此外,它是一个交流信号,可以是正的,也可以是负的。因此,在将信号放入 MCU 的输入之前,您必须对其进行移位(否则您只会看到正半波)并放大它。
即仅仅将插头连接到 GND 和 ADC 输入是不够的,您需要一些原理图来使信号具有适当的电平。
你可以google。例如:
(来自 This project)
我正在通过 atmel mega16 处理器和 CodeVisionAVR 为我大学的第二个项目设计吉他调谐器。我已将单声道插孔连接到处理器的 PINA.7(ADC 转换器)和 GND。我有 7 个 LED (PORTB.0..6),它们应该根据信号基频的频率通过一系列 if/elseif 打开。
我正在通过 800 个样本的 DFT(我知道有更快的 FT,但我们的大学告诉我们应该使用 DFT,他们知道为什么)获取信号的基础。在选择的 800 个样本中,它计算频谱。然后下一个for是用来计算每个频率的绝对值,取最大的,所以可以作为吉他调音师的一个很好的参考。
暂时,我在主函数中加入了一个大频率条件来查看 LED 是否亮起,但它没有。
我尝试在整个代码中将 LED 从 0 切换到 6,它似乎停在 F = computeDft();
,所以我删除了变量,只让 computeDft();
运行 , 但下一个 LED 没有亮起。该函数永远不会被调用吗?我已经用生成的余弦函数尝试了 Visual Studio 中的函数,它运行良好。它始终检测基波。为什么它在 CVAVR 中不起作用?
#define M_PI 3.1415926f
#define N 800
unsigned char read_adc(void)
{
ADCSRA |= 0x40; //start conversion;
while (ADCSRA&(0x40)); //wait conversion end
return (float)ADCH;
}
typedef struct
{
float re;
float im;
} Complex;
float computeDft()
{
unsigned char x[N] = {0};
float max = 0;
float maxi = 0;
float magnitude = 0;
Complex X1[N] = {0};
int n = N;
int k;
for (n = 0; n < N; ++n)
{
for (k = 0; k < n; k++)
{
x[k] = read_adc();
X1[n].re += x[k] * cos(n * k * M_PI / N);
X1[n].im -= x[k] * sin(n * k * M_PI / N);
}
}
for (k = 0; k < n; k++)
{
magnitude = sqrt(X1[k].re * X1[k].re + X1[k].im * X1[k].im);
if (magnitude > maxi)
{
maxi = magnitude;
max = k;
}
}
return max;
}
/*
* main function of program
*/
void main (void)
{
float F = 0;
Init_initController(); // this must be the first "init" action/call!
#asm("sei") // enable interrupts
LED1 = 1; // initial state, will be changed by timer 1
L0 = 0;
L1 = 0;
L2 = 0;
L3 = 0;
L4 = 0;
L5 = 0;
L6 = 0;
ADMUX = 0b10100111; // set ADC0
ADCSRA = 0b10000111; //set ADEN, precale by 128
while(TRUE)
{
wdogtrig(); // call often else processor will reset ;
F = computeDft();
if (F > 50 && F < 200)
{
L3 = 1;
}
}
}// end main loop
我想要达到的结果是来自 phone 或计算机的信号(可能是一个人调吉他的 YouTube 视频)通过插孔发送到 AD 转换器中的处理器(PINA.7)。主函数调用 computeDft;
函数,它会要求 read_adc();
将通过电缆发送的电压值加到 x[k],然后计算它的 Dft。然后相同的函数选择基频(绝对值最高的那个),然后 returns 它。在 main 函数中,一个变量将被赋予基本值,并通过一系列的 ifs,它将它的值与标准吉他弦频率 82.6、110 等进行比较...
1. 首先:在 DFT 中只选取较大的谐波作为调音器并不好,因为根据所演奏的乐器,泛音可能具有较大的振幅.体面的调谐器可以通过使用例如来完成。 auto-correlation算法。
2. 我在你的项目中看到这一行:
wdogtrig(); // call often else processor will reset ;
为什么首先需要看门狗?它在哪里配置?它设置了什么超时?你怎么想,在 computeDft()
中执行两个嵌套循环需要多长时间?里面有很多浮点运算,包括每一步计算正弦和余弦?在 16MHz 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)
。 IE。您可以预先计算所有 2*N 个可能的值,并将它们作为常量 table 放入闪存中。
4. 查看 computeDft()
在内部循环中,您每次都在调用 read_adc()
!
你想把信号挑一次存入缓冲区,然后对保存的信号进行DFT。 IE。首先将 ADC 值读入 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] 的值,none 将使用之前的 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;
}
}
就是这样!您通过删除无意义的 Complex X1[N]
数组
6.你的初始化代码有错误:
ADMUX = 0b10100111; // set ADC0
我不知道什么是 "ATmega16P",我认为它的工作原理与 "ATmega16" 相同。所以这个寄存器的最高有效位,称为 REFS1
和 REFS0
用于 select 参考电压。可能的值是:
- 00 - 来自 AREF 引脚的外部电压;
- 01 - AVCC 电压作为参考
- 11 - 内部稳压器(ATmega16 为 2.56V,ATmega168PA 为 1.1V)
10
是一个不正确的值。
7.吉他输出的是小信号,可能有几十毫伏。此外,它是一个交流信号,可以是正的,也可以是负的。因此,在将信号放入 MCU 的输入之前,您必须对其进行移位(否则您只会看到正半波)并放大它。
即仅仅将插头连接到 GND 和 ADC 输入是不够的,您需要一些原理图来使信号具有适当的电平。
你可以google。例如: