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