Piping OV7670 video to VGA output on ZYBO

轉自:http://lauri.xn–vsandi-pxa.com/hdl/zynq/zybo-ov7670-to-vga.html


Piping OV7670 video to VGA output on ZYBO10. Nov ‘14


Introduction

Before getting into more complex topics such as AXI Stream and direct memory access, it’s recommended to first get familiar with pixel data encoding schemes and video timing signals. Hamsterworks has great examples for Zynq boards [1]. In this example VGA frames are grabbed from OV7670 chipset based camera and stored in Block RAM based framebuffer.
這裏寫圖片描述
Omnivision OV7670 is a cheap 640x480 30fps camera module.

ZYBO is however more resource constrained so several modifications were required. In this case we’re reducing the vertical resolution twofold since ZYBO does not have enough Block RAM to contain whole VGA frame. The example is basically working on ZYBO, but there are still few bugs that need to be ironed out.

[1] http://hamsterworks.co.nz/mediawiki/index.php/OV7670_camera


Capture block

The capture block parses VSYNC and HREF signals and converts them into block RAM address. Pixel data is also a bit tricky - OV7670 transmits half of an 16-bit RGB (5:6:5) pixel during one PCLK cycle. Capture block latches the previous half and combines two halves into 12-bit RGB (4:4:4) pixel which is stored in block RAM. You are encouraged to use logic analyzer to debug video timing signals, as connecting wires to VGA output while display is connected is troublesome you might have to route extra pins onto Pmod connectors for debugging purposes.

----------------------------------------------------------------------------------
-- Engineer: Mike Field <[email protected]>
--
-- Description: Captures the pixels coming from the OV7670 camera and
--              Stores them in block RAM
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.ALL;
use ieee.NUMERIC_STD.ALL;

entity ov7670_capture is
    port (
        pclk  : in   std_logic;
        vsync : in   std_logic;
        href  : in   std_logic;
        d     : in   std_logic_vector ( 7 downto 0);
        addr  : out  std_logic_vector (17 downto 0);
        dout  : out  std_logic_vector (11 downto 0);
        we    : out  std_logic
    );
end ov7670_capture;

architecture behavioral of ov7670_capture is
   signal d_latch      : std_logic_vector(15 downto 0) := (others => '0');
   signal address      : std_logic_vector(18 downto 0) := (others => '0');
   signal address_next : std_logic_vector(18 downto 0) := (others => '0');
   signal wr_hold      : std_logic_vector( 1 downto 0)  := (others => '0');

begin
   addr <= address(18 downto 1);
   process(pclk)
   begin
      if rising_edge(pclk) then
         -- This is a bit tricky href starts a pixel transfer that takes 3 cycles
         --        Input   | state after clock tick
         --         href   | wr_hold    d_latch           d                 we address  address_next
         -- cycle -1  x    |    xx      xxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxx  x   xxxx     xxxx
         -- cycle 0   1    |    x1      xxxxxxxxRRRRRGGG  xxxxxxxxxxxxxxxx  x   xxxx     addr
         -- cycle 1   0    |    10      RRRRRGGGGGGBBBBB  xxxxxxxxRRRRRGGG  x   addr     addr
         -- cycle 2   x    |    0x      GGGBBBBBxxxxxxxx  RRRRRGGGGGGBBBBB  1   addr     addr+1

         if vsync = '1' then
            address <= (others => '0');
            address_next <= (others => '0');
            wr_hold <= (others => '0');
         else
            -- This should be a different order, but seems to be GRB!
            dout    <= d_latch(15 downto 12) & d_latch(10 downto 7) & d_latch(4 downto 1);
            address <= address_next;
            we      <= wr_hold(1);
            wr_hold <= wr_hold(0) & (href and not wr_hold(0));
            d_latch <= d_latch( 7 downto  0) & d;

            if wr_hold(1) = '1' then
               address_next <= std_logic_vector(unsigned(address_next)+1);
            end if;
         end if;
      end if;
   end process;
end behavioral;

Video output block

VGA output block generates HSYNC and VSYNC signals for the video outputs and corresponding input for the read address.

----------------------------------------------------------------------------------
-- Engineer: Mike Field <[email protected]>
--
-- Description: Generate analog 640x480 VGA, double-doublescanned from 19200 bytes of RAM
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity ov7670_vga is
    port (
        clk25       : in  STD_LOGIC;
        vga_red     : out STD_LOGIC_VECTOR(4 downto 0);
        vga_green   : out STD_LOGIC_VECTOR(5 downto 0);
        vga_blue    : out STD_LOGIC_VECTOR(4 downto 0);
        vga_hsync   : out STD_LOGIC;
        vga_vsync   : out STD_LOGIC;
        frame_addr  : out STD_LOGIC_VECTOR(17 downto 0);
        frame_pixel : in  STD_LOGIC_VECTOR(11 downto 0)
    );
end ov7670_vga;

architecture Behavioral of ov7670_vga is
   -- Timing constants
   constant hRez       : natural := 640;
   constant hStartSync : natural := 640+16;
   constant hEndSync   : natural := 640+16+96;
   constant hMaxCount  : natural := 800;

   constant vRez       : natural := 480;
   constant vStartSync : natural := 480+10;
   constant vEndSync   : natural := 480+10+2;
   constant vMaxCount  : natural := 480+10+2+33;

   constant hsync_active : std_logic := '0';
   constant vsync_active : std_logic := '0';

   signal hCounter : unsigned( 9 downto 0) := (others => '0');
   signal vCounter : unsigned( 9 downto 0) := (others => '0');
   signal address  : unsigned(18 downto 0) := (others => '0');
   signal blank    : std_logic := '1';

begin
   frame_addr <= std_logic_vector(address(18 downto 1));

   process(clk25)
   begin
      if rising_edge(clk25) then
         -- Count the lines and rows
         if hCounter = hMaxCount-1 then
            hCounter <= (others => '0');
            if vCounter = vMaxCount-1 then
               vCounter <= (others => '0');
            else
               vCounter <= vCounter+1;
            end if;
         else
            hCounter <= hCounter+1;
         end if;

         if blank = '0' then
            vga_red   <= frame_pixel(11 downto 8) & "0";
            vga_green <= frame_pixel( 7 downto 4) & "00";
            vga_blue  <= frame_pixel( 3 downto 0) & "0";
         else
            vga_red   <= (others => '0');
            vga_green <= (others => '0');
            vga_blue  <= (others => '0');
         end if;

         if vCounter  >= vRez then
            address <= (others => '0');
            blank <= '1';
         else
            if hCounter  < 640 then
               blank <= '0';
               address <= address+1;
            else
               blank <= '1';
            end if;
         end if;

         -- Are we in the hSync pulse? (one has been added to include frame_buffer_latency)
         if hCounter > hStartSync and hCounter <= hEndSync then
            vga_hSync <= hsync_active;
         else
            vga_hSync <= not hsync_active;
         end if;

         -- Are we in the vSync pulse?
         if vCounter >= vStartSync and vCounter < vEndSync then
            vga_vSync <= vsync_active;
         else
            vga_vSync <= not vsync_active;
         end if;
      end if;
   end process;
end Behavioral;

It essentially plays the role of video card in a PC.


Controller block

Omnivision OV7670 uses Omnivision Serial Camera Control Bus (SCCB) protocol to set up the camera parameters. SCCB actually is I²C-compliant interface, but avoids the usage of I²C brand due to licensing fees [2]. The controller component is composed of three components: I²C bus master, OV7670 instructions and glue code.

The first one is used to emulate I²C bus master:

----------------------------------------------------------------------------------
-- Engineer: <[email protected]
--
-- Description: Send the commands to the OV7670 over an I2C-like interface
----------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity i2c_sender is
    port (
        clk   : in    std_logic;
        siod  : inout std_logic;
        sioc  : out   std_logic;
        taken : out   std_logic;
        send  : in    std_logic;
        id    : in    std_logic_vector(7 downto 0);
        reg   : in    std_logic_vector(7 downto 0);
        value : in    std_logic_vector(7 downto 0)
    );
end i2c_sender;

architecture behavioral of i2c_sender is
    -- this value gives a 254 cycle pause before the initial frame is sent
    signal   divider  : unsigned (7 downto 0) := "00000001";
    signal   busy_sr  : std_logic_vector(31 downto 0) := (others => '0');
    signal   data_sr  : std_logic_vector(31 downto 0) := (others => '1');
begin
    process(busy_sr, data_sr(31))
    begin
        if busy_sr(11 downto 10) = "10" or
           busy_sr(20 downto 19) = "10" or
           busy_sr(29 downto 28) = "10"  then
            siod <= 'Z';
        else
            siod <= data_sr(31);
        end if;
    end process;

    process(clk)
    begin
        if rising_edge(clk) then
            taken <= '0';
            if busy_sr(31) = '0' then
                SIOC <= '1';
                if send = '1' then
                    if divider = "00000000" then
                        data_sr <= "100" &   id & '0'  &   reg & '0' & value & '0' & "01";
                        busy_sr <= "111" & "111111111" & "111111111" & "111111111" & "11";
                        taken <= '1';
                    else
                        divider <= divider+1; -- this only happens on powerup
                    end if;
                end if;
            else

                case busy_sr(32-1 downto 32-3) & busy_sr(2 downto 0) is
                    when "111"&"111" => -- start seq #1
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '1';
                            when "01"   => SIOC <= '1';
                            when "10"   => SIOC <= '1';
                            when others => SIOC <= '1';
                        end case;
                    when "111"&"110" => -- start seq #2
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '1';
                            when "01"   => SIOC <= '1';
                            when "10"   => SIOC <= '1';
                            when others => SIOC <= '1';
                        end case;
                    when "111"&"100" => -- start seq #3
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '0';
                            when "01"   => SIOC <= '0';
                            when "10"   => SIOC <= '0';
                            when others => SIOC <= '0';
                        end case;
                    when "110"&"000" => -- end seq #1
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '0';
                            when "01"   => SIOC <= '1';
                            when "10"   => SIOC <= '1';
                            when others => SIOC <= '1';
                        end case;
                    when "100"&"000" => -- end seq #2
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '1';
                            when "01"   => SIOC <= '1';
                            when "10"   => SIOC <= '1';
                            when others => SIOC <= '1';
                        end case;
                    when "000"&"000" => -- Idle
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '1';
                            when "01"   => SIOC <= '1';
                            when "10"   => SIOC <= '1';
                            when others => SIOC <= '1';
                        end case;
                    when others      =>
                        case divider(7 downto 6) is
                            when "00"   => SIOC <= '0';
                            when "01"   => SIOC <= '1';
                            when "10"   => SIOC <= '1';
                            when others => SIOC <= '0';
                        end case;
                end case;

                if divider = "11111111" then
                    busy_sr <= busy_sr(32-2 downto 0) & '0';
                    data_sr <= data_sr(32-2 downto 0) & '1';
                    divider <= (others => '0');
                else
                    divider <= divider+1;
                end if;
            end if;
        end if;
    end process;
end behavioral;

The second one contains OV7670 setup instructions:

-- Company:
-- Engineer: Mike Field <hamster@sanp.net.nz>
--
-- Description: Register settings for the OV7670 Caamera (partially from OV7670.c
--              in the Linux Kernel
-- Edited by : Christopher Wilson <wilson@chrec.org>
------------------------------------------------------------------------------------
--
-- Notes:
-- 1) Regarding the WITH SELECT Statement:
--      WITH sreg(sel) SELECT
--           finished    <= '1' when x"FFFF",
--                        '0' when others;
-- This means the transfer is finished the first time sreg ends up as "FFFF",
-- I.E. Need Sequential Addresses in the below case statements
--
-- Common Debug Issues:
--
-- Red Appearing as Green / Green Appearing as Pink
-- Solution: Register Corrections Below
--
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity ov7670_registers is
    Port ( clk      : in  STD_LOGIC;
           resend   : in  STD_LOGIC;
           advance  : in  STD_LOGIC;
           command  : out  std_logic_vector(15 downto 0);
           finished : out  STD_LOGIC);
end ov7670_registers;

architecture Behavioral of ov7670_registers is
   signal sreg   : std_logic_vector(15 downto 0);
   signal address : std_logic_vector(7 downto 0) := (others => '0');
begin
   command <= sreg;
   with sreg select finished  <= '1' when x"FFFF", '0' when others;

   process(clk)
   begin
      if rising_edge(clk) then
         if resend = '1' then
            address <= (others => '0');
         elsif advance = '1' then
            address <= std_logic_vector(unsigned(address)+1);
         end if;

         case address is
            when x"00" => sreg <= x"1280"; -- COM7   Reset
            when x"01" => sreg <= x"1280"; -- COM7   Reset
            when x"02" => sreg <= x"1204"; -- COM7   Size & RGB output
            when x"03" => sreg <= x"1100"; -- CLKRC  Prescaler - Fin/(1+1)
            when x"04" => sreg <= x"0C00"; -- COM3   Lots of stuff, enable scaling, all others off
            when x"05" => sreg <= x"3E00"; -- COM14  PCLK scaling off

            when x"06" => sreg <= x"8C00"; -- RGB444 Set RGB format
            when x"07" => sreg <= x"0400"; -- COM1   no CCIR601
             when x"08" => sreg <= x"4010"; -- COM15  Full 0-255 output, RGB 565
            when x"09" => sreg <= x"3a04"; -- TSLB   Set UV ordering,  do not auto-reset window
            when x"0A" => sreg <= x"1438"; -- COM9  - AGC Celling
            when x"0B" => sreg <= x"4f40"; --x"4fb3"; -- MTX1  - colour conversion matrix
            when x"0C" => sreg <= x"5034"; --x"50b3"; -- MTX2  - colour conversion matrix
            when x"0D" => sreg <= x"510C"; --x"5100"; -- MTX3  - colour conversion matrix
            when x"0E" => sreg <= x"5217"; --x"523d"; -- MTX4  - colour conversion matrix
            when x"0F" => sreg <= x"5329"; --x"53a7"; -- MTX5  - colour conversion matrix
            when x"10" => sreg <= x"5440"; --x"54e4"; -- MTX6  - colour conversion matrix
            when x"11" => sreg <= x"581e"; --x"589e"; -- MTXS  - Matrix sign and auto contrast
            when x"12" => sreg <= x"3dc0"; -- COM13 - Turn on GAMMA and UV Auto adjust
            when x"13" => sreg <= x"1100"; -- CLKRC  Prescaler - Fin/(1+1)
            when x"14" => sreg <= x"1711"; -- HSTART HREF start (high 8 bits)
            when x"15" => sreg <= x"1861"; -- HSTOP  HREF stop (high 8 bits)
            when x"16" => sreg <= x"32A4"; -- HREF   Edge offset and low 3 bits of HSTART and HSTOP
            when x"17" => sreg <= x"1903"; -- VSTART VSYNC start (high 8 bits)
            when x"18" => sreg <= x"1A7b"; -- VSTOP  VSYNC stop (high 8 bits)
            when x"19" => sreg <= x"030a"; -- VREF   VSYNC low two bits
            when x"1A" => sreg <= x"0e61"; -- COM5(0x0E) 0x61
            when x"1B" => sreg <= x"0f4b"; -- COM6(0x0F) 0x4B
            when x"1C" => sreg <= x"1602"; --
            when x"1D" => sreg <= x"1e37"; -- MVFP (0x1E) 0x07  -- FLIP AND MIRROR IMAGE 0x3x
            when x"1E" => sreg <= x"2102";
            when x"1F" => sreg <= x"2291";
            when x"20" => sreg <= x"2907";
            when x"21" => sreg <= x"330b";
            when x"22" => sreg <= x"350b";
            when x"23" => sreg <= x"371d";
            when x"24" => sreg <= x"3871";
            when x"25" => sreg <= x"392a";
            when x"26" => sreg <= x"3c78"; -- COM12 (0x3C) 0x78
            when x"27" => sreg <= x"4d40";
            when x"28" => sreg <= x"4e20";
            when x"29" => sreg <= x"6900"; -- GFIX (0x69) 0x00
            when x"2A" => sreg <= x"6b4a";
            when x"2B" => sreg <= x"7410";
            when x"2C" => sreg <= x"8d4f";
            when x"2D" => sreg <= x"8e00";
            when x"2E" => sreg <= x"8f00";
            when x"2F" => sreg <= x"9000";
            when x"30" => sreg <= x"9100";
            when x"31" => sreg <= x"9600";
            when x"32" => sreg <= x"9a00";
            when x"33" => sreg <= x"b084";
            when x"34" => sreg <= x"b10c";
            when x"35" => sreg <= x"b20e";
            when x"36" => sreg <= x"b382";
            when x"37" => sreg <= x"b80a";
            when others => sreg <= x"ffff";
         end case;
      end if;
   end process;
end Behavioral;

Third one contains glue code for the IP core that can actually be instantiated:

----------------------------------------------------------------------------------
-- Engineer: Mike Field <[email protected]>
--
-- Description: Controller for the OV760 camera - transferes registers to the
--              camera over an I2C like bus
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity ov7670_controller is
    Port ( clk   : in    STD_LOGIC;
                          resend :in    STD_LOGIC;
                          config_finished : out std_logic;
           sioc  : out   STD_LOGIC;
           siod  : inout STD_LOGIC;
           reset : out   STD_LOGIC;
           pwdn  : out   STD_LOGIC;
                          xclk  : out   STD_LOGIC
);
end ov7670_controller;

architecture Behavioral of ov7670_controller is
        COMPONENT ov7670_registers
        PORT(
                clk      : IN std_logic;
                advance  : IN std_logic;
                resend   : in STD_LOGIC;
                command  : OUT std_logic_vector(15 downto 0);
                finished : OUT std_logic
                );
        END COMPONENT;

        COMPONENT i2c_sender
        PORT(
                clk   : IN std_logic;
                send  : IN std_logic;
                taken : out std_logic;
                id    : IN std_logic_vector(7 downto 0);
                reg   : IN std_logic_vector(7 downto 0);
                value : IN std_logic_vector(7 downto 0);
                siod  : INOUT std_logic;
                sioc  : OUT std_logic
                );
        END COMPONENT;

        signal sys_clk  : std_logic := '0';
        signal command  : std_logic_vector(15 downto 0);
        signal finished : std_logic := '0';
        signal taken    : std_logic := '0';
        signal send     : std_logic;

        constant camera_address : std_logic_vector(7 downto 0) := x"42"; -- 42"; -- Device write ID - see top of page 11 of data sheet
begin
   config_finished <= finished;

        send <= not finished;
        Inst_i2c_sender: i2c_sender PORT MAP(
                clk   => clk,
                taken => taken,
                siod  => siod,
                sioc  => sioc,
                send  => send,
                id    => camera_address,
                reg   => command(15 downto 8),
                value => command(7 downto 0)
        );

        reset <= '1';                                           -- Normal mode
        pwdn  <= '0';                                           -- Power device up
        xclk  <= sys_clk;

        Inst_ov7670_registers: ov7670_registers PORT MAP(
                clk      => clk,
                advance  => taken,
                command  => command,
                finished => finished,
                resend   => resend
        );

        process(clk)
        begin
                if rising_edge(clk) then
                        sys_clk <= not sys_clk;
                end if;
        end process;
end Behavioral;

[2] http://e2e.ti.com/support/dsp/davinci_digital_media_processors/f/99/t/6092


Importing VHDL code

To insert VHDL code snippets into Vivado:

  • From the main menu select Tools → Create and Package IP, click Next.
  • Select Package a specified directory, click Next.
  • Locate the directory which contains VHDL files for IP location, click
    Next.
  • Set Project name to main component name.
  • Set Project location to the parent folder of the VHDL files.
  • Click Finish

Once you have added everything to the library it’s time to instantiate the code in the design, for each component add the corresponding block:

  • Click on Open Block Design under IP Integrator to open up the high
    level block design.
  • Right click in the designer area and select Add IP…
  • Locate the components added earlier
  • Repeat same steps for all components

Next step is to insert block RAM, clocking wizard and connect the components.


Instantiating block RAM

Since block RAM is highly platform specific a Xilinx block has to be inserted. Right click in the high level design → Add IP…Block Memory Generator to insert block RAM. Right click on the block → Customize block… opens up the dialog for editing block RAM parameters.

Stand Alone mode makes it possible to generate Simple Dual Port RAM which is essentially memory with write port and read port. Port width refers to amount of bits that can be read/written at once or in other words the size of a memory slot. Port depth refers to count of such slots which translates to address bit width.

這裏寫圖片描述
Block RAM generator parameters


Routing pins

The base.xdc important chunks are following:

# Debounce button and config finished LED
set_property PACKAGE_PIN R18 [get_ports button_debounce]
set_property PACKAGE_PIN M14 [get_ports led_config_finished]

# Top JE
set_property PACKAGE_PIN H15 [get_ports ov7670_reset]
set_property PACKAGE_PIN J16 [get_ports {ov7670_d[1]}]
set_property PACKAGE_PIN W16 [get_ports {ov7670_d[3]}]
set_property PACKAGE_PIN V12 [get_ports {ov7670_d[5]}]

# Bottom JE
set_property PACKAGE_PIN Y17 [get_ports ov7670_pwdn]
set_property PACKAGE_PIN T17 [get_ports {ov7670_d[0]}]
set_property PACKAGE_PIN U17 [get_ports {ov7670_d[2]}]
set_property PACKAGE_PIN V13 [get_ports {ov7670_d[4]}]

# Top JD
set_property PACKAGE_PIN R14 [get_ports {ov7670_d[7]}]
set_property PACKAGE_PIN P14 [get_ports ov7670_pclk]
set_property PACKAGE_PIN T15 [get_ports ov7670_vsync]
set_property PACKAGE_PIN T14 [get_ports ov7670_sioc]

# Bottom JD
set_property PACKAGE_PIN V18 [get_ports {ov7670_d[6]}]
set_property PACKAGE_PIN V17 [get_ports ov7670_xclk]
set_property PACKAGE_PIN U15 [get_ports ov7670_href]
set_property PACKAGE_PIN U14 [get_ports ov7670_siod]

# Red channel of VGA output
set_property PACKAGE_PIN M19 [get_ports {RED_O[0]}]
set_property PACKAGE_PIN L20 [get_ports {RED_O[1]}]
set_property PACKAGE_PIN J20 [get_ports {RED_O[2]}]
set_property PACKAGE_PIN G20 [get_ports {RED_O[3]}]
set_property PACKAGE_PIN F19 [get_ports {RED_O[4]}]

# Green channel of VGA output
set_property PACKAGE_PIN H18 [get_ports {GREEN_O[0]}]
set_property PACKAGE_PIN N20 [get_ports {GREEN_O[1]}]
set_property PACKAGE_PIN L19 [get_ports {GREEN_O[2]}]
set_property PACKAGE_PIN J19 [get_ports {GREEN_O[3]}]
set_property PACKAGE_PIN H20 [get_ports {GREEN_O[4]}]
set_property PACKAGE_PIN F20 [get_ports {GREEN_O[5]}]
# Blue channel of VGA output
set_property PACKAGE_PIN P20 [get_ports {BLUE_O[0]}]
set_property PACKAGE_PIN M20 [get_ports {BLUE_O[1]}]
set_property PACKAGE_PIN K19 [get_ports {BLUE_O[2]}]
set_property PACKAGE_PIN J18 [get_ports {BLUE_O[3]}]
set_property PACKAGE_PIN G19 [get_ports {BLUE_O[4]}]

# Horizontal and vertical synchronization of VGA output
set_property PACKAGE_PIN P19 [get_ports HSYNC_O]
set_property PACKAGE_PIN R19 [get_ports VSYNC_O]

# Voltage levels
set_property IOSTANDARD LVCMOS33 [get_ports button_debounce]
set_property IOSTANDARD LVCMOS33 [get_ports led_config_finished]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_pclk]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_sioc]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_vsync]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_reset]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_pwdn]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_href]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_xclk]
set_property IOSTANDARD LVCMOS33 [get_ports ov7670_siod]
set_property IOSTANDARD LVCMOS33 [get_ports {ov7670_d[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RED_O[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports {GREEN_O[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BLUE_O[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports HSYNC_O]
set_property IOSTANDARD LVCMOS33 [get_ports VSYNC_O]

# Magic
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets ov7670_pclk_IBUF]

Using the pin mapping above the camera can be connected cleanly to the board:

這裏寫圖片描述
Omnivision OV7670 attached to Pmod connectors JD and JE.

Remember to connect GND and 3.3V rails of the ZYBO to cameras GND and 3.3V rails.


Final high level design

這裏寫圖片描述
High level design

Click on Generate bitstream button and transfer resulting bitstream file to the boot partition and restart ZYBO.


Summary

If you’ve connected camera correctly you should see the video feed from the camera on the screen attached to VGA output. Capture and controller blocks can be re-used in other examples involving Omnivision OV7670 camera, so it’s important to get expected outcome at this point.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章