Kontrol Tampilan Tujuh Segmen FPGA

Halo, Habr! Saya ingin memberikan kontribusi untuk kemajuan FPGA. Dalam artikel ini saya akan mencoba menjelaskan cara menjelaskan dalam VHDL perangkat yang mengontrol tampilan tujuh segmen. Tetapi sebelum memulai, saya ingin berbicara singkat tentang bagaimana saya datang ke FPGA dan mengapa saya memilih bahasa VHDL.

Sekitar setengah tahun yang lalu saya memutuskan untuk mencoba pemrograman FPGAs. Sebelum itu, saya belum pernah mengalami sirkuit. Ada sedikit pengalaman menggunakan mikrokontroler (Atmega328p, STM32). Segera setelah keputusan untuk merasa nyaman dengan FPGA, muncul pertanyaan tentang memilih bahasa yang akan saya gunakan. Pilihan jatuh pada VHDL karena pengetikannya yang ketat. Sebagai seorang pemula, saya ingin menangkap sebanyak mungkin masalah pada tahap sintesis, dan bukan pada perangkat yang berfungsi.

Mengapa tepatnya layar tujuh segmen? LED berkedip sudah lelah, dan logika berkedip itu tidak mewakili sesuatu yang menarik. Logika mengendalikan tampilan di satu sisi lebih rumit daripada mengedipkan LED (mis. Menulis itu lebih menarik), dan di sisi lain itu cukup sederhana untuk diterapkan.

Apa yang saya gunakan dalam proses pembuatan perangkat:

  • FPGA Altera Cyclone II (saya tahu ini sudah ketinggalan zaman, tetapi orang Cina dapat membelinya dengan harga satu sen)
  • Quartus II versi 13.0.0 (sejauh yang saya tahu, ini adalah versi terbaru yang mendukung Cyclone II)
  • Simulator ModelSim
  • Layar tujuh segmen dengan register geser

Tantangan


Buat perangkat yang akan menampilkan angka 0 - 9 dalam satu siklus. Setelah satu detik, nilai yang ditampilkan pada layar akan meningkat sebesar 1.

Ada banyak cara untuk mengimplementasikan logika ini. Saya akan membagi perangkat ini menjadi beberapa modul, yang masing-masing akan melakukan beberapa tindakan dan hasil dari tindakan ini akan dikirimkan ke modul berikutnya.

Modul


  • Perangkat ini harus dapat menghitung waktu. Untuk menghitung waktu, saya membuat modul "tunda". Modul ini memiliki 1 sinyal masuk dan 1 keluar. Modul menerima sinyal frekuensi FPGA dan, setelah sejumlah periode tertentu dari sinyal yang masuk, mengubah nilai sinyal keluar ke kebalikannya.
  • Perangkat harus membaca dari 0 hingga 9. Modul bcd_counter akan digunakan untuk ini.
  • Untuk menyalakan segmen pada tampilan, Anda perlu mengatur bit yang sesuai dengan segmen ke 0 pada register shift tampilan, dan untuk menghapus segmen menjadi bit, tulis 1 (tampilan saya memiliki logika terbalik). Dekoder bcd_2_7seg akan menangani instalasi dan reset bit yang diinginkan.
  • Modul pemancar akan bertanggung jawab untuk transfer data.

Perangkat host akan mengontrol transmisi sinyal yang benar antar modul, serta menghasilkan sinyal rclk setelah transfer data selesai.

Untuk lebih jelasnya, saya berikan diagram perangkat ini
memesan

Seperti yang dapat Anda lihat dari diagram, perangkat memiliki 1 sinyal masuk (clk) dan 3 sinyal keluar (sclk, dio, rclk). Sinyal clk datang dalam 2 pembagi sinyal (sec_delay dan transfer_delay). Sinyal keluar dengan periode 1s meninggalkan perangkat sec_delay. Di sisi atas sinyal ini, penghitung (bcd_counter1) mulai menghasilkan angka berikutnya untuk ditampilkan. Setelah nomor dihasilkan, decoder (bcd_2_7seg1) mengubah representasi biner dari angka tersebut menjadi segmen yang menyala dan tidak menyala pada tampilan. Yang, menggunakan pemancar (transmitter1), ditransmisikan ke layar. Pemancar diberi clock menggunakan perangkat transfer_delay.

Kode


Untuk membuat perangkat dalam VHDL, konstruksi dua komponen entitas dan arsitektur digunakan. Entitas menyatakan antarmuka untuk bekerja dengan perangkat. Arsitektur menggambarkan logika perangkat.

Inilah bentuk entitas perangkat penundaan
entity delay is --   entity,  generic    generic (delay_cnt: integer); --       port(clk: in std_logic; out_s: out std_logic := '0'); end entity delay; 


Melalui bidang generik, kita dapat mengatur perangkat ke penundaan yang diinginkan. Dan di bidang port kami menggambarkan sinyal masuk dan keluar perangkat.

Arsitektur perangkat penundaan adalah sebagai berikut
 --   architecture  ,     --   entity    0    architecture delay_arch of delay is begin delay_proc: process(clk) variable clk_cnt: integer range 0 to delay_cnt := 0; variable out_v: std_logic := '0'; begin --        if(rising_edge(clk)) then clk_cnt := clk_cnt + 1; if(clk_cnt >= delay_cnt) then -- switch/case   VHDL case out_v is when '0' => out_v := '1'; when others => out_v := '0'; end case; clk_cnt := 0; --    out_s   out_v out_s <= out_v; end if; end if; end process delay_proc; end delay_arch; 


Kode di dalam bagian proses dieksekusi secara berurutan, kode lain dieksekusi secara paralel. Dalam tanda kurung, setelah kata kunci proses, sinyal ditunjukkan, dengan mengubah proses yang akan dimulai (daftar sensivitas).

Perangkat bcd_counter dalam hal logika eksekusi identik dengan perangkat penundaan. Karena itu, saya tidak akan membahasnya secara rinci.

Berikut adalah entitas dan arsitektur dekoder
 entity bcd_to_7seg is port(bcd: in std_logic_vector(3 downto 0) := X"0"; disp_out: out std_logic_vector(7 downto 0) := X"00"); end entity bcd_to_7seg; architecture bcd_to_7seg_arch of bcd_to_7seg is signal not_bcd_s: std_logic_vector(3 downto 0) := X"0"; begin not_bcd_s <= not bcd; disp_out(7) <= (bcd(2) and not_bcd_s(1) and not_bcd_s(0)) or (not_bcd_s(3) and not_bcd_s(2) and not_bcd_s(1) and bcd(0)); disp_out(6) <= (bcd(2) and not_bcd_s(1) and bcd(0)) or (bcd(2) and bcd(1) and not_bcd_s(0)); disp_out(5) <= not_bcd_s(2) and bcd(1) and not_bcd_s(0); disp_out(4) <= (not_bcd_s(3) and not_bcd_s(2) and not_bcd_s(1) and bcd(0)) or (bcd(2) and not_bcd_s(1) and not_bcd_s(0)) or (bcd(2) and bcd(1) and bcd(0)); disp_out(3) <= (bcd(2) and not_bcd_s(1)) or bcd(0); disp_out(2) <= (not_bcd_s(3) and not_bcd_s(2) and bcd(0)) or (not_bcd_s(3) and not_bcd_s(2) and bcd(1)) or (bcd(1) and bcd(0)); disp_out(1) <= (not_bcd_s(3) and not_bcd_s(2) and not_bcd_s(1)) or (bcd(2) and bcd(1) and bcd(0)); disp_out(0) <= '1'; end bcd_to_7seg_arch; 


Semua logika perangkat ini dieksekusi secara paralel. Saya berbicara tentang cara mendapatkan formula untuk perangkat ini di salah satu video di saluran saya. Siapa peduli, di sini ada tautan ke video .

Dalam perangkat pemancar, saya menggabungkan logika serial dan paralel
 entity transmitter is port(enable: in boolean; clk: in std_logic; digit_pos: in std_logic_vector(7 downto 0) := X"00"; digit: in std_logic_vector(7 downto 0) := X"00"; sclk, dio: out std_logic := '0'; ready: buffer boolean := true); end entity transmitter; architecture transmitter_arch of transmitter is constant max_int: integer := 16; begin sclk <= clk when not ready else '0'; send_proc: process(clk, enable, ready) variable dio_cnt_v: integer range 0 to max_int := 0; variable data_v: std_logic_vector((max_int - 1) downto 0); begin --   dio      clk if(falling_edge(clk) and (enable or not ready)) then if(dio_cnt_v = 0) then --    ,     --          data_v := digit_pos & digit; ready <= false; end if; if(dio_cnt_v = max_int) then dio_cnt_v := 0; ready <= true; dio <= '0'; else dio <= data_v(dio_cnt_v); dio_cnt_v := dio_cnt_v + 1; end if; end if; end process send_proc; end transmitter_arch; 


Untuk sinyal sclk, saya mengarahkan ulang nilai sinyal clk yang memasuki pemancar, tetapi hanya jika perangkat saat ini mentransmisikan data (sinyal ready = false). Kalau tidak, nilai sinyal sclk akan menjadi 0. Pada awal transfer data (aktif = sinyal benar), saya menggabungkan data dari dua vektor 8-bit (digit_pos dan digit) memasukkan perangkat ke dalam vektor 16-bit (data_v) dan mengirimkan data dari vektor ini adalah satu bit per jam, menetapkan nilai bit yang ditransmisikan dalam sinyal keluar keluar. Dari hal-hal menarik tentang perangkat ini, saya ingin mencatat bahwa data dalam dio diatur ke trailing edge dari sinyal clk, dan data dari pin dio akan ditulis ke register geser tampilan ketika ujung depan sinyal sclk tiba. Setelah menyelesaikan transmisi, dengan mengatur sinyal <= true yang siap, saya memberi sinyal ke perangkat lain bahwa transmisi telah selesai.

Di sini terlihat entitas dan arsitektur perangkat layar
 entity display is port(clk: in std_logic; sclk, rclk, dio: out std_logic := '0'); end entity display; architecture display_arch of display is component delay is generic (delay_cnt: integer); port(clk: in std_logic; out_s: out std_logic := '0'); end component; component bcd_counter is port(clk: in std_logic; bcd: out std_logic_vector(3 downto 0)); end component; component bcd_to_7seg is port(bcd: in std_logic_vector(3 downto 0); disp_out: out std_logic_vector(7 downto 0)); end component; component transmitter is port(enable: in boolean; clk: in std_logic; digit_pos: in std_logic_vector(7 downto 0); digit: in std_logic_vector(7 downto 0); sclk, dio: out std_logic; ready: buffer boolean); end component; signal sec_s: std_logic := '0'; signal bcd_counter_s: std_logic_vector(3 downto 0) := X"0"; signal disp_out_s: std_logic_vector(7 downto 0) := X"00"; signal tr_enable_s: boolean; signal tr_ready_s: boolean; signal tr_data_s: std_logic_vector(7 downto 0) := X"00"; --  ,   tr_ready_s  --    rclk  signal disp_refresh_s: boolean; signal transfer_clk: std_logic := '0'; begin sec_delay: delay generic map(25_000_000) port map(clk, sec_s); transfer_delay: delay generic map(10) port map(clk, transfer_clk); bcd_counter1: bcd_counter port map(sec_s, bcd_counter_s); bcd_to_7seg1: bcd_to_7seg port map(bcd_counter_s, disp_out_s); transmitter1: transmitter port map(tr_enable_s, transfer_clk, X"10", tr_data_s, sclk, dio, tr_ready_s); tr_proc: process(transfer_clk) variable prev_disp: std_logic_vector(7 downto 0); variable rclk_v: std_logic := '0'; begin if(rising_edge(transfer_clk)) then --         if(tr_ready_s) then --         if(not (prev_disp = disp_out_s)) then prev_disp := disp_out_s; --        tr_data_s <= disp_out_s; --    tr_enable_s <= true; end if; else disp_refresh_s <= true; --       --   , --         tr_enable_s <= false; end if; if(rclk_v = '1') then disp_refresh_s <= false; end if; if(tr_ready_s and disp_refresh_s) then rclk_v := '1'; else rclk_v := '0'; end if; rclk <= rclk_v; end if; end process tr_proc; end display_arch; 


Perangkat ini mengontrol perangkat lain. Di sini, sebelum mendeklarasikan sinyal tambahan, saya menyatakan komponen yang akan saya gunakan. Dalam arsitektur itu sendiri (setelah kata kunci mulai) saya membuat instance perangkat:

  • sec_delay - sebuah instance dari komponen delay. Sinyal keluar diarahkan ke detik.
  • transfer_delay - turunan dari komponen penundaan. Sinyal keluar dikirim ke sinyal transfer_clk.
  • bcd_counter1 - sebuah instance dari komponen bcd_counter. Sinyal keluar diarahkan ke bcd_counter_s.
  • bcd_to_7seg1 - sebuah instance dari komponen bcd_to_7seg. Sinyal keluar dialihkan ke disp_out_s.
  • transmitter1 adalah turunan dari komponen pemancar. Sinyal keluar dikirim ke sinyal sclk, dio, tr_ready_s.

Setelah komponen contoh, proses dinyatakan. Proses ini memecahkan beberapa masalah:

  1. Jika pemancar tidak sibuk, proses menginisialisasi dimulainya transfer data.
      if(tr_ready_s) then if(not (prev_disp = disp_out_s)) then prev_disp := disp_out_s; --     --    tr_data_s <= disp_out_s; --    tr_enable_s <= true; end if; else ... 


  2. Jika pemancar sibuk (tr_ready_s = false), maka proses menetapkan nilai sinyal disp_refresh_s <= true (sinyal ini menunjukkan bahwa setelah transfer selesai, data pada tampilan harus diperbarui). Nilai sinyal tr_enable_s <= false juga ditetapkan, jika ini tidak dilakukan sebelum transmisi selesai, maka data yang diunduh ke pemancar akan dikirim
  3. Mengatur dan mengatur ulang sinyal rclk setelah transfer data selesai
      if(rclk_v = '1') then disp_refresh_s <= false; end if; if(tr_ready_s and disp_refresh_s) then rclk_v := '1'; else rclk_v := '0'; end if; rclk <= rclk_v; 



Bagan waktu


Berikut adalah diagram waktu untuk mentransfer nomor 1 ke posisi pertama tampilan
diagram waktu

Pertama, data "10011111" ditransmisikan. Maka posisi nomor pada tampilan adalah "00010000" (parameter ini tiba di pemancar sebagai konstanta X "10"). Dalam kedua kasus, bit paling kanan (lsb) ditransmisikan pertama kali.

Semua kode dapat dilihat di github . File dengan subscript * _tb.vhd adalah file debug untuk komponen yang sesuai (misalnya, transmitter_tb.vhd adalah file debug untuk pengirim). Untuk berjaga-jaga, saya juga mengunggahnya ke github. Kode ini diunduh dan berfungsi di papan nyata. Siapa peduli, Anda dapat melihat ilustrasi kode di sini (mulai pukul 15:30). Terima kasih atas perhatian anda

Source: https://habr.com/ru/post/id447454/


All Articles