FPGA ile Hareketli Ortalama Hesaplama

Ortalama bir zaman aralığındaki alınan verilerin toplamının veri sayısına bölünmesi ile hesaplanır. Hareketli ortalama ise zaman aralığının sürekli kaydırılması ile hesaplanır. Yani normal ortalamada her n veride ortalama tekrar hesaplanırken, hareketli ortalamada en eski veri ortalamadan atılır ve yeni veri ortalamaya alınır. Y hareketli ortalama, k ortalamaya girecek eleman sayısı ve x 'de değerler olmak üzere, hareketli ortalama şu şekilde yazılabilir:

\begin{align*}Y_m=\frac{1}{k}\sum\limits_{n=0}^{k-1}x_{m-n}\end{align*}

Bölme işlemi diğer işlemlere göre daha yavaş hesaplandığından dolayı, eleman sayısı 2'nin kuvveti olarak seçilerek bölme işlemi bitsel kaydırma işlemine dönüştürülebilir.

Donanımsal tasarım aşağıdaki gibi gösterilebilir:

Tasarımı daha kolay anlaşılabilir hale getirmek için, tasarımı modüller sayesinde yapalım. Her bir modül aşağıdaki gibi olsun.

Bu modülün VHDL kodu aşağıdadır:

--Kütüphaneler
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL; 

entity MovingAverageModule is
    generic(DataLength : integer := 12;                          --Veri uzunluğu
        SumLength : integer := 17);                              --Toplam verisinin uzunluğu
        
    port(Clk, Reset : in std_logic;                              --Clock ve reset girişi
        D : in std_logic_vector(DataLength - 1 downto 0);        --Veri girişi
        Q : out std_logic_vector(DataLength - 1 downto 0);       --Veri çıkışı
        SumIn : in std_logic_vector(SumLength - 1 downto 0);     --Toplam verisi girişi
        SumOut : out std_logic_vector(SumLength - 1 downto 0));  --Toplam verisi çıkışı
end MovingAverageModule;

architecture Behavioral of MovingAverageModule is

    --Q sinyali
    signal QSignal : std_logic_vector(DataLength - 1 downto 0);

begin

    --Flip-Flop processi
    process(Clk, Reset)
    begin
        --Eğer Reset sinyali '1' ise flip-flop'u resetle
        if Reset = '1' then
            QSignal <= (others => '0');
            
        --Reset '1' değilse, clock sinyalinin yükselen kenarı ile
        --D sinyalindeki veriyi QSignal sinyaline ata
        elsif rising_edge(Clk) then
            QSignal <= D;
        end if;
    end process;
    
    --QSignal sinyalini Q sinyaline bağla
    Q <= QSignal;
    
    --SumIn ve QSignal sinyallerini topla ve SumOut sinyaline gönder
    SumOut <= std_logic_vector(unsigned(SumIn) + unsigned(QSignal));

end Behavioral;

Daha sonra bu modülü kullarak hareketli ortalamayı hesaplayan modülü tasarlayalım:

--Kütüphaneler
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL; 

entity MovingAverage is
    generic(DataLength : integer := 12;                           --Veri uzunluğu
        SamplePower : integer := 5);                              --Ortalamaya girecek örnek sayısının kuvveti
                                                                  --Örnek sayısı = 2^SamplePower
    port(Clk, Reset : in std_logic;                               --Clock ve Reset girişi
        DataIn : in std_logic_vector(DataLength - 1 downto 0);    --Ortalamaya girecek yeni veri
        Average : out std_logic_vector(DataLength - 1 downto 0)); --Ortalama çıkışı
end MovingAverage;

architecture Behavioral of MovingAverage is

--Kayan ortalama modülünü çağır
component MovingAverageModule
    generic(DataLength : integer := 12;                          --Veri uzunluğu
        SumLength : integer := 17);                              --Toplam verisinin uzunluğu
    port(Clk, Reset : in std_logic;                              --Clock ve reset girişi
        D : in std_logic_vector(DataLength - 1 downto 0);        --Veri girişi
        Q : out std_logic_vector(DataLength - 1 downto 0);       --Veri çıkışı
        SumIn : in std_logic_vector(SumLength - 1 downto 0);     --Toplam verisi girişi
        SumOut : out std_logic_vector(SumLength - 1 downto 0));  --Toplam verisi çıkışı
end component;

    --Verilerin taşınacağı sinyal dizisi
    type Data_Array is array (2**SamplePower downto 0) of std_logic_vector(DataLength - 1 downto 0);
    
    --Toplam verilerinin taşınacağı sinyal dizisi
    type Sum_Array is array (2**SamplePower downto 0) of std_logic_vector(DataLength + SamplePower - 1 downto 0);
    
    --Veri ve toplam verilerinin taşınacağı sinyaller
    signal DBus : Data_Array;
    signal SBus : Sum_Array;

begin

    --Modullerin üretilmesi
    Modules : for i in 0 to 2**SamplePower - 1 generate
        Module : MovingAverageModule
            generic map(DataLength, DataLength + SamplePower)
            port map(Clk, Reset, DBus(i), DBus(i + 1), SBus(i), SBus(i + 1));
    end generate;
    
    --DBus dizisinin ilk elemanına yeri veriler gönderilir
    DBus(0) <= DataIn;
    
    --SBus dizisinin ilk elemanına 0 gönderilir
    SBus(0) <= (others => '0');
    
    --Bölme işlemi bitsel kaydırma ile hesaplanır
    --SBus dizisinin son elemanı SamplePower kadar sağa kaydırılır
    Average <= SBus(2**SamplePower)(DataLength + SamplePower - 1 downto SamplePower);

end Behavioral;

Daha önce ADC128S022 entegresi ile ADC verilerini almıştık. Yazıya buradan ulaşabilirsiniz. Aldığımız verilerin hareketli ortalamasını bularak daha stabil değerler elde edebiliriz.

ADC + Hareketli Ortalama Uygulaması:

--Kütüphaneler
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL; 

entity MovingAverageADC is
    port(Clk, Reset : in std_logic;                                 --Clock ve reset girişi
        CS, SCLK, DIN : out std_logic;                              --ADC entegresinin SPI bağlantıları
        DOUT : in std_logic;                                        --ADC entegresinin SPI bağlantıları
        LedOut : out std_logic_vector(11 downto 0));                --Led çıkışı
end MovingAverageADC;

architecture Behavioral of MovingAverageADC is

component MovingAverage
    generic(DataLength : integer := 12;                           --Veri uzunluğu
        SamplePower : integer := 5);                              --Ortalamaya girecek örnek sayısının kuvveti
                                                                  --Örnek sayısı = 2^SamplePower
    port(Clk, Reset : in std_logic;                               --Clock ve Reset girişi
        DataIn : in std_logic_vector(DataLength - 1 downto 0);    --Ortalamya girecek yeni veri
        Average : out std_logic_vector(DataLength - 1 downto 0)); --Ortalama çıkışı
end component;

component ADC
    port(Clk : in std_logic;                             --Clock Girişi
        CS, SCLK, DIN : out std_logic;                   --ADC Entegresinin CS, SCLK ve DIN Girişleri
        DOUT : in std_logic;                             --ADC Entegresinin DOUT Çıkışı
        ADCOut : out std_logic_vector(11 downto 0));     --ADC'den okunan 12 bitlik sinyal
end component;

    signal ADCOut : std_logic_vector(11 downto 0);
    
    --Hareketli ortalama modülünün clock sinyali
    signal ClkDiv : std_logic := '0';

begin

    --Hareketli ortalama modülü için clock bölücü process.
    --ADC modülün hızı yaklaşık 195ksps olduğundan
    --clock bölücü ile 50Mhz'den 195khz elde edilir.
    process(Clk)
    variable ClkCnt : unsigned(7 downto 0) := (others => '0');
    begin
        if rising_edge(Clk) then
            if ClkCnt < 127 then
                ClkCnt := ClkCnt + 1;
            else
                ClkDiv <= not ClkDiv;                
                ClkCnt := (others => '0');
            end if;
        end if;
    end process;

    --ADC modülünün üretilmesi
    ADCModule : ADC
        port map(Clk, CS, SCLK, DIN, DOUT, ADCOut);
    
    --Hareketli ortalama modülünün üretilmesi
    --SamplePower = 5 -> 32 örneğin ortalaması alınıyor.
    MovingAverageModule : MovingAverage
        generic map(12, 5)
        port map(ClkDiv, not Reset, ADCOut, LedOut);

end Behavioral;

Video:

One thought on “FPGA ile Hareketli Ortalama Hesaplama

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir