首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我正在通过ATmega16p和CodeVisionAVR设计一个吉他调谐器,我只是无法运行我的代码

我正在通过ATmega16p和CodeVisionAVR设计一个吉他调谐器,我只是无法运行我的代码
EN

Stack Overflow用户
提问于 2019-05-20 20:44:42
回答 1查看 125关注 0票数 0

我正在设计一个吉他调谐器通过爱特梅尔mega16处理器和CodeVisionAVR为我的大学的第二个项目。我已经连接了一个单插孔到处理器的PINA.7 (ADC转换器)和GND。我有7个发光二极管(PORTB.0.6),根据信号的基波频率,它应该通过一系列的if/elseif打开。

我正在通过一个DFT (我知道有更快的FTs,但我们的学校告诉我们应该使用DFT,他们知道为什么)800个样本的基本信号。在选取的800个样本中,计算频谱。然后,下一个for被用来计算每个频率的绝对值,并选择最大的,这样它就可以成为吉他调谐器的一个很好的参考点。

重要的是,我的主要功能只是一个大频率的条件,看看LED是否亮了,但它没有。

在整个代码中,我尝试将let从0切换到6,它似乎停止在F = computeDft();,所以我删除了变量,只让computeDft();运行,但是下一个let没有亮起来。这个函数永远不会被调用吗?我在Visual中用生成的余弦函数尝试了这个函数,它工作得很好。它总是能检测到基本面。为什么它在CVAVR中不起作用?

代码语言:javascript
复制
#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 

我试图达到的结果是,一个来自手机或电脑的信号(可能是一个人正在调吉他的YouTube视频)通过插孔发送到AD转换器(PINA.7)的处理器。主函数调用computeDft;函数,它将要求read_adc();将通过电缆发送的电压的值添加到xk,然后计算它的Dft值。相同的函数然后选择基频(绝对值最高的一个),然后返回它。在主函数内部,一个变量将被赋予基本的值,通过一系列的ifs,它将它的值与标准吉他弦频率82.6,110等进行比较。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-05-21 11:59:03

1.首先:仅仅选择DFT中较大的谐波,不是很好的调谐器,因为根据乐器的不同,泛音可能有较大的振幅。好的调谐器可以使用例如自相关算法来完成。

2.我在您的项目中看到了这一行:

代码语言:javascript
复制
 wdogtrig();         // call often else processor will reset ; 

你为什么一开始就需要看门狗?在哪里配置的?设置的超时时间是什么?您认为,在computeDft()中执行两个嵌套循环需要多长时间?每一步都有很多浮点运算,包括正弦和余弦的计算?在16 MCU 8位微控制器上?我认为这至少需要几秒钟,所以根本不要使用看门狗,或者更频繁地重置它。

3.查看

代码语言:javascript
复制
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。也就是说,首先将ADC值读入xk数组:

代码语言:javascript
复制
for (k = 0; k < N; k++)
{       
    x[k] = read_adc();            
}

只有这样,您才能对其进行DFT计算:

代码语言:javascript
复制
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.仔细研究了两个周期:

代码语言:javascript
复制
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);
}

在这里的每个步骤中,您都在计算X1n的值,没有使用以前的X1值。

另一个循环如下:

代码语言:javascript
复制
for (k = 0; k < n; k++)  
{
    magnitude = sqrt(X1[k].re * X1[k].re +  X1[k].im * X1[k].im);
    ...
}

在这里,您正在计算X1k的大小,并且没有使用以前的X1值。因此,您可以简单地将它们组合在一起:

代码语言:javascript
复制
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].reX1[n].im。快把他们赶走!

代码语言:javascript
复制
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.初始化代码中有一个错误:

代码语言:javascript
复制
ADMUX = 0b10100111; // set ADC0

我不知道什么是"ATmega16P",我猜想它和"ATmega16“一样工作。因此,这个寄存器中最重要的位,称为REFS1REFS0,用于选择基准电压。可能的价值是:

  • 00 -来自AREF引脚的外部电压;
  • 01 - AVCC电压作为参考
  • 11 -内部调节器( ATmega16为2.56V,ATmega168PA为1.1V )

10是一个不正确的值。

7.吉他输出是一个小信号,可能几十毫伏。同时,它也是一个交流信号,可以是正的,也可以是负的。因此,在将信号输入到MCU的输入之前,您必须将其移除(否则您只会看到正半波)并放大它。

也就是说,仅仅将插孔插头连接到GND和ADC输入是不够的,您需要一些原理图才能使信号达到适当的电平。

你可以在谷歌上搜索。例如:

(来自这个项目)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56227939

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档