FPGA ile ADXL345 Entegresinin Kontrolü

ADXL345 Analog Devices tarafından üretilen ivme ölçer entegresidir. ±2g, ±4g, ±8g ve ±16g aralıklarında 13-bite varan çözünürlükte ivme değerleri okunabilir. Veri çıkış hızı maksimum 3200hz'dir. İletişim I²C, 3-Wire SPI ve 4-Wire SPI üzerinden yapılabilir. Bu uygulamada 3-Wire SPI ile entegre ile iletişim kurulmuştur. Zamanlama diyagramı aşağıda gösterilmiştir.

Diyagramda gösterilen ilk byte'ın 7. biti, okuma ya da yazma yapılacağını belirten R/W bitidir, 6. bit ise 1 byte'dan fazla okuma ya da yazma yapılacağını belirten bittir, geriye kalan 6 bit ise erişim yapılacak registerin adresidir. 1 byte'dan fazla okuma ya da yazma yapılacağı zaman CS sinyali '1' konumuna gelene kadar iletişim devam eder. Bu sırada SCLK sinyali sürekli üretilir. Bir sonraki erişimde adres değeri otomatikman 1 artar. Bu sayede ivme değerleri okunurken her seferinde register adresinin gönderilmesi gerekmez.

1600hz ya da 3200hz veri hızı için SPI hızının 2Mhz'e eşit ya da daha yüksek olması gerekmektedir. Bu uygulamada veri hızı 3200hz ve SPI hızı 4.16Mhz olarak seçilmiştir.

Tasarımın VHDL kodu:

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

entity ADXL345 is
    port(Clk, Reset : in std_logic;                   --Clk ve Reset girişi
        INT : in std_logic;                           --ADXL345'in kesme girişi
        nCS, SCLK : out std_logic;                    --ADXL345'in CS ve SCLK çıkışları
        SDIO : inout std_logic;                       --ADXL345'in SDIO hattı
        NewData : out std_logic;                      --Yeni veri sinyali
                                                      --Bu sinyalin yükselen kenarı ile
                                                      --yeni verinin geldiği anlaşılır.
                                                                     
        XAxis, YAxis, ZAxis : out std_logic_vector(11 downto 0)); --Her bir eksendeki ivme.
                                                                  --3200Hz ve Full-çözünürlük ile
                                                                  --maxsimum 12-bitlik veri elde edilebilir.
end ADXL345;

architecture Behavioral of ADXL345 is

    --ADXL345'in bazı registerleri ve adresleri
    constant BW_RATE : std_logic_vector(7 downto 0) := X"2C";
    constant POWER_CTL : std_logic_vector(7 downto 0) := X"2D";
    constant INT_ENABLE : std_logic_vector(7 downto 0) := X"2E";
    constant DATA_FORMAT : std_logic_vector(7 downto 0) := X"31";
    constant DATAX0 : std_logic_vector(7 downto 0) := X"32";

    --Start sinyali
    --'1' konumuna geçirildiğnde Address, Data, ... sinyallerine
    --göre iletişim gerçekleştirilir. İletişim bittiğinde
    --'0' konumuna geçirilir.
    signal Start : std_logic := '0';
    
    --SDIO ve SCLK kontrol sinyalleri
    signal SCLKSignal : std_logic := '1';
    signal SDIOSignal : std_logic;

    --SDIO sinyalinin yönü
    --'1' konumunda SDIO sinyali Hi-Z konumundadır ve hat
    --ADXL345 tarafından kontrol edilir (Giriş).
    --'0' konumunda SDIO sinyali, SDIOSignal sinyali tarafında
    --kontrol edilir (Çıkış).
    signal SDIODirection  : std_logic := '1';
    
    --Okunacak ya da yazılacak registerin adresi
    signal Address : std_logic_vector(7 downto 0);
    
    --Yazma için gönderilen veri
    signal Data : std_logic_vector(7 downto 0);
    
    --Okunacak ya da yazılacak byte sayısı
    signal Bytes : integer range 0 to 7 := 0;
    
    --Kalan okunacak ya da yazılacak byte sayısı
    signal RemainingBytes : integer range 0 to 7 := 0;
    
    --Alnan veri
    signal ReceivedData : std_logic_vector(47 downto 0);
    
    --Gönderilen ilk byte
    signal FirstByte : std_logic_vector(7 downto 0);
    
    --Gönderilen ilk byte'ın 7. biti R/W bitidir.
    --Okuma ya da yazma yapılacağını belirtir.
    alias RW is FirstByte(7);
    
    --Gönderilen ilk byte'ın 6. Multiple-Byte bitidir.
    --Bu bit '1' konumunda ise 1 byte'dan fazla veri
    --yazılacağını ya da okunacağının belirtir.
    --Veri sayısı Bytes değişkeni ile belirlenir.
    alias MB is FirstByte(6);
    
begin
    
    --Gönderilen ilk byte'ın ilk 6 biti adresdir.
    FirstByte(5 downto 0) <= Address(5 downto 0);    
    
    --Alınan veri her bir eksen için parçalara ayrılır.
    XAxis(3 downto 0) <= ReceivedData(47 downto 44);
    XAxis(11 downto 4) <= ReceivedData(39 downto 32);
    
    YAxis(3 downto 0) <= ReceivedData(31 downto 28);
    YAxis(11 downto 4) <= ReceivedData(23 downto 16);
    
    ZAxis(3 downto 0) <= ReceivedData(15 downto 12);
    ZAxis(11 downto 4) <= ReceivedData(7 downto 0);
        
    --SCLK için clock bölücü process
    --50Mhz / 12 = 4.16Mhz SPI hızı
    process(Clk)
    variable ClkCnt : integer range 0 to 15 := 0;
    begin
        if rising_edge(Clk) then
            if ClkCnt < 5 then
                ClkCnt := ClkCnt + 1;
            else
                ClkCnt := 0;
                SCLKSignal <= not SCLKSignal;
            end if;
        end if;    
    end process;
    
    --İletişimin ve entegrenin ayarlarının yapıldığı process
    process(SCLKSignal, Clk, SDIO, Start, Address, SDIOSignal)
    
    --SPI iletişiminin adım sayacı
    variable State : integer range 0 to 31 := 0;
    
    --Program sayacı
    variable ProgState : integer range 0 to 15 := 0;
    begin

        if falling_edge(SCLKSignal) then
        
            --Eğer 1'den fazla byte veri okunacaksa ya da yazılacaksa,
            --0. adımda okunacak byte sayısı kalan byte sayısına aktarılır.
            if State = 0 and MB = '1' then
                RemainingBytes <= Bytes;
            end if;

            --17. adımda, birden fazla byte veri okunacaksa ya da yazılacaksa
            --10. adıma geri dönülür.             
            if State = 17 and RemainingBytes > 0 and MB = '1' then
                State := 10;
                --Okunacak kalan byte sayısı 1 azaltılır.
                RemainingBytes <= RemainingBytes - 1;
            
            --İletişim sayacı 19 dan küçükse, SCLKSignal'in düşen kenarı
            --ile 1 arttırılır.
            elsif State < 19 and Start = '1' then
                State := State + 1;
                
            --Değilse başa dönülür.
            else
                State := 0;
            end if;
        end if;
        
        --SPI haberleşme işlemleri
        if rising_edge(Clk) and Start = '1' then
            --nCS ve SCLK '1' konumunda olduğunda dolayı hat boşta 
            if State = 0 then
                SCLK <= '1';
                SDIODirection <= '0';
                SDIOSignal <= '1';
                nCS <= '1';
             
            --nCS, '0' konumuna geçirilerek iletişimin başlayacağı gösterilir.
            elsif State = 1 then
                SCLK <= '1';            
                SDIODirection <= '0';
                SDIOSignal <= '1';
                nCS <= '0';  
                           
             --SCLK hattına 4.16Mh'lik SPI clock'u bağlanır.             
             --SDIO hattından da ilk byte gönderilir.             
             elsif (State > 1) and (State < 10) then
                SCLK <= SCLKSignal;
                SDIOSignal <= FirstByte(9 - State);
                SDIODirection <= '0';
                nCS <= '0'; 
                             
             --Registere yazma ya da registerden okuma yapılan döngü.
             elsif (State > 9) and (State < 18) then
                SCLK <= SCLKSignal;
                SDIODirection <= RW;
                
                --Yazma işlemi yapılacaksa SDIO hattından veri gönderilir.
                if RW = '0' then
                    SDIOSignal <= Data(17 - State);            
                end if;
                nCS <= '0';
                
            --SCLK, '1' konumuna getirilerek iletişim durdurulur.
            elsif State = 18 then
                SCLK <= '1';
                SDIODirection <= '0';
                SDIOSignal <= '1';
                nCS <= '0';
                
            --nCS, '0' konumuna getirilerek iletişim sonlandırılır.
            elsif State = 19 then
                SCLK <= '1';
                SDIODirection <= '0';
                SDIOSignal <= '1';
                nCS <= '1';
                Start <= '0';
             end if;
         end if;

        --Okuma işlemi yapılacaksa SCLK'nın yükselen kenarı ile
        --örnekleme yapılır.
          if (State > 9) and (State < 18) and RW = '1' then
            if rising_edge(SCLKSignal) then
                ReceivedData(17 - State + RemainingBytes * 8) <= SDIO;
            end if;
        end if;
        
        --Program
        --Konfigürasyonun ve kesme kontrolünün yapıldığı bölüm
        if rising_edge(Clk) then
        
            --3-Wire SPI, +-16g ölçüm aralığı ve Left Justified
            if ProgState = 0 and (Start = '0') and (State = 0) then
                RW <= '0';
                Address <= DATA_FORMAT;
                Data <= X"4F";
                MB <= '0';
                Bytes <= 0;
                Start <= '1';
                ProgState := 1;
                
            --Standby modundan çık
            --Full çözünürlük
            elsif ProgState = 1 and (Start = '0') and (State = 0) then
                RW <= '0';
                Address <= POWER_CTL;
                Data <= X"08";
                MB <= '0';
                Bytes <= 0;
                Start <= '1';
                ProgState := 2;
                
            --Saniyede 3200Hz veri çıkışı
            elsif ProgState = 2 and (Start = '0') and (State = 0) then
                RW <= '0';
                Address <= BW_RATE;
                Data <= X"FF";
                MB <= '0';
                Bytes <= 0;
                Start <= '1';
                ProgState := 3;
                
            --DataReady kesmesi aktif
            elsif ProgState = 3 and (Start = '0') and (State = 0) then
                RW <= '0';
                Address <= INT_ENABLE;
                Data <= X"80";
                MB <= '0';
                Bytes <= 0;
                Start <= '1';
                ProgState := 4;
                
            --Kesme oluştu mu kontrol et
            elsif ProgState = 4 and (Start = '0') then
            --INT sinyali '1' ise yani kesme gerçekleşmiş ise
                --bir sonraki adıma geç ve NewData sinyalini '0' a çek.
                if INT = '1' then
                    ProgState := 5;
                    NewData <= '0';
                --INT sinyali '0' ise NewData sinyalini '1' konumunda
                --tut ve bu döngüde kal.
                else
                    ProgState := 4;
                    NewData <= '1';
                end if;
                --Registerler üzerinde işlem yapılmayacağında dolayı 
                --Start biti '0' a çekilir.
                Start <= '0';    
                
            --İvme verilerini oku.
            --DATAX0 adresinden başlayarak sırayla 6 veri oku.
            elsif ProgState = 5 and (Start = '0') and (State = 0) then
                RW <= '1';
                Address <= DATAX0;
                Data <= X"00";
                MB <= '1';
                Bytes <= 5;
                Start <= '1';
                ProgState := 4;
            end if;
        end if;
        
    end process;
    
    
    --SDIODirection sinyaline göre SDIO sinyalinin 
    --durumunun belirlenmesi.
    with SDIODirection select
        SDIO <= 'Z' when '1',
                  SDIOSignal when others;

end Behavioral;

Bir cevap yazın

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