Vivado ile Nexys 4 Kartı Üzerinde UART Protokolü Kullanarak Data Kontrolü

UART (Universal asynchronous receiver/transmitter – Evrensel eşzamanlı olmaya alıcı/verici) paralel ve seri formlar arasında data çevrim işlemi yapan bilgisayar donanım parçasından biridir. UART’lar RS-232, RS-485 gibi yaygın iletişim standartları ile birlikte kullanılır.

Aşağıda VHDL dilinde UART modülü ile data alma ve gönderme işlemlerinin gerçekleştirildiği örnekler verilmiştir. 

Örnek 1: Aşağıda UART protokoklü kullanarak   data gönderim işlemninin yapıldığı UART_tx.vhd VHDL kodu erilmiştir. UART_tx varlığımıza ilişkin generic bildirimleri 7-10. satırlarda yapılmıştır. generic bildirimi içerisinde yapılan değerler kullanılarak 23. satırda her bir bit değeri için gerekli saat darbesi sayısı hesaplanmaktadır. Port bildirim işlemleri 11-18 satırları arasında yapılmaktıdır. UART_tx varlığımız 1 bitlik başla biti, 1 bitlik bitir biti ve 8 bitlik data gönderecek şekilde tasarlanmıştır. t_UART_tx tipinde tanımlı r_UART_tx sinyali başlangıç durumunda BOSTA durumundadır ve in_tx_basla giriş portu değerinin ‘1’ olmasını beklemektedir.  in_tx_basla giriş portu değerinin ‘1’ olması ile birlikte, r_data sinyaline in_txt_data giriş portu değeri atanmaktadır ve r_UART_tx sinyali BASLA durumuna dallanır. BASLA durumunda başla biti gönderim işlemi yapılmaktadır. Yani CLK_BIT sabitinin değeri kadar saat darbesinde bu durum içerisinde beklenmekte ve out_tx_cikis portuna ‘1’ değeri gönderilmektedir. CLK_BIT sayısı kadar saat darbesi beklendikten sonra r_UART_tx sinyali GONDER durumuna dallanır. Bu durumda r_data sinyalinin en anlamsız bitinden en anlamlı bitine doğru tüm datalar  CLK_BIT sayısı kadar saat darbesi süresi ile gönderilmektedir. Yani bu durum içerisinde 8 x CLK_BIT saat darbesi kadar beklenmektedir. Tüm bitlerin gönderim işleminden sonra r_UART_tx sinyali BITIR durumuna dallanır. Bu durum içerisinde bitir biti gönderim işlemi yapılmaktadır. Yani CLK_BIT sabitinin değeri kadar saat darbesinde bu durum içerisinde beklenmekte ve out_tx_cikis portuna ‘1’ değeri gönderilmektedir. CLK_BIT sayısı kadar saat darbesi beklendikten sonra r_UART_tx sinyali TAMAM durumuna dallanır. TAMAM durumunda r_tx_tamam sinyali ‘1’ değerini alarak data gönderim işleminin bittiği bildirilmektedir ve r_UART_tx sinyali BOSTA durumuna dallanır.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
 
entity UART_tx is
  Generic (
    CLK_FREKANS : integer := 100000000; -- 100 MHz
    BOUDRATE : integer := 115200
  );
  Port(
    in_clk : in std_logic;
    in_rst : in std_logic;
    in_tx_basla : in std_logic;
    in_tx_data : in std_logic_vector(7 downto 0);
    out_tx : out std_logic;
    out_tx_tamam : out std_logic
  );
end UART_tx;
 
architecture Behavioral of UART_tx is
 
  constant CLK_BIT : integer := CLK_FREKANS / BOUDRATE + 1;
 
  type t_UART_tx is (BOSTA, BASLA, GONDER, BITIR, TAMAM);
  signal r_UART_tx : t_UART_tx := BOSTA;
  signal r_clk_sayac : integer range 0 to CLK_BIT - 1 := 0;
  signal r_data_ind : integer range 0 to 7 := 0;
  signal r_data   : std_logic_vector(7 downto 0) := (others => '0');
  signal r_tx : std_logic := '1';
  signal r_tx_tamam : std_logic := '0';   
 
begin
 
  out_tx <= r_tx;
  out_tx_tamam <= r_tx_tamam;
 
  process(in_clk, in_rst)
  begin
    if in_rst = '1' then
      r_UART_tx <= BOSTA;
      r_clk_sayac <= 0;
      r_data_ind <= 0;
      r_data <= (others => '0');
      r_tx <= '1';
      r_tx_tamam <= '0';
 
    elsif rising_edge(in_clk) then
      r_tx_tamam <= '0';
      case r_UART_tx is
        when BOSTA =>
          r_tx <= '1';
          r_clk_sayac <= 0;
          r_data_ind <= 0;
          if in_tx_basla = '1' then
            r_data <= in_tx_data;
            r_UART_tx <= BASLA;
          end if; 
 
        when BASLA =>
          r_tx <= '0';
          if r_clk_sayac = CLK_BIT - 1 then
            r_clk_sayac <= 0;
            r_UART_tx <= GONDER;                       
          else
            r_clk_sayac <= r_clk_sayac + 1;
          end if;
 
        when GONDER =>     
          r_tx  <= r_data(r_data_ind);
          if r_clk_sayac = CLK_BIT - 1 then
            r_clk_sayac <= 0;
            if r_data_ind = 7 then
              r_data_ind <= 0;
              r_UART_tx <= BITIR;
            else
              r_data_ind <= r_data_ind + 1;
            end if;
          else
            r_clk_sayac <= r_clk_sayac + 1;
          end if;                   
 
        when BITIR =>
          r_tx  <= '1';
          if r_clk_sayac = CLK_BIT - 1 then
            r_clk_sayac <= 0;
            r_UART_tx <= TAMAM;                       
          else
            r_clk_sayac <= r_clk_sayac + 1;
          end if;
 
        when TAMAM =>
          r_tx  <= '1';
          r_tx_tamam <= '1';
          r_UART_tx <= BOSTA; 
 
        when others => NULL;
      end case;
    end if;
end process;
 
end Behavioral;

Örnek 2: Aşağıda UART protokoklü kullanarak   data alım işlemininin yapıldığı UART_rx.vhd VHDL kodu erilmiştir. UART_rx varlığımıza ilişkin generic bildirimleri 7-10. satırlarda yapılmıştır. generic bildirimi içerisinde yapılan değerler kullanılarak 22. satırda her bir bit değeri için gerekli saat darbesi sayısı hesaplanmaktadır. Port bildirim işlemleri 11-17 satırları arasında yapılmaktıdır. UART_rx varlığımız 8 bitlik data alacak şekilde tasarlanmıştır. 48. satırda saat darbelerinin farklı olmasından dolayı domain eşleştirme işlemi yapılmaktadır. t_UART_rx tipinde tanımlı r_UART_rx sinyali başlangıç durumunda BOSTA durumundadır ve r_rx_cnt sinyalin ilk 2 bitinin değerinin “10” olması beklemektedir.r_rx_cnt sinyalin ilk 2 bitinin değerinin “10” olması ile r_UART_rx sinyali BASLA durumuna dallanır. BASLA durumunda CLK_BIT sabitinin değerinin yarısı kadar beklenmektedir. CLK_BIT/2 sayısı kadar saat darbesi beklendikten sonra r_UART_rx sinyali DATA_AL durumuna dallanır. Bu durumda CLK_BIT sayısı kadar saat darbesi süresi kadar beklendikten sonra r_data sinyalinin en anlamsız bitinden en anlamlı bitine doğru datalar yazılmaktadır. Yani bu durum içerisinde 8 x CLK_BIT saat darbesi kadar beklenmektedir. Tüm bitlerin alınma işleminden sonra r_UART_rx sinyali BITIR durumuna dallanır. Bu durum içerisinde bitir CLK_BIT sabitinin değeri kadar saat darbesi beklendikten sonra r_UART_rx sinyali TAMAM durumuna dallanır. TAMAM durumunda r_rx_tamam sinyali ‘1’ değerini alarak data alım işleminin bittiği bildirilmektedir ve r_UART_rx sinyali BOSTA durumuna dallanır.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
 
entity UART_rx is
  Generic (
    CLK_FREKANS : integer := 100000000;
    BOUDRATE : integer := 115200
  );
  Port(
    in_clk : in std_logic;
    in_rst : in std_logic;
    in_rx : in std_logic;
    out_rx_data : out std_logic_vector(7 downto 0);
    out_rx_tamam : out std_logic
  );
end UART_rx;

architecture Behavioral of UART_rx is
 
  constant CLK_BIT : integer := CLK_FREKANS / BOUDRATE + 1;
 
  type t_UART_rx is (BOSTA, BASLA, DATA_AL, BITIR, TAMAM);
  signal r_UART_rx : t_UART_rx := BOSTA;
  signal r_clk_sayac : integer range 0 to CLK_BIT - 1 := 0;
  signal r_data_ind : integer range 0 to 7 := 0;
  signal r_data   : std_logic_vector(7 downto 0) := (others => '0');
  signal r_rx_tamam : std_logic := '0'; 
  signal r_rx_cnt : std_logic_vector(2 downto 0) := (others => '0');
 
begin
 
  out_rx_data <= r_data;
  out_rx_tamam <= r_rx_tamam;
 
  process(in_clk)
  begin
    if in_rst = '1' then
      r_UART_rx <= BOSTA;
      r_clk_sayac <= 0;
      r_data_ind <= 0;
      r_data <= (others => '0');
      r_rx_cnt <= (others => '0');
      r_rx_tamam <= '0';
 
    elsif rising_edge(in_clk) then
      r_rx_cnt <= r_rx_cnt(1 downto 0) & in_rx;
      r_rx_tamam <= '0';
 
      case r_UART_rx is
        when BOSTA =>
          if r_rx_cnt(2 downto 1) = "10" then
            r_UART_rx <= BASLA;               
          end if;
 
        when BASLA =>
          if r_clk_sayac = (CLK_BIT - 1) / 2 then
            r_clk_sayac <= 0;
            r_UART_rx <= DATA_AL;                       
          else
            r_clk_sayac <= r_clk_sayac + 1;
          end if;
 
        when DATA_AL =>
          r_data(r_data_ind) <= r_rx_cnt(2);
          if r_clk_sayac = CLK_BIT - 1 then
            r_clk_sayac <= 0;
            if r_data_ind = 7 then
              r_data_ind <= 0;
              r_UART_rx <= BITIR;                          
            else
              r_data_ind <= r_data_ind + 1;
            end if;
          else
            r_clk_sayac <= r_clk_sayac + 1;
          end if;
 
        when BITIR =>
          if r_clk_sayac = CLK_BIT - 1 then
            r_clk_sayac <= 0;
            r_UART_rx <= TAMAM;                       
          else
            r_clk_sayac <= r_clk_sayac + 1;
          end if;
 
        when TAMAM =>
          r_rx_tamam <= '1';
          r_UART_rx <= BOSTA; 
        when others => NULL;
      end case;  
    end if;
  end process;
end Behavioral;

Örnek 3: Aşağıda UART protokoklü kullanarak   data alım-gönderim işlemininin yapıldığı UART_main.vhd VHDL kodu erilmiştir. UART_main varlığımıza ilişkin port bildirim işlemleri 12-18 satırları arasında yapılmaktıdır. UART_rx varlığımız UART_rx alt devresinden aldığı dataları UART_tx alt devresi ile göndermek üzere tasarlanmıştır. UART_tx alt devresi component tanımlama işlemleri 15-28. satırlar arasında, bağlantı işlemleri ise 97-109. satırlar arasında yapılmaktadır. UART_rx alt devresi component tanımlama işlemleri 30-42. satırlar arasında, bağlantı işlemleri ise 84-95. satırlar arasında yapılmaktadır.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
 
entity UART_main is
  Port (
    in_clk : in std_logic;
    in_rst : in std_logic;
    in_rx : in std_logic;
    out_tx : out std_logic
  );
end UART_main;
 
architecture Behavioral of UART_main is
 
  component UART_tx
  Generic (
   CLK_FREKANS : integer := 100000000;
   BOUDRATE : integer := 115200
  );
  Port(
    in_clk : in std_logic;
    in_rst : in std_logic;
    in_tx_basla : in std_logic;
    in_tx_data : in std_logic_vector(7 downto 0);
    out_tx : out std_logic;
    out_tx_tamam : out std_logic
  );
  end component;
 
  component UART_rx
  Generic (
    CLK_FREKANS : integer := 100000000;
    BOUDRATE : integer := 115200
  );
  Port(
    in_clk : in std_logic;
    in_rst : in std_logic;
    in_rx : in std_logic;
    out_rx_data : out std_logic_vector(7 downto 0);
    out_rx_tamam : out std_logic
  ); 
  end component;
 
  type t_Data_Cntrl is (BOSTA, DATA_AL, DATA_GONDER);
  signal r_Data_Cntrl : t_Data_Cntrl := BOSTA;
  signal r_tx_basla : std_logic := '0';
  signal r_tx_tamam : std_logic := '0';
  signal r_rx_tamam : std_logic := '0';
  signal r_data : std_logic_vector(7 downto 0);
  signal r_rx_data : std_logic_vector(7 downto 0);
  signal r_tx_data : std_logic_vector(7 downto 0);
 
begin

  process(in_clk, in_rst)
  begin
    if in_rst = '1' then
      r_Data_Cntrl <= BOSTA;
      r_data <= (others => '0');

    elsif rising_edge(in_clk) then
      r_tx_basla <= '0';

      case r_Data_Cntrl is   
        when BOSTA =>
          r_Data_Cntrl <= DATA_AL;
 
        when DATA_AL =>
          if r_rx_tamam = '1' then
            r_tx_data <= r_rx_data;
            r_tx_basla <= '1';
          end if;
 
        when DATA_GONDER =>
          if r_tx_tamam = '1' then
            r_Data_Cntrl <= BOSTA;
          end if;
 
        when others => NULL;
      end case;
    end if;
  end process;
 
  UART_rx_map : UART_rx
  Generic map(
    CLK_FREKANS => 100000000, --100 MHz
    BOUDRATE => 115200
  )
  Port map(
    in_clk => in_clk,
    in_rst => in_rst,
    in_rx => in_rx,
    out_rx_data => r_rx_data,
    out_rx_tamam => r_rx_tamam
  ); 
 
  UART_tx_map : UART_tx
  Generic map(
    CLK_FREKANS => 100000000, --100 MHz
    BOUDRATE => 115200
  )
  Port map(
    in_clk => in_clk,
    in_rst => in_rst,
    in_tx_basla => r_tx_basla,
    in_tx_data => r_tx_data,
    out_tx => out_tx,
    out_tx_tamam => r_tx_tamam
  );

end Behavioral;

UART_main modülümüzün test işlemleri için Realterm programı kullanılmıştır. Realterm programında Display sekmesinde Display As’de Hex[space] seçilerek ekranda gösterilecek karakter formatı seçilmektedir (Şekil 1).

Şekil 1 UART_main modülü test işlemleri – 1

Port sekmesinde Baud sekmesinde 115200, Port sekmesinde ise \VCP0 seçilmektedir. Daha sonra Change butonuna basılarak değişiklikler yapılmaktadır (Şekil 2).

Şekil 2 UART_main modülü test işlemleri – 2

Send sekmesinde UART protokülü ile FPGA’ya göndermek istediğimiz datayı hex sayı formatında yazarak Send Numbers butonuna basılır. UART_main modülü aldığı datayı tekrar göndermek üzere tasarlandığından dolayı ekranda gönderilen data görülmektedir (Şekil 3).

Şekil 3 UART_main modülü test işlemleri – 3

2 thoughts on “Vivado ile Nexys 4 Kartı Üzerinde UART Protokolü Kullanarak Data Kontrolü

  1. Hocam Merhabalar,

    Kodlarınız üzerinde denemeler yaparken, her byte veri gönderildikten sonra bir byte boşluk bırakılıp sonra diğer byte yazlıyor. Yani 5 byte gönderdiysem eğer 1, 3 ve 5. bytler görünüyor sadece. Bunun sebebi nedir öğrenmek istiyorum. Teşekkürler

    1. Merhaba Cihat,

      Buradaki kod doğrudan bir byte okuyup onu geri dönderiyor. Eğer Sen ardışık 5 byte göndereceksen ona göre düzenleme yapmalısın. Senin 2. ve 4. bytlerinda durum makinası gönderme durumunda olduğu için o bilgileri göremiyorsun.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir