【问题标题】:My guitar tuner code in CodeVisionAVR using an ATmega164 can't handle more than 4 samples我在 CodeVisionAVR 中使用 ATmega164 的吉他调音器代码不能处理超过 4 个样本
【发布时间】:2019-05-21 20:18:32
【问题描述】:

我正在通过 CodeVisionAVR 设计吉他调音器,并在我的大学项目中使用 ATmega164 微芯片。如果我将样本数设置为高于 4,则芯片 LED 将持续闪烁,并且代码不会超过该过程的引脚读取阶段(在每行代码后点亮 LED 进行测试以查看它停止的位置)。

大学强迫我使用 DFT 通过 CVAVR 设计代码。我已经(在对最近一个问题的回答的帮助下)编写的代码比原来的代码小了近 18 倍。我不知道如何让它更容易运行,以便它可以容忍超过 4 个样本 (#define N 4)

#define M_PI 3.1415926f
#define N 4

unsigned char read_adc(void)
{
ADCSRA |= 0b01000000;  //start conversion;
while (ADCSRA&(0b01000000)); //wait conversion end
return ADCH;
}

float computeDft()
{      
    unsigned char x[N] = {0};
    float max = 0;   
    float maxi = 0;
    float magnitude = 0; 
    int k = 0;
    int n = 0;
    float re = 0;
    float im = 0;          
    for (k = 0; k < N; k++)
    {       
        x[k] = read_adc();            
    }
    for (n = 0; n < N; n++)
    {
        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;   
        }
    }                                     
    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
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef OPTIMIZE_SIZE
#pragma optsize+
#endif

TWCR=0x00;
//adc init
ADMUX = 0b10100111; // set ADC0
ADCSRA = 0b10000111; //set ADEN, precale by 128
while(TRUE)
{
    wdogtrig();         // call often else processor will reset
    F = computeDft();    
    L2 = 1;
    if(F > 20 && F < 100)
    {
        L3 = 1;
    }
}  


}// end main loop 

由于 Nyquist-Shannon 采样定理,一个调音器通常应该能够使用至少 800 个样本,并且高 E 吉他弦大约在 380 Hz 左右(不记得确切)。

/* initialization file */

#include <mega164a.h>
#include "defs.h"


/*
 * most intialization values are generated using Code Wizard and depend on clock value
 */
void Init_initController(void)
{
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTA=0x00;
DDRA=0x00;

// Port B initialization
PORTB=0x00;
DDRB=0x00;

// Port C initialization
PORTC=0x00;
DDRC=0x00;

// Port D initialization
PORTD=0b00100000; // D.5 needs pull-up resistor
DDRD= 0b01010000; // D.6 is LED, D.4 is test output

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0A=0x00;
TCCR0B=0x00;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 19.531 kHz = CLOCK/256
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off

TCCR1A=0x00;
TCCR1B=0x0D;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;

// 1 sec = 19531 counts = 4C41H counts, from 0 to 4C40 
// 4C40H = 4CH (MSB) and 40H (LSB)
OCR1AH=0x4C;
OCR1AL=0x40;

OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-15: Off
// Interrupt on any change on pins PCINT16-23: Off
// Interrupt on any change on pins PCINT24-31: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;

// Timer/Counter 0,1,2 Interrupt(s) initialization
TIMSK0=0x00;
TIMSK1=0x02;
TIMSK2=0x00;

// USART0 initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART0 Receiver: On
// USART0 Transmitter: On
// USART0 Mode: Asynchronous
// USART0 Baud rate: 9600
UCSR0A=0x00;
UCSR0B=0xD8;
UCSR0C=0x06;
UBRR0H=0x00;
UBRR0L=0x81;

// USART1 initialization
// USART1 disabled
UCSR1B=0x00;


// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;
DIDR1=0x00;

// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/2048  
#pragma optsize-
#asm("wdr")
// Write 2 consecutive values to enable watchdog
// this is NOT a mistake !
WDTCSR=0x18;
WDTCSR=0x08;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

}

【问题讨论】:

  • 虽然采样定理规定 每秒 >760 个样本,但 DFT 不需要 >760 个样本 - 也就是说,您可以使用小于一秒的窗口工作数据的。您只需要几个周期。 128 个样本就足够了。
  • 好的,我明白了,但我仍然无法超过 4 个样本而不会导致芯片崩溃并不断循环
  • 顺便说一句:您是否打算在嵌套循环内重置re = 0; im = 0;,在累积其值的内部循环之前?
  • 好吧,我的观点是评论,而不是答案。我已经发布了答案。
  • 高E弦是329.63 Hz BTW。

标签: c avr atmega16 tuner codevisionavr


【解决方案1】:

1.

float re = 0;
float im = 0;  

移动声明,或者不要忘记在嵌套循环之前的循环内将这些值重置为零。

2. Nyquist-Shannon 定理(也称为 Kotelnikov 定理)是关于采样率的。要传输高达 F 的频率,您必须具有至少 2*F 的采样率。这与样本数量无关。如果您的 MCU 以 16MHz 运行,我可以假设您的采样率约为 9kHz = 16MHz / 128(ADC 的预分频器)/14(每个样本的 ADC 时钟周期)

3. Init_initController(); 是什么?您的代码中没有这样的功能。它在做什么?为什么是must be the first "init" action/call!? 大概有问题。看着wdogtrig();,我可以假设某处看门狗定时器已初始化。由于浮点数学需要很长时间,因此看门狗在循环完成之前就过期了。

要么禁用看门狗,要么将wdogtrig(); 放入computeDft(); 的循环中,以便更频繁地重置看门狗定时器。

4. 你可以声明

unsigned char x[N] = {0};

全局,将其移出函数,内存将被分配一次,每次函数调用时不会重新填充堆栈

5. 如果您提出问题,请提供问题中的所有信息:您期望什么、您得到什么、所有自定义函数和不起作用的确切代码。

【讨论】:

  • Init_initController(); 只是微处理器初始化。端口、中断等的声明。在问题中包括另外 1000 行初始化 AVR 代码将太混乱了。它们都是大学作为“测试程序”提供给我们的预制功能,由 CodeWizardAVR 制作(初始化、ADC 读取等)。不起作用的部分在main();函数中,每当我的#define N &lt;samples&gt;大于4。处理器在完成computeDft()函数之前一直在重置。
  • 反正我不知道Init_initController();里面有什么,我只能建议。你仍然没有确认也没有反驳我的建议有一个看门狗定时器被使用。
  • 我在帖子中包含了Init_initController();。我尝试在整个代码中更频繁地使用wdogtrig();,它确实完成了while循环的运行。谢谢!现在我必须弄清楚来自 ADC 的数据是如何传输的,以便我可以选择一个变量F = computeDft(); 来进行比较以点亮调谐器的 LED。
【解决方案2】:

堆栈大小是多少?您的 x[N] 已在堆栈上实例化,并且可能没有足够的空间容纳足够大小的数组。

静态分配它以避免堆栈溢出:

static unsigned char x[N] = {0};

即便如此,ATmega164 总共也只有 1K RAM。您可能能够创建一个 800 个样本缓冲区,但在这种情况下,您的堆栈必须不超过 200 个字节左右。但是,您不需要那个大小的缓冲区;您将样本数与每秒样本数混淆了。 DFT 只需要合理数量的循环即可达到所需的精度。以 Hz 为单位的分辨率将等于 Fs/N,因此 256 个样本缓冲区将产生大约 3Hz 的精度,这可能已经足够准确了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-20
    • 2013-01-22
    • 2011-05-29
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多