Elektronik / Elektronik Kaynakları/

Teoriden Gerçeğe: Goertzel Algoritması (Vol. II)

Daha önceki yazımda teorisinden bahsedip, bilgisayar ortamında örneklerini yaptığım Goertzel Algoritmasını bugün en sonunda gerçeğe dökebildim.

Uygulamada temel bileşen ile (50Hz) özellikle elektrik ve güç elektroniği ile ilgilenenlerin başını sıkça ağrıtan ilk 10 harmoniği (150-250-350-450-550-650-750-850-950-1050) algılamaya çalıştım. Yaptığım uygulama ayrıca PIC24F serileri ile örnek alımını ve bu serilerin ADC ve Timer birimlerini öğrenmek isteyenlere güzel bir örnek teşkil edeceğini düşünüyorum.

Yaptığım uygulamaya ait blok diyagram aşağıda görülebilir.

GOERTZEL

Yukarıdaki blok diyagramın görevini yerine getiren kodu aşağıda görebilirsiniz.

/*
 * Yazar: Fırat Deveci (FxDev)
 * Konu : Goertzel Algoritması
 */

#include "p24FJ64GA002.h"
#include "math.h"

_CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & BKBUG_OFF & COE_OFF & ICS_PGx1 & FWDTEN_OFF & WINDIS_OFF);
_CONFIG2(IESO_OFF & SOSCSEL_LPSOSC & WUTSEL_FST & FNOSC_FRCPLL & FCKSM_CSDCMD & OSCIOFNC_OFF & IOL1WAY_OFF & I2C1SEL_PRI & POSCMOD_NONE)

#define Fs              5000            // Örnekleme frekansımız, izleyeceğimiz en yüksek frekans değerinin en az iki katı
#define BINS            11              // 11 temel frekans izlenecek
#define Pi              3.141592654     // Pi sayısı tanımlanıyor
#define GOERTZEL_N      100             // 128 örnek alınacak
#define Level_Segment   7
#define divider         16000000/Fs

const int   freqs    [BINS] = {50, 150, 250, 350, 450, 550, 650, 750, 850, 950, 1050};
float       samples  [GOERTZEL_N];

float       coeffs   [BINS];
float       prev1    [BINS];
float       prev2    [BINS];
float       magnitude[BINS];
int         levels   [BINS];
float       n        [GOERTZEL_N];

unsigned char sample_counter    = 0;
unsigned char sample_complete   = 0;

void timer2_init(void)
{
    // 16Mhz/ftimer=PR2 olmalı.
    PR2                 = divider;
    T2CON               = 0x0000;     // 1:1, Fosc/2
    IPC1bits.T2IP       = 6;          // Kesme öncelik sırası 6
    IFS0bits.T2IF       = 0;          // Timer1 kesme bayrağı temizleniyor
    IEC0bits.T2IE       = 1;          // Timer1 kesmesi aktif
    T2CONbits.TON       = 1;          // Timer1 açılıyor
}

void ADC_init(void)
{
    AD1PCFG             = 0xFFFC;       // AN0 ve AN1 ADC portu, diğer AN portları digital I/O
    AD1CON2             = 0x0000;       // AVdd ve AVss seçildi, 28pinli modeller için bunlar zaten VDD ve VSS'ye bağlı
    AD1CON3bits.ADRC    = 0;            // Tcy kullanılacak
    AD1CON3bits.SAMC    = 3;            // Minimum Tad süresi 75ns'den büyük seçilmeli, 32MHz kristal için Tcy=62.5ns, bu değer için 2 yeterlidir
    AD1CON3bits.ADCS    = 2;            // ADCS=(Tad/Tcy)-1 şeklinde seçilir
    AD1CON1             = 0x00E0;       // Atomatik örnekleme ve çevrim yapılacak, ASAM yada SAMP bitine emirle çevrim başlayacak
    AD1CSSL             = 0x0000;       // Auto tarama yapılmayacak
    AD1CON1bits.ADON    = 1;            // ADC açılıyor
}

int ADC_read(unsigned char channel)
{
    AD1CHS              = channel;
    AD1CON1bits.DONE    = 0;
    AD1CON1bits.SAMP    = 1;           // Çevrim başlama komutu veriliyor
    while(AD1CON1bits.SAMP);
    while(!AD1CON1bits.DONE);          // Çevrimin bitmesi bekleniyor

    return ADC1BUF0;
}

// Goertzel için katsayılar hesaplanıyor

void coefficient(void)
{
    unsigned int k, i;
    for(i=0;i < BINS;i++)
    {
        k           =(unsigned int)(0.5 + (float)GOERTZEL_N * freqs[i] / Fs);
        coeffs[i]   = 2.0 * cos(2.0 * Pi * ((float)k/GOERTZEL_N));
    }
}

int goertzel(void)
{
    int big =0,
        i   =0;
    float val = 0,
          max = 0;

    // Geçici dizilerin içerisi temizleniyor
    for(i=0;i < BINS;i++)
    {
        prev2[i]=0;
        prev1[i]=0;
    }

    // GOERTZEL Algoritması İşletiliyor
    for(sample_counter=0;sample_counter < GOERTZEL_N; sample_counter++)
    {
        for(i=0;i < BINS;i++)
        {
            val     = coeffs[i] * prev1[i] - prev2[i] + (float)samples[sample_counter];
            prev2[i]= prev1[i];
            prev1[i]= val;
        }
    }

    // Genlikler belirleniyor
    for(i=0;i < BINS;i++)
    {
        magnitude[i] = (prev1[i] * prev1[i]) + (prev2[i] * prev2[i]) - (coeffs[i] * prev1[i] * prev2[i]);
        if(magnitude[i]>max)
        {
            max=magnitude[i];
            big=i;
        }
    }

    for(i=0;i < BINS;i++)
    {
        levels[i]=(int)(Level_Segment*(float)(magnitude[i]/max));
    }

    return big;
}

int main(void)
{
    CLKDIV=0;
    TRISB=0x0000;

    lcd_init();

    coefficient();
    ADC_init();
    timer2_init();

    lcd_gotoxy(2,1);
    lcd_yaz("FxDev.org");

    for(;;)
    {
        if(sample_complete)
        {
            goertzel();     // Goertzel hesaplanıyor
            lcd_gotoxy(1,1);
            veri_yolla(levels[0]);
            veri_yolla(levels[1]);
            veri_yolla(levels[2]);
            veri_yolla(levels[3]);
            veri_yolla(levels[4]);
            veri_yolla(levels[5]);
            veri_yolla(levels[6]);
            veri_yolla(levels[7]);
            veri_yolla(levels[8]);
            veri_yolla(levels[9]);
            veri_yolla(levels[10]);
            sample_complete = 0;
            sample_counter  = 0;
            IEC0bits.T2IE   = 1;
            T2CONbits.TON   = 1;
        }

    }
}

void __attribute__((__interrupt__, auto_psv)) _T2Interrupt(void)
{
    samples[sample_counter++]=(ADC_read(0)-512);
    if(sample_counter==GOERTZEL_N)
    {
        IEC0bits.T2IE   = 0;
        T2CONbits.TON   = 0;
        sample_complete = 1;
    }
    IFS0bits.T2IF = 0;
}

Yukarıdaki kodun daha önce burada anlattığım C kodlarıyla ne kadar fazla benzerlik gösterdiğini görebilirsiniz.
Uygulamaya ait çalışma videosunu aşağıda seyredebilirsiniz.

Bir başka çalışmada görüşmek üzere.
Herkese kolay gelsin.

Sponsorlu Bağlantılar
  1. murat 2013/05/18
  2. Ali 2013/05/18
  3. FxDev 2013/05/18
  4. Overlord 2013/05/18
  5. FxDev 2013/05/18
  6. Mustafa 2013/05/18
  7. FxDev 2013/05/18
  8. metin 2013/05/18
  9. FxDev 2013/05/18
  10. metin 2013/05/18
  11. Ferhat YOL 2013/05/18
  12. FxDev 2013/05/18
  13. Ferhat YOL 2013/05/18
  14. FxDev 2013/05/18
  15. Ferhat YOL 2013/05/18
  16. Hakan K. 2013/05/18
  17. FxDev 2013/05/18
  18. Ali Deniz 2016/05/03

Yorum

Soru: