0

I am looking for some guidance for my code.

#include <mega164a.h>

// Declare your global variables here
#define BUTTON1 PINC7
#define Freq_e 0xEF
#define Freq_B 0x9F
#define Freq_G 0xDE
#define Freq_D 0xBD
#define Freq_A 0xF7
#define Freq_E 0xCF

// Standard Input/Output functions
#include <stdio.h>
#include <delay.h>
#include <stdbool.h>
void USARTInit()
{
    // 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;

    #asm("sei")
}
 bool button_state()
{
  if (!((PINC & (1 << BUTTON1)) >> BUTTON1))
  {
    delay_ms(30);
   if (!((PINC & (1 << BUTTON1)) >> BUTTON1))
     return true;
  }
  return false;
}
  unsigned char SelectedFreq=0;
/*unsigned char readanalog()
{
    unsigned short val=0;
    while(ADCSRA&(1<<ADSC));
    val=ADCL;
    val+=(ADCH<<8);
    return ADCL;
}
*/
float val1=0.0;
float desirefreqe=329.63;
float desirefreqB=246.94;
float desirefreqG=196;
float desirefreqD=146.83;
float desirefreqA=110;
float desirefreqEs=82.41;
void compfreq(unsigned char SelectedFreq)
{
    float tempfreq=0;
  if(SelectedFreq==0){
     tempfreq=desirefreqe;
  }
  else if(SelectedFreq==1){
     tempfreq=desirefreqB;
  }
   else if(SelectedFreq==2){
     tempfreq=desirefreqG;
  }
   else if(SelectedFreq==3){
     tempfreq=desirefreqD;
  }
   else if(SelectedFreq==4){
     tempfreq=desirefreqA;
  }
   else if(SelectedFreq==5){
     tempfreq=desirefreqEs;
  }
  if(val1>(tempfreq+2)){
   PORTB=0x9c;
  }
  else if(val1<(tempfreq-2)){
   PORTB=0xE2;
  }
  else{
  PORTB=0x81;
  }
}
void closefreq(){
    if(val1>=(desirefreqe-((desirefreqe-desirefreqB)/2))){
       PORTD=Freq_e;
    }
    else if(val1>=(desirefreqB-((desirefreqB-desirefreqG)/2))){
        PORTD=Freq_B;
    }
    else if(val1>=(desirefreqG-((desirefreqG-desirefreqD)/2))){
        PORTD=Freq_G;
    }
     else if(val1>=(desirefreqD-((desirefreqD-desirefreqA)/2))){
        PORTD=Freq_D;
    }
     else if(val1>(desirefreqA-((desirefreqA-desirefreqEs)/2))){
        PORTD=Freq_A;
    }
     else {
        PORTD=Freq_E;
    }
}
/*unsigned char read_adc(unsigned char adc_input) {
    ADMUX = adc_input | ADC_VREF_TYPE;
    // Delay needed for the stabilization of the ADC input voltage
    delay_us(10);
    // Start the AD conversion
    ADCSRA |= (1 << ADSC);
    // Wait for the AD conversion to complete
    while ((ADCSRA & (1 << ADIF)) == 0);

    ADCSRA |= (1 << ADIF);
    return ADCH;
}
*/
unsigned char read_adc1(void)
{
    ADCSRA |= 0b01000000;  //start conversion;
    while (ADCSRA&(0b01000000)); //wait conversion end
    return ADCH;
}
void main(void)
{
// Declare your local variables here

// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=(1<<CLKPCE);
CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Input/Output Ports initialization
// Port A initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
PRR &= ~(1 << PRADC);
//ADMUX=ADC_VREF_TYPE;
ADMUX = 0b10100111; // set ADC0
ADCSRA = 0b10000111; //set ADEN, precale by 128
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);

// Port B initialization
// Function: Bit7=In Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRB=(0<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0);
// State: Bit7=T Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=0
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// Port C initialization
// Function: Bit7=In Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRC=(0<<DDC7) | (1<<DDC6) | (1<<DDC5) | (1<<DDC4) | (1<<DDC3) | (1<<DDC2) | (1<<DDC1) | (1<<DDC0);
// State: Bit7=T Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=0
PORTC=(1<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

// Port D initialization
// Function: Bit7=In Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRD=(0<<DDD7) | (1<<DDD6) | (1<<DDD5) | (1<<DDD4) | (1<<DDD3) | (1<<DDD2) | (1<<DDD1) | (1<<DDD0);
// State: Bit7=T Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=0
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=0xFF
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=0xFFFF
// OC1A output: Disconnected
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
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=(0<<EXCLK) | (0<<AS2);
TCCR2A=(0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0) | (0<<WGM21) | (0<<WGM20);
TCCR2B=(0<<WGM22) | (0<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;

// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);

// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=(0<<ICIE1) | (0<<OCIE1B) | (0<<OCIE1A) | (0<<TOIE1);

// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=(0<<OCIE2B) | (0<<OCIE2A) | (0<<TOIE2);


// 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=(0<<ISC21) | (0<<ISC20) | (0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
EIMSK=(0<<INT2) | (0<<INT1) | (0<<INT0);
PCICR=(0<<PCIE3) | (0<<PCIE2) | (0<<PCIE1) | (0<<PCIE0);


// USART1 initialization
// USART1 disabled
UCSR1B=(0<<RXCIE1) | (0<<TXCIE1) | (0<<UDRIE1) | (0<<RXEN1) | (0<<TXEN1) | (0<<UCSZ12) | (0<<RXB81) | (0<<TXB81);

// Analog Comparator initialization
// Analog Comparator: Off
// The Analog Comparator's positive input is
// connected to the AIN0 pin
// The Analog Comparator's negative input is
// connected to the AIN1 pin
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
ADCSRB=(0<<ACME);
// Digital input buffer on AIN0: On
// Digital input buffer on AIN1: On
DIDR1=(0<<AIN0D) | (0<<AIN1D);

// ADC initialization
// ADC disabled
ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);

// SPI initialization
// SPI disabled
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);

// TWI initialization
// TWI disabled
TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);
USARTInit();


while (1)
      {
     // readanalog();
      val1 = read_adc1();
      if( button_state())
      {
        SelectedFreq++;
        SelectedFreq=SelectedFreq%6;
        delay_ms(60);
      }
      if(SelectedFreq==0)
      {
        PORTC=Freq_e;
      }
      else if (SelectedFreq==1)
          {
            PORTC=Freq_B;
          }
      else if (SelectedFreq==2)
          {
            PORTC=Freq_G;
          }
      else if (SelectedFreq==3)
          {
            PORTC=Freq_D;
          }
      else if (SelectedFreq==4)
          {
            PORTC=Freq_A;
          }
       else if (SelectedFreq==5)
          {
            PORTC=Freq_E;
          }
       compfreq(SelectedFreq);
       closefreq();
      }
}

All i need is a function that can read a frequency from the PINA1 of the atmega164 and thats all but i don't have a clue how to build it. All the help is much appreciated. Before posting here i`ve tried for more than two weeks to find the answer to make my code work but i am not been able to do it. The code fail at reading the value from the PINA1 which is a frequency

The code can now compare a value that is meant to be an input frequency and compare it to 6 frequencies for 6 different node (e, B, G, D, A, E). What the program fails to do is to read the input frequency from the PINA1.

RotterPol
  • 5
  • 4
  • 1
    *i don't have a clue how to build it* - What does it mean? You have posted a whole wall of code. What does it do? What does it fail to do? – Eugene Sh. Jan 28 '21 at 18:56
  • @EugeneSh. i can't make a function that read the value sent to PINA1 to save it into a variable to compare it – RotterPol Jan 28 '21 at 19:01
  • The voltage on PINA1 _is not_ the frequency. Your code appears to be written as if it is. You also assign it to a float (you don't need floating point for this in any case), while the ADC read function is unsigned char. – Clifford Jan 28 '21 at 19:24
  • @Clifford how do i transform the input from PINA1 into the frequency thats what bothers me and i dont know how to do it – RotterPol Jan 28 '21 at 19:27
  • The [Fast Fourier Transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) is the algorithm that you are looking for. The implementation is up to you. – Joe Jan 28 '21 at 19:33
  • @JoePerri Thats all i need ty – RotterPol Jan 28 '21 at 19:39
  • @RotterPol The signal waveform is a voltage changing over time. The ADC reads the _instantaneous_ voltage, you have to repeatedly sample to see that change over time. To do signal processing you need to read at a constant rate that is greater than twice the maximum frequency component in the signal. It seems that you are missing some digital signal processing fundamentals which you ideally need to tackle this. – Clifford Jan 28 '21 at 19:59
  • Note that if you are running your AVR on the internal RC oscillator, even calibrated it will not be particularly accurate, and uncalibrated it will be an out-of tune guitar! Even with an external crystal or oscillator, trying to tune a guitar to 2 decimal placed of Hz is optimistic, it will likely change by more than 1Hz from the heat of your fingers playing it. Using floating point is not sensible - it will make for larger, slower code and will in the worst case affect the critical timing of your algorithm. – Clifford Jan 28 '21 at 21:00

1 Answers1

0

A simple (crude, but might just work) solution is to count zero-crossings over time. I am not sure what timer support the CodeVision library provides, but I'll assume you have a 1 millisecond resolution tick count and a 1 millisecond resolution delay. If not you'll have to provide your own using a timer peripheral.

int measureFrequency( int dc_offset )
{
    int zero_x_count = 0 ;
    
    delayms(1) ;  // align to the tick boundary
    int start = tickms() ;
    int previous_sign  = read_adc1() = dc_offset < 0 ? -1 : 1 ;
    
    // Count zero x for 1 second
    while( tickms() - start < 1000 )
    {
        delayms(1) ;
        int sign  = read_adc1() = dc_offset < 0 ? -1 : 1 ;

        if( sign != previous_sign )
        {
            zero_x_count++ ;
        }
    }
    
    // Frequency = zero-x / 2
    return zero_x_count >> 1 ;
}

The dc_offset argument is the quiescent ADC reading corresponding to no signal (silence). You could measure that separately, by taking an average over time while no string is plucked. A more sophisticated method is to use high pass IIR filter with a very low cut-off frequency (a DC-blocking filter) to remove the offset from the signal in real-time.

The 1ms delay between readings is hopefully sufficient to prevent false counts due to higher-frequency harmonics and noise in the reading but you will have to experiment. The waveform for a plucked string is complex and may defeat this simple approach. Ideally you's have an analogue anti-aliasing filter on the input with a cut-off frequency of about 500Hz; that will make it more reliable by removing these higher-frequency components.

A more sophisticated digital signal processing (DSP) approaches include:

  • Fast Fourier Transform (FFT) and find the peak frequency.
  • create a digital band-pass filter for each string frequency and measure the response at each frequency.

The advantage of this second approach is you can in fact tune all strings simultaneously just by strumming an open chord. In this case though you are not measuring the frequency, but the signal level at that frequency. You need a very narrow band and good rejection and frankly the ATMega is probably not up to it.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • I tried ti apply this to my code, but the tickms is not something i can find in codevision also the codevision doesn't wantto open the time.h library – RotterPol Jan 28 '21 at 20:01
  • I read on the CodeVision website: _"Besides the standard C libraries, the CodeVisionAVR compiler features a rich set of libraries [...]"_, but clearly not! As I said write your own or find a library. – Clifford Jan 28 '21 at 20:14
  • i will try to make the timers myself right now, instead of the tick and clocks i will put normal ints and try to figure it – RotterPol Jan 28 '21 at 20:19
  • @RotterPol `tickms()` is a made-up name; your toolchain's library is unlikely to use the same name. There is a `millis()` function example at https://www.avrfreaks.net/comment/679787#comment-679787 the delay function can be written using the `millis()` function at that link. – Clifford Jan 28 '21 at 20:23
  • @RotterPol Note that the reference to `CLOCKS_PER_SEC` was a hangover from my an original draft using `clock()` - time.h is not needed for this solution. – Clifford Jan 28 '21 at 20:25
  • as i see online they actually make the clock themselves so i will try to see what i can modify in order for it to run without errors – RotterPol Jan 28 '21 at 20:29
  • I have found a CodeVisionAVR manual (at Cornell University, not the vendor's site). It is from 2007, but the library has `delay_ms()` and `delay_us()` functions. No tick counter though - read the manual though, they require some initialisation - personally I would not bother, it is trivial to create tick counter and implement the delay by polling the counter. – Clifford Jan 28 '21 at 20:36
  • i will try to read it and make more documentation until i somehow make the program work. Ty for your time and advices. @Clifford – RotterPol Jan 28 '21 at 20:41
  • @RotterPol For any signal processing you are going to need precise timing, so it is as well to get that working first. – Clifford Jan 28 '21 at 20:45
  • I integrated this function into my code and got more documentation and now is working smoothly. Thank you very much for your help and time. – RotterPol Jan 30 '21 at 02:40