0

I'm designing a guitar tuner through CodeVisionAVR and using an ATmega164 microchip for my university project. If i set the number of samples any higher than 4, the chip LEDs will just flash continuously and the code won't get past the pin reading phase of the process (tested with lighting up LEDs after each line of code to see where it stops).

I'm forced by the university to design the code through CVAVR, using a DFT. I have made (with the great help of an answer to a recent question) the code almost 18 times smaller than it was originally. I don't know how i can make it any easier to run so that it can tolerate more than 4 samples (#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 

A tuner in general should be able to use at least 800 samples because of the Nyquist–Shannon sampling theorem, and the high E guitar string being somewhere around 380 Hz (can't remember exactly).

/* 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

}
  • While the sampling theorem dictates >760 samples _per second_, the DFT however does not require >760 samples - that is you can work with a windows much less that one seconds worth of data. You need only a few _cycles_. 128 samples would be more that adequate. – Clifford May 21 '19 at 21:22
  • Ok, i understand, but i still can't get past 4 samples without the chip crashing and looping continuously – Comaroni Andrei May 21 '19 at 21:25
  • Just an aside: did you intend to reset `re = 0; im = 0;` inside the nested loop, before the inner loop which accumulates their value? – Weather Vane May 21 '19 at 21:26
  • Well my point was a comment, not an answer. I have posted an answer. – Clifford May 21 '19 at 21:46
  • The high E string is 329.63 Hz BTW. – Clifford May 21 '19 at 21:55
  • The stack size is 1016 bytes. I'm using 56 bytes (down from 9068). I've tried 128 samples and allocating x[N] statically without succeding. It still loops unto itself without finishing the code. – Comaroni Andrei May 21 '19 at 22:08
  • Use a debugger to see what is happening. The part you say you are using has only 1K RAM, so I am not sure how you managed to use 9068 bytes! If you use a statically allocated buffer, your stack size will necessarily need to reduce. What is the 56 bytes? How have you determined that usage? – Clifford May 21 '19 at 22:23
  • When i first started out the project i used a very bad brute force implementation of DFT, coupled with a Complex structure that grouped real and imaginary parts of a number into a single variable. That was a LOT of space. The final version that uses just 56 bytes is exactly what you can see in the code i've linked in the question. Nothing more – Comaroni Andrei May 21 '19 at 22:25
  • I also can't use CVAVR's debugger on the freeware program, what i've did is i've turned on a Led (```L0 = 1;``` for example) for each line of code, and if it worked, i'd move to the next one and so on. With 4 samples, the LED lights up right at the end of the ```main()``` function. With 128 samples, the ```read_adc()``` function is the one that fails first, and no LEDs light up. If i remove the function ```read_adc()```, the DFT calculation of ```re``` an ```im``` also produce a loop and no LEDs light up. It simply acts like it's too much to handle. – Comaroni Andrei May 21 '19 at 22:27

2 Answers2

1

1.

float re = 0;
float im = 0;  

move the declaration, or do not forget to reset those values to zero inside the loop before the nested loop.

2. Nyquist–Shannon theorem (also known as Kotelnikov's theorem) says about sampling rate. To transfer frequencies up to F you have to have at least 2*F sampling rate. It is nothing to do with number of samples. If your MCU is running at 16MHz, i can assume you have the sampling rate about 9kHz = 16MHz / 128 (prescaler of the ADC) / 14 (ADC clock cycles per one sample)

3. What is Init_initController(); ? There is no such function in your code. What it is doing? Why it must be the first "init" action/call!? Probably there is an issue. Looking at wdogtrig(); I can assume somewhere watchdog timer is initialized. Since float math takes too much long time, The watchdog expires before the loop finished.

Either disable the watchdog, or put wdogtrig(); inside the loop in computeDft();, for watchdog timer reset more often.

4. You can make the declaration

unsigned char x[N] = {0};

global, moving it outside the function, the memory will be allocated once and do not refill the stack on each function call

5. Please, if you asking a question, provide all the information inside the question: what you were expecting, what you got, all custom functions and exact code which does not work.

AterLux
  • 4,566
  • 2
  • 10
  • 13
  • ```Init_initController();``` is just the microprocessor initialization. The declaration of ports, interrupts, etc. etc. Including another 1000 lines of intialization AVR code in the question would have just been too messy. They are all premade functions given to us by the university as a "test program", made with the CodeWizardAVR (the intialization, the ADC read, everything). The part that doesn't work is in the ```main();``` function, whenever my ```#define N ``` is larger than 4. The processor keeps resetting before finishing the ```computeDft()``` function. – Comaroni Andrei May 22 '19 at 10:48
  • Anyway, since I don't know what inside `Init_initController();` I can only suggest. Still you did not confirm and did not refute my suggestion there is a watchdog timer is used. – AterLux May 22 '19 at 11:25
  • I have included ```Init_initController();``` in the post. I have tried to use```wdogtrig();``` more often throughout the code and it did indeed finish running the while loop. Thank you! Now i have to work out how is the data from the ADC transmitted so that i can select a variable ```F = computeDft();``` that will be compared to light up the LEDs of a tuner. – Comaroni Andrei May 22 '19 at 11:46
0

What is the stack size? Your x[N] is instantiated on the stack and there is probably insufficient space for an array of sufficient size.

Allocate it statically to avoid the stack-overflow:

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

Even then the ATmega164 has only 1K RAM in total. You may be able to create an 800 sample buffer, but your stack will have to be no more than around 200 bytes in that case. However you don't need a buffer that size; you are confusing number of samples with number of samples per second. What you need for the DFT is only a reasonable number of cycles to achieve your required precision. The resolution in Hz will be equal to Fs/N, so a 256 sample buffer will yield a precision of around 3Hz which may be accurate enough.

Clifford
  • 88,407
  • 13
  • 85
  • 165