Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'ElbertV.2'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino i ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - roboty
    • Projekty - DIY
    • Projekty - DIY (początkujący)
    • Projekty - w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie

Kategorie

  • Quizy o elektronice
  • Quizy do kursu elektroniki I
  • Quizy do kursu elektroniki II
  • Quizy do kursów Arduino
  • Quizy do kursu STM32L4
  • Quizy do pozostałych kursów

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Imię


Strona

Znaleziono 2 wyniki

  1. Cześć, jestem w trakcie prac nad zbieraniem sampli sygnału audio na zestawie FPGA "Sipeed Tang Nano 4K" i potrzebuje w celu debugowania układu jakiegoś kanału do komunikacji z portem szeregowym, lub Arduino. Chciałem użyć któregoś z IP Cores z "Gowin EDA" ale zarówno UART jak i "SPI Master" są tak przekombinowane, że ich użycie wymaga napisania sporej ilości kodu. Postanowiłem więc użyć zestawu FPGA "Elbert v.2" w celu napisania prostego kodu do komunikacji za pomocą protokołu SPI . Powstał więc bardzo prosty "SPI Master", który jest uruchomiony na zestawie FPGA i połączony za pomocą protokołu SPI z "Arduino UNO", które odbiera dane i wyświetla je za pomocą "Monitora portu szeregowego". Do samego kodu "SPI Master'a" został dołączony "debouncer" do przycisku i generator pojedynczego impulsu (o długości okresu zegara), który inicjuje wysłanie jednego bajtu danych. W celu wygenerowania jednego bardzo krótkiego impulsu napisałem (za pomocą kilku przerzutników typu D) i bramki AND układ generatora monostabilnego. Tutaj wynik symulacji działania generatora monostabilnego: Układ działa w ten sposób, że naciśnięcie przycisku (SW1 z Elbert'a) powoduje wysłanie jednego bajtu danych do Arduino za pomocą SPI. Tak wygląda układ testowy (potrzebny był poza FPGA i Arduino 4-liniowy konwerter poziomów logicznych): Tutaj kod w VHDL głównego modułu (a właściwie entity) projektu - plik "lw_spi_master.vhd ": library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; -- Uncomment the following library declaration if using -- arithmetic functions with Signed or Unsigned values --use IEEE.NUMERIC_STD.ALL; -- Uncomment the following library declaration if instantiating -- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity lw_spi_master is generic ( c_clkfreq : integer := 12_000_000; c_sclkfreq : integer := 5_000_000; c_cpol : std_logic := '0'; c_cpha : std_logic := '0' ); Port ( clk_i : in STD_LOGIC; en_i_Low : in STD_LOGIC; mosi_data_i : in STD_LOGIC_VECTOR (7 downto 0); miso_data_o : out STD_LOGIC_VECTOR (7 downto 0); data_ready_o : out STD_LOGIC; cs_o : out STD_LOGIC; sclk_o : out STD_LOGIC; mosi_o : out STD_LOGIC; miso_i : in STD_LOGIC ); end lw_spi_master; architecture Behavioral of lw_spi_master is component debounce is port ( clk : IN STD_LOGIC; --input clock button : IN STD_LOGIC; --input signal to be debounced result : OUT STD_LOGIC); --debounced signal end component; component monostable_ff is port( mono_pulse : out std_logic; clk : in std_logic; reset: in std_logic; key :in std_logic ); end component; signal write_reg : std_logic_vector (7 downto 0) := (others => '0'); signal read_reg : std_logic_vector (7 downto 0) := (others => '0'); signal en_i : std_logic := '0'; signal sclk_en : std_logic := '0'; signal sclk : std_logic := '0'; signal sclk_prev : std_logic := '0'; signal sclk_rise : std_logic := '0'; signal sclk_fall : std_logic := '0'; signal pol_phase : std_logic_vector (1 downto 0) := (others => '0'); signal mosi_en : std_logic := '0'; signal miso_en : std_logic := '0'; signal en_i_DB : std_logic; signal cnt : std_logic_vector (10 downto 0) := (others => '0'); constant c_edgecntrlimdiv2 : integer := c_clkfreq/(c_sclkfreq*2); signal edgecntr : integer range 0 to c_edgecntrlimdiv2 := 0; signal cntr : integer range 0 to 15 := 0; signal small_cnt : integer range 0 to 1000 := 0; signal blokada : integer range 0 to 3 := 0; type states is (S_IDLE, S_TRANSFER); signal state : states := S_IDLE; -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- begin C1: debounce port map (clk_i,en_i_Low, en_i_DB); C2: monostable_ff port map ( mono_pulse => en_i, clk => clk_i, reset => '1', key => not en_i_DB ); --en_i <= not(en_i_DB); pol_phase <= c_cpol & c_cpha; P_SAMPLE_EN : process (pol_phase, sclk_fall, sclk_rise) begin case pol_phase is when "00" => mosi_en <= sclk_fall; miso_en <= sclk_rise; when "01" => mosi_en <= sclk_rise; miso_en <= sclk_fall; when "10" => mosi_en <= sclk_rise; miso_en <= sclk_fall; when "11" => mosi_en <= sclk_fall; miso_en <= sclk_rise; when others => end case; end process; P_RISEFALL_DETECT : process (sclk, sclk_prev) begin if (sclk = '1' and sclk_prev = '0') then sclk_rise <= '1'; else sclk_rise <= '0'; end if; if (sclk = '0' and sclk_prev = '1') then sclk_fall <= '1'; else sclk_fall <= '0'; end if; end process; P_MAIN : process (clk_i) begin if (rising_edge(clk_i)) then sclk_prev <= sclk; case state is when S_IDLE => cs_o <= '1'; mosi_o <= '0'; data_ready_o <= '0'; sclk_en <= '0'; cntr <= 0; if (c_cpol = '0') then sclk_o <= '0'; else sclk_o <= '1'; end if; if (en_i = '1') then state <= S_TRANSFER; sclk_en <= '1'; write_reg <= mosi_data_i; mosi_o <= mosi_data_i(7); read_reg <= x"00"; end if; when S_TRANSFER => cs_o <= '0'; mosi_o <= write_reg(7); if (c_cpha = '1') then if (cntr = 0) then sclk_o <= sclk; if (miso_en = '1') then read_reg(0) <= miso_i; read_reg(7 downto 1) <= read_reg(6 downto 0); cntr <= cntr + 1; end if; elsif (cntr = 8) then data_ready_o <= '1'; miso_data_o <= read_reg; if (mosi_en = '1') then data_ready_o <= '0'; if (en_i = '1') then write_reg <= mosi_data_i; mosi_o <= mosi_data_i(7); sclk_o <= sclk; cntr <= 0; else state <= S_IDLE; cs_o <= '1'; end if; end if; elsif (cntr = 9) then if (miso_en = '1') then state <= S_IDLE; cs_o <= '1'; end if; else sclk_o <= sclk; if (miso_en = '1') then read_reg(0) <= miso_i; read_reg(7 downto 1) <= read_reg(6 downto 0); cntr <= cntr + 1; end if; if (mosi_en = '1') then mosi_o <= write_reg(7); write_reg(7 downto 1) <= write_reg(6 downto 0); end if; end if; else -- c_cpha = '0' if (cntr = 0) then sclk_o <= sclk; if (miso_en = '1') then read_reg(0) <= miso_i; read_reg(7 downto 1) <= read_reg(6 downto 0); cntr <= cntr + 1; end if; elsif (cntr = 8) then data_ready_o <= '1'; miso_data_o <= read_reg; sclk_o <= sclk; if (mosi_en = '1') then data_ready_o <= '0'; if (en_i = '1') then write_reg <= mosi_data_i; mosi_o <= mosi_data_i(7); cntr <= 0; else cntr <= cntr + 1; end if; if (miso_en = '1') then state <= S_IDLE; cs_o <= '1'; end if; end if; elsif (cntr = 9) then if (miso_en = '1') then state <= S_IDLE; cs_o <= '1'; end if; else sclk_o <= sclk; if (miso_en = '1') then read_reg(0) <= miso_i; read_reg(7 downto 1) <= read_reg(6 downto 0); cntr <= cntr + 1; end if; if (mosi_en = '1') then write_reg(7 downto 1) <= write_reg(6 downto 0); end if; end if; end if; end case; end if; end process; P_SCLK_GEN : process (clk_i) begin if (rising_edge(clk_i)) then if (sclk_en = '1') then if edgecntr = c_edgecntrlimdiv2-1 then sclk <= not sclk; edgecntr <= 0; else edgecntr <= edgecntr + 1; end if; else edgecntr <= 0; if (c_cpol = '0') then sclk <= '0'; else sclk <= '1'; end if; end if; end if; end process; end Behavioral; W tym pliku jest zawarta cała implementacja "SPI Master'a". Widać też użycia komponentów "debouncer'a" i generatora monostabilnego. Tutaj kod "deuboncer'a": LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; ENTITY debounce IS GENERIC( counter_size : INTEGER := 20); --counter size (19 bits gives 10.5ms with 50MHz clock) PORT( clk : IN STD_LOGIC; --input clock button : IN STD_LOGIC; --input signal to be debounced result : OUT STD_LOGIC); --debounced signal END debounce; ARCHITECTURE logic OF debounce IS SIGNAL flipflops : STD_LOGIC_VECTOR(1 DOWNTO 0); --input flip flops SIGNAL counter_set : STD_LOGIC; --sync reset to zero SIGNAL counter_out : STD_LOGIC_VECTOR(counter_size DOWNTO 0) := (OTHERS => '0'); --counter output BEGIN counter_set <= flipflops(0) xor flipflops(1); --determine when to start/reset counter PROCESS(clk) BEGIN IF(clk'EVENT and clk = '1') THEN flipflops(0) <= button; flipflops(1) <= flipflops(0); If(counter_set = '1') THEN --reset counter because input is changing counter_out <= (OTHERS => '0'); ELSIF(counter_out(counter_size) = '0') THEN --stable input time is not yet met counter_out <= counter_out + 1; ELSE --stable input time is met result <= flipflops(1); END IF; END IF; END PROCESS; END logic; Tutaj kod VHDL generatora krótkiego impulsu "monostable_ff.vhd": library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity monostable_ff is port( mono_pulse : out std_logic; clk : in std_logic; reset: in std_logic; key :in std_logic ); end monostable_ff; architecture Behavioral of monostable_ff is component DFF_AsyncReset is port( Q : out std_logic; QNot : out std_logic; clk : in std_logic; reset: in std_logic; D :in std_logic ); end component; component nand2 is port(a,b : in std_logic; y : out std_logic); end component; signal Q1,Q2,Q3,Q4,Q5 : std_logic; signal Q1N,Q2N,Q3N,Q4N,Q5N : std_logic; signal s11 : std_logic; begin DFF1: DFF_AsyncReset port map ( Q => Q1, QNot => Q1N, clk => clk, reset => reset, D => key ); DFF2: DFF_AsyncReset port map ( Q => Q2, QNot => Q2N, clk => clk, reset => reset, D => Q1 ); DFF3: DFF_AsyncReset port map ( Q => Q3, QNot => Q3N, clk => clk, reset => reset, D => Q2 ); DFF4: DFF_AsyncReset port map ( Q => Q4, QNot => Q4N, clk => clk, reset => reset, D => Q3 ); DFF5: DFF_AsyncReset port map ( Q => Q5, QNot => Q5N, clk => clk, reset => reset, D => Q4 ); mono_pulse <= (Q1 and Q5N); end Behavioral; Układ jest zrobiony na "liniI opóźniającej" z kilku przerzutników typu D i dwu-wejściowej bramki "AND" (kiedyś w mojej przeszłości budowało się podobne układy na układach scalonych z serii TTL). Do budowy tego generatora monostabilnego użyłem przerzutników typu D (wejście zegarowe reaguje na zbocze narastające) z asynchronicznym resetem stanem niskim. Tutaj kod przerzutnika typu D ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; -- Uncomment the following library declaration if using -- arithmetic functions with Signed or Unsigned values --use IEEE.NUMERIC_STD.ALL; -- Uncomment the following library declaration if instantiating -- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity DFF_AsyncReset is port( Q : out std_logic; QNot : out std_logic; clk : in std_logic; reset: in std_logic; D :in std_logic ); end DFF_AsyncReset; architecture Behavioral of DFF_AsyncReset is begin process(clk,reset) begin if(reset='0') then Q <= '0'; QNot <= '1'; elsif(rising_edge(clk)) then Q <= D; QNot <= (not D); end if; end process; end Behavioral; A tutaj plik "user constraints" "lw-spi_master.ucf" dla zestawu Elbert v.2: CONFIG VCCAUX = "3.3" ; # Clock 12 MHz NET "clk_i" LOC = P129 | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz; NET "en_i_Low" LOC = P80 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; #SW1 # mosi_data_i : in STD_LOGIC_VECTOR (7 downto 0); DIPSwitch NET "mosi_data_i[7]" LOC = P70 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[6]" LOC = P69 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[5]" LOC = P68 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[4]" LOC = P64 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[3]" LOC = P63 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[2]" LOC = P60 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[1]" LOC = P59 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "mosi_data_i[0]" LOC = P58 | PULLUP | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; # miso_data_o : out STD_LOGIC_VECTOR (7 downto 0); LEDs NET "miso_data_o[7]" LOC = P46 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[6]" LOC = P47 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[5]" LOC = P48 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[4]" LOC = P49 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[3]" LOC = P50 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[2]" LOC = P51 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[1]" LOC = P54 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "miso_data_o[0]" LOC = P55 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; #################################################################################################### # HEADER P1 #################################################################################################### # sclk_o : out STD_LOGIC; NET "sclk_o" LOC = P31 | IOSTANDARD = LVCMOS33 | SLEW = FAST | DRIVE = 12; #1 # mosi_o : out STD_LOGIC; NET "mosi_o" LOC = P32 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; #2 # miso_i : in STD_LOGIC NET "miso_i" LOC = P28 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; #3 # cs_o : out STD_LOGIC; NET "cs_o" LOC = P30 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; #4 # data_ready_o : out STD_LOGIC; NET "data_ready_o" LOC = P27 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; #5 #Port ( # clk_i : in STD_LOGIC; # en_i_Low : in STD_LOGIC; # mosi_data_i : in STD_LOGIC_VECTOR (7 downto 0); # miso_data_o : out STD_LOGIC_VECTOR (7 downto 0); # data_ready_o : out STD_LOGIC; # cs_o : out STD_LOGIC; # sclk_o : out STD_LOGIC; # mosi_o : out STD_LOGIC; # miso_i : in STD_LOGIC #); Jak widać w pliku dane (bajt), które są wysyłane magistralą SPI do Arduino UNO, są podłączone do 8-miu DIP Switchy z zestawu Elbert, a przycisk "SW1" powoduje jednorazowa wysyłkę bajtu danych. Tutaj zrzut ekranu z "monitora portu szeregowego" z ARDUINO IDE z programem do odbioru danych po SPI.: Na koniec wklejam spakowany zip'em projekt dla Xilinx "ISE 14.7". Implementacja całego projektu zajmuje poniżej 100 LUT. Wklejam opis projektu bo takie funkcje komunikacyjne przydają się w przyszłości do bardziej skomplikowanych projektów. Teraz pozostaje uruchomienie kodu na zestawie FPGA "Sipeed Tang nano 4K". LW_SPI_master_01.zip Pozdrawiam
  2. Cześć, ponieważ I2C jest jednym z najprostszych szeregowych protokołów komunikacyjnych (i jest bardzo często stosowany) postanowiłem sprawdzić jak wygląda przykładowa implementacja na zestawie FPGA. Zacząłem od prostszej rzeczy, czyli implementacji "I2C slave" (poniekąd też slave jest mi bardziej przydatny do moich eksperymentów). Ponieważ projekt jest stosunkowo prosty i nie zajmuje dużo zasobów postanowiłem go przetestować na zestawie FPGA ElbertV.2 (ten używany w kursie FPGA Forbot'a). Masterem I2C będzie zwykłe "Arduino UNO" - z poziomu Arduino bardzo prosto można oprogramować komunikację po I2C z użyciem biblioteki Wire. Punktem wyjścia do implementacji "I2C Slave" jest darmowy projekt zamieszczony na stronie opencores.org. Tutaj link do projektu: https://opencores.org/projects/i2cslave Projekt ma stosunkowo prostą budowę i jest właściwie tylko szkieletem do budowy na jego podstawie bardziej skomplikowanych układów (będzie to wyjaśnione dalej). Jak przeważnie się to zdarza trzeba było dodać nowy zegar (DCMs 1 ze Spartan3A) 48 MHz (pętla PLL), ponieważ oryginalny projekt pracował z takim zegarem. Po wykonaniu potrzebnych przeróbek projekt zajmuje niecałe 30% zasobów układu Spartan3 z Elberta - patrz screen: Jak można zobaczyć na podglądzie RTL - top entity projektu ma bardzo prosty interfejs - patrz screen: Tutaj kod w Verilog'u głównego modułu projektu (już po dodaniu zegara PLL 48 MHz) `include "i2cSlave_define.v" module i2cSlaveTop ( clk, rst, sda, scl, myReg0 ); input clk; input rst; inout sda; input scl; output [7:0] myReg0; wire clk48Mhz, clkBufg; // Instantiate the module PLL clock PLL_clock PLL_48Mhz ( .CLKIN_IN(clk), .RST_IN(~rst), .CLKFX_OUT(clk48Mhz), .CLKIN_IBUFG_OUT(clkBufg) ); i2cSlave u_i2cSlave( .clk(clk48Mhz), .rst(~rst), .sda(sda), .scl(scl), .myReg0(myReg0), .myReg1(), .myReg2(), .myReg3(), .myReg4(8'h12), .myReg5(8'h34), .myReg6(8'h56), .myReg7(8'h78) ); endmodule Jak widać "i2cSlaveTop" ma trzy wejścia: clk - główny zegar projektu (w Elbercie 12 Mhz) rst - reset asynchroniczny - stanem High więc dla naszej płytki FPGA trzeba go było zanegować scl - zegar magistrali I2c , jedno wejście/wyjście (inout) sda - dane magistrali I2C i jedno wyjście (wektor 8 bitów) myReg0 - na tym wyjściu równoległym będą się pojawiać dane wysłane przez mastera I2C (Arduino UNO), dlatego podłączymy je do diod LED w zestawie Elbert, aby móc te dane łatwo obserwować. Tutaj treść pliku ucf (user constraints) dla płytki FPGA Elbert: NET "clk" LOC = P129 | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz; NET "rst" PULLUP; NET "rst" LOC = P76; NET "sda" LOC = P31 | IOSTANDARD = LVCMOS33 | SLEW = FAST | DRIVE = 12; NET "scl" LOC = P32 | IOSTANDARD = LVCMOS33 | SLEW = FAST | DRIVE = 12; # myReg0 - 8 bit data NET "myReg0[7]" LOC = P46 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[6]" LOC = P47 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[5]" LOC = P48 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[4]" LOC = P49 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[3]" LOC = P50 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[2]" LOC = P51 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[1]" LOC = P54 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; NET "myReg0[0]" LOC = P55 | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12; Jak widać zdefiniowany jest domyślny pin zegara (12 MHz), reset - dolny z pięciu switchy, diody LED (myReg0 - dane przesłane po I2C do płytki Elbert), scl i sda 2 piny magistarli I2C (pierwsze dwa piny złącza P1 na płytce Elbert - patrz rysunek: Jeśli chodzi o piny magistrali I2C (scl, sda) od strony Arduino UNO to zaznaczyłem je fioletową obwódką na rysunku pinout'u dla UNO: Oczywiście pomiędzy zestawem FPGA (poziom logiki 3,3V) a Arduini UNO (poziom logiki 5V) trzeba wstawić na tych dwóch liniach (scl, sda) konwertery poziomów logicznych.Ja zrobiłem to za pomocą takiego konwertera poziomów (bo tylko taki miałem pod ręką) https://botland.com.pl/pl/konwertery-napiec/2523-konwerter-poziomow-logicznych-dwukierunkowy-4-kanalowy-pololu.html Tutaj zdjęcie całego układu (konwerter poziomów na płytce stykowej): Skoro połączenia mamy omówione wróćmy może do kodu projektu (Verilog). Przejdżmy do pliku "i2cSlave_define.v" plik ten jest dołączany (dyrektywa include) za pomocą linii: `include "i2cSlave_define.v" do wszystkich plików żródłowych projektu (Verilog) podobnie jak w języku C. W pliku tym mamy zdefiniowane bardzo ważne parametry modulów - patrz źródła: // ----------------------- i2cSlave_define.v -------------------- // stream states `define STREAM_IDLE 2'b00 `define STREAM_READ 2'b01 `define STREAM_WRITE_ADDR 2'b10 `define STREAM_WRITE_DATA 2'b11 // start stop detection states `define NULL_DET 2'b00 `define START_DET 2'b01 `define STOP_DET 2'b10 // i2c ack and nak `define I2C_NAK 1'b1 `define I2C_ACK 1'b0 // ---------------------------------------------------------------- // ------------- modify constants below this line ----------------- // ---------------------------------------------------------------- // i2c device address `define I2C_ADDRESS 7'h3c // System clock frequency in MHz // If you are using a clock frequency below 24MHz, then the macro // for SDA_DEL_LEN will result in compile errors for i2cSlave.v // you will need to hand tweak the SDA_DEL_LEN constant definition `define CLK_FREQ 48 // Debounce SCL and SDA over this many clock ticks // The rise time of SCL and SDA can be up to 1000nS (in standard mode) // so it is essential to debounce the inputs. // The spec requires 0.05V of hysteresis, but in practise // simply debouncing the inputs is sufficient // I2C spec requires suppresion of spikes of // maximum duration 50nS, so this debounce time should be greater than 50nS // Also increases data hold time and decreases data setup time // during an I2C read operation // 10 ticks = 208nS @ 48MHz `define DEB_I2C_LEN (10*`CLK_FREQ)/48 // Delay SCL for use as internal sampling clock // Using delayed version of SCL to ensure that // SDA is stable when it is sampled. // Not entirely citical, as according to I2C spec // SDA should have a minimum of 100nS of set up time // with respect to SCL rising edge. But with the very slow edge // speeds used in I2C it is better to err on the side of caution. // This delay also has the effect of adding extra hold time to the data // with respect to SCL falling edge. I2C spec requires 0nS of data hold time. // 10 ticks = 208nS @ 48MHz `define SCL_DEL_LEN (10*`CLK_FREQ)/48 // Delay SDA for use in start/stop detection // Use delayed SDA during start/stop detection to avoid // incorrect detection at SCL falling edge. // From I2C spec start/stop setup is 600nS with respect to SCL rising edge // and start/stop hold is 600nS wrt SCL falling edge. // So it is relatively easy to discriminate start/stop, // but data setup time is a minimum of 100nS with respect to SCL rising edge // and 0nS hold wrt to SCL falling edge. // So the tricky part is providing robust start/stop detection // in the presence of regular data transitions. // This delay time should be less than 100nS // 4 ticks = 83nS @ 48MHz `define SDA_DEL_LEN (4*`CLK_FREQ)/48 Dla nas najważniejsze są dwa parametry: `define I2C_ADDRESS 7'h3c Pierwszy to adres naszego slave'a w magistrali I2C (jest dostepnych 128 różnych adresów) - wynosi on 3C zapisany heksadecymalnie. Drugi to częstotliwość zegara - 48 MHz: `define CLK_FREQ 48 Teraz drugi plik "registerInterface": `include "i2cSlave_define.v" module registerInterface ( clk, addr, dataIn, writeEn, dataOut, myReg0, myReg1, myReg2, myReg3, myReg4, myReg5, myReg6, myReg7 ); input clk; input [7:0] addr; input [7:0] dataIn; input writeEn; output [7:0] dataOut; output [7:0] myReg0; output [7:0] myReg1; output [7:0] myReg2; output [7:0] myReg3; input [7:0] myReg4; input [7:0] myReg5; input [7:0] myReg6; input [7:0] myReg7; reg [7:0] dataOut; reg [7:0] myReg0; reg [7:0] myReg1; reg [7:0] myReg2; reg [7:0] myReg3; // --- I2C Read always @(posedge clk) begin case (addr) 8'h00: dataOut <= myReg0; 8'h01: dataOut <= myReg1; 8'h02: dataOut <= myReg2; 8'h03: dataOut <= myReg3; 8'h04: dataOut <= myReg4; 8'h05: dataOut <= myReg5; 8'h06: dataOut <= myReg6; 8'h07: dataOut <= myReg7; default: dataOut <= 8'h00; endcase end // --- I2C Write always @(posedge clk) begin if (writeEn == 1'b1) begin case (addr) 8'h00: myReg0 <= dataIn; 8'h01: myReg1 <= dataIn; 8'h02: myReg2 <= dataIn; 8'h03: myReg3 <= dataIn; endcase end end endmodule Plik ten jest o tyle ważny, że widać w nim iż w projekcie jest używanych 8 rejestrów 8-mio bitowych (cztery wejściwe i cztery wyjściowe). Ale tylko jeden rejestr jest w głównym module (implementacja jest trochę niepełna - o czym autor pisze, że jest to punkt wyjścia do własnych implementacji). Dla nas jest jedynie istotne, że dla adresu 8'h00 (czyli 0 dziesiętnie rejestr myReg0 jest obsługiwany poprawnie), będzie to istotne przy pisaniu mastera I2C na Arduino UNO. Tutaj zamieszczam działający projekt " I2C slave" (dla Elbert'a) - Xilinx ISE 14.7: I2CSlave01.zip Jeśli ktoś nie chce uruchamiać syntezy może od razu załadować za pomocą programu "Elbert V2 Configurator" plik "i2cslavetop.bin" do zestawu FPGA. Po wgraniu do zestawu FPGA Elbert pliku konfiguracji i wykonaniu połączeń z Arduino UNO możemy przejść do programu dla Arduino. Najpierw warto programem "I2C scanner" sprawdzić, czy Arduino z załadowanym programem skanera wykryje nam slave'a i2C pod adresem 0x3C. Tutaj kod progrmu "I2C scanner" - program należy skompilować za pomocą "Arduino IDE" i wgrać do płytki Arduino UNO. Kod programu: #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); while (!Serial); // Leonardo: wait for serial monitor Serial.println("\nI2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !"); nDevices++; } else if (error==4) { Serial.print("Unknown error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n"); delay(5000); // wait 5 seconds for next scan } Tak wygląda prawidłowe wykrycie slave'a (FPGA) pod adresem 0x3C na magistrali I2C przez Arduino: Jeśli to zadziała, to możemy wgrać taki krótki program mastera I2C na Arduino UNO: #include <Wire.h> void setup() { Wire.begin(); // join i2c bus (address optional for master) } byte x = 0; void loop() { Wire.beginTransmission(0x3C); // transmit to device #3C Wire.write(0); //wysylamy adres rejestru 0 (ten odczytany w kodzie Verilog) Wire.write(x); // wysylamy jeden bajt - wartosc x Wire.endTransmission(); // stop transmitting x++; delay(500); } Jeśli wszystko poszło OK, możemy obserwować na diodach zestawu Elbert zmienijącą się wartość x (inkrementowaną co jeden obieg pętli głównej programu) przesyłaną z mastera (Arduino) do slave'a (FPGA). Patrz filmik (sorry nie mogę przesłać na forbot pliku mp4 z filmem). Pozdrawiam
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.