FC2カウンター FPGAの部屋 画像のエッジ検出(横方向のみ)1
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

画像のエッジ検出(横方向のみ)1

画像のエッジ検出2(方式の検討)”で縦横斜めのエッジを検出する予定だったのですが、思ったより手間取るようなので、shirouさんにコメントで教えていただいた横方向のエッジ検出だけを行ってみることにした。これならば、今まで書いてきたVHDLコードをコピーするだけで簡単に書くことができる。後で縦横斜めエッジ検出と性能を比べることもできる。
書きなおしたのはCamera_Controller.vhdとトップのCamDispCntrl_SRAM.vhd、それに、シミュレーションの時にY(輝度)データの差分がうまく取れるように、OV7640_Model.vhd も改造した。
とりあえずは、Camera_Controller.vhd から下に貼る。

-- CAMERA_CONTROLLER
-- カメラデータを書き込むためのSRAMのアドレスを出力
-- 将来的にはI2Cコントローラを付けて、CMOSカメラの設定を変更することも考えている

library IEEE;
use IEEE.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

entity Camera_Controller is
    port(
        clk : in std_logic;
        reset : in std_logic;        
        cam_href_2d : in std_logic;    -- CMOSカメラからのHREFのラッチ出力
        cam_href_3d : in std_logic;    -- CMOSカメラからのHREFのラッチ出力(3クロック遅延)
        r_w : in std_logic; -- Read / Write
        master_sync : in std_logic; -- 同期信号
        cam_ydata_2d : in std_logic_vector(7 downto 0); -- CMOSカメラからの輝度データ
        ydata_out : out std_logic_vector(7 downto 0); -- 比較した値
        mem_addr : out std_logic_vector(17 downto 0) -- CMOSカメラのデータを書き込むSRAMのアドレス
    );
end Camera_Controller;

architecture RTL of Camera_Controller is
constant Edge_Threshold_Def : integer := 64;
constant Edge_Value_Def : integer := 128;

signal mem_a : unsigned(17 downto 0);
signal sub_count : std_logic_vector(1 downto 0);
type COMPARE_H_STATE is (no_data_h, comparing_h);
signal cs_chs : COMPARE_H_STATE;
signal h_valid_flag : std_logic;
signal alu_out : unsigned(7 downto 0);
signal ydata_1d : std_logic_vector(7 downto 0);

begin
    -- 4クロックにアドレスが1つ進むので、4つ数えるカウンタを用意する。
    process(clk) begin
        if clk'event and clk='1' then
            if reset='1' or master_sync='1' then
                sub_count <= (others => '0');
            else
                if cam_href_2d='1' then 
                    sub_count <= sub_count + 1;
                end if;
            end if;
        end if;
    end process;
    
    -- SRAMのアドレス生成、4クロックに一回+1する
    process(clk) begin
        if clk'event and clk='1' then
            if reset='1' or master_sync='1' then
                mem_a <= (others => '0');
                -- mem_a <= "000000000100000000";
            else
                if cam_href_2d='1' and sub_count="11" then
                    mem_a <= mem_a + 1;
                end if;
            end if;
        end if;
    end process;
    mem_addr <= std_logic_vector(mem_a);
    
    -- 水平方向の2つのデータが比較可能ということを表すステートマシン、cam_href_2dが1になった最初のデータは比較するデータがないので比較できない
    process(clk) begin
        if clk'event and clk='1' then
            if reset='1' then
                cs_chs <= no_data_h;
                h_valid_flag <= '0';
            else
                case cs_chs is
                    when no_data_h =>
                        if cam_href_3d='1' then -- 最初の1つは排除
                            cs_chs <= comparing_h;
                            h_valid_flag <= '1';
                        else
                            cs_chs <= no_data_h;
                        end if;
                    when comparing_h =>
                        if cam_href_2d='0' then
                            cs_chs <= no_data_h;
                            h_valid_flag <= '0';
                        else
                            cs_chs <= comparing_h;
                        end if;
                end case;
            end if;
        end if;
    end process;

    -- 1つ前のYデータを覚える
    process(clk) begin
        if clk'event and clk='1' then
            if reset='1' then
                ydata_1d <= (others => '0');
            elsif r_w='1' then
                ydata_1d <= cam_ydata_2d;
            end if;
        end if;
    end process;
    
    -- cam_ydata_2d - ydata_1dの絶対値を取る
    process(cam_ydata_2d, ydata_1d) begin
        if unsigned(cam_ydata_2d) >= unsigned(ydata_1d) then
            alu_out <= unsigned(cam_ydata_2d) - unsigned(ydata_1d);
        else
            alu_out <= unsigned(ydata_1d) - unsigned(cam_ydata_2d);
        end if;
    end process;
    
    -- 1つ前のYデータと現在のYデータを比較してエッジを検出する
    process(h_valid_flag, alu_out) begin
        if h_valid_flag='0' then -- 1つ前のYデータがないので、値は0
            ydata_out <= (others => '0');
        elsif h_valid_flag='1' then
            if alu_out >= Edge_Threshold_Def then
                ydata_out <= CONV_STD_LOGIC_VECTOR(Edge_Value_Def, 8);
             else
                ydata_out <= (others => '0');
            end if;
        else
            ydata_out <= (others => '0');
        end if;
    end process;
    
end RTL;


constant文でEdge_Threshold_Def(エッジ検出のスレッショルド)を決定している。前のデータとの差がEdge_Threshold_Defよりも大きければ、Edge_Value_Defを出力する。それ以外の場合は0を出力データとする。
次は、CamDispCntrl_SRAM.vhd。

--CamDisplay Contoroller with SRAM(トップモジュール)
-- トップモジュールから全部作ることにする。
-- CMOSカメラから出てくるビデオ出力はUYVYとする
-- VSYNCは正論理、HREFも正論理
-- clk(48MHz)はそのまま使用して、PCLKをDCMで受けて使用する。
-- IOBにマップされるロジックはこのトップモジュールにインスタンシエーションする。
-- synchronizerでは_1dのデータを使用し、その他のモジュールでは_2dのデータを使用する
-- 全体のタイミングは_2dを基準とする

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

-- pragma translate_off
library UNISIM;
use UNISIM.VComponents.all;
-- pragma translate_on

entity CamDispCntrl_SRAM is
    port (
        clk    : in std_logic;            -- 水晶発振器からの48MHzクロック
        cam_vsync : in std_logic;    -- CMOSカメラからのVSYNC
        cam_href : in std_logic;    -- CMOSカメラからのHREF
        cam_pclk : in std_logic;        -- CMOSカメラからのpclk
        cam_ydata : in std_logic_vector(7 downto 0); -- CMOSカメラからのYデータ
        cam_sio_c : out std_logic;    -- CMOSカメラのI2Cクロック
        -- cam_sio_d : inout std_logic; -- CMOSカメラのI2Cデータ
        cam_sio_d : out std_logic; -- CMOSカメラのI2Cデータ
        cam_clk    : out std_logic;     -- CMOSカメラのクロック
        cam_reset    : out std_logic;     -- CMOSカメラのリセット(Hでリセット)
        dac_red    : out std_logic_vector(7 downto 0);    -- DACへのRED出力
        dac_green : out std_logic_vector(7 downto 0); -- DACへのGREEN出力
        dac_blue : out std_logic_vector(7 downto 0); -- DACへのBLUE出力
        n_dac_blank : out std_logic; -- DACへのアクティブローBLANK
        dac_clk : out std_logic; -- DACクロック(24MHz)
        vga_vsync : out std_logic; -- VGAのVSYNC
        vga_hsync : out std_logic; -- VGAのHSYNC
        mem_data : inout std_logic_vector(15 downto 0);    -- SRAMのmemory data
        n_mem_we : out std_logic; -- SRAMのmemory write enable
        n_mem_rd : out std_logic; -- SRAMのmemory read enable
        n_mem_cs0 : out std_logic; -- SRAMのchip select 0
        n_mem_cs1 : out std_logic; -- SRAMのchip select 1
        n_mem_cs2 : out std_logic; -- SRAMのchip select 2
        n_mem_cs3 : out std_logic; -- SRAMのchip select 3
        n_mem_lowerB : out std_logic; -- SRAMのmemory lower byte enable
        n_mem_upperB : out std_logic; -- SRAMのmemory upper byte enable
        mem_addr : out std_logic_vector(17 downto 0) -- SRAMのmemory address
    );
end CamDispCntrl_SRAM;

architecture RTL of CamDispCntrl_SRAM is
component DCM_module_24MHz
    port(
        clk48MHz_in : in std_logic;
        clk48_out : out std_logic;
        cam_clk_out : out std_logic;
        pclk_in : in std_logic;
        mclk_out : out std_logic;
        cam_clk_locked : out std_logic;
        mclk_locked : out std_logic
    );
end component;
component ODDR2
 generic(

      DDR_ALIGNMENT : string := "NONE";
      INIT          : bit    := '0';
      SRTYPE        : string := "SYNC"
      );

  port(
      Q           : out std_ulogic;

      C0          : in  std_ulogic;
      C1          : in  std_ulogic;
      CE          : in  std_ulogic := 'H';
      D0          : in  std_ulogic;
      D1          : in  std_ulogic;
      R           : in  std_ulogic := 'L';
      S           : in  std_ulogic := 'L'
    );
end component;
component VGA_Display_Controller
    port(
        clk : in std_logic;
        reset : in std_logic;
        master_sync : in std_logic; -- 表示タイミングの同期信号
        pixel_y_data : in std_logic_vector(15 downto 0); -- Yのデータ、下のdata_enableが1の時に有効
        data_enable : in std_logic;
        h_count_out : out unsigned(9 downto 0); -- 水平カウンタのカウント出力
        v_count_out : out unsigned(9 downto 0); -- 垂直カウンタのカウント出力
        red_out : out std_logic_vector(7 downto 0); -- VGA出力
        green_out : out std_logic_vector(7 downto 0); -- VGA出力
        blue_out : out std_logic_vector(7 downto 0); -- VGA出力
        blank_out : out std_logic; -- BLANK出力
        h_syncx_out : out std_logic; -- 水平同期出力
        v_syncx_out : out std_logic; -- 垂直同期出力
        mem_addr : out std_logic_vector(18 downto 0) -- SRAMのアドレス、バイト単位
    );
end component;
component synchronizer
    port(
        clk : in std_logic;
        reset : in std_logic;
        cam_vsync_1d : in std_logic;
        cam_href_1d : in std_logic;
        master_sync : out std_logic;
        r_w : out std_logic
    );
end component;
component SRAM_Controller
    port(
        clk : in std_logic;
        clk48 : in std_logic;
        reset : in std_logic;
        r_w : in std_logic; -- Read or Write
        cam_href_3d : in std_logic; -- cam_href の3クロック遅れ、各モジュールで2クロック遅れの信号を使っているので、それよりも1クロック遅れている信号をweのイネーブルとして使用する。これはUYVYとデータが来るので、サンプルするのに1クロック遅れるから。
        master_sync : in std_logic;
        cam_mem_addr : in std_logic_vector(17 downto 0); -- CMOSカメラの書き込み用のアドレス
        cam_ydata_in : in std_logic_vector(7 downto 0); -- CMOSカメラのデータ
        vga_mem_addr : in std_logic_vector(18 downto 1); -- VGAコントローラー用読み出し用アドレス
        
        mem_data_out : out std_logic_vector(15 downto 0); -- SRAMのデータ出力。CMOSカメラのデータを2つ集めたもの。
        mem_data_oe : out std_logic; -- SRAMのmemory data出力のOutput Enable
        
        n_mem_we : out std_logic; -- SRAMのWE
        n_mem_cs0 : out std_logic; -- SRAMのchip select 0
        n_mem_cs1 : out std_logic; -- SRAMのchip select 1
        n_mem_cs2 : out std_logic; -- SRAMのchip select 2
        n_mem_cs3 : out std_logic; -- SRAMのchip select 3
        n_mem_lowerB : out std_logic; -- SRAMのmemory lower byte enable
        n_mem_upperB : out std_logic; -- SRAMのmemory upper byte enable
        mem_addr : out std_logic_vector(17 downto 0) -- SRAMのmemory address
    );
end component;
component Camera_Controller
    port(
        clk : in std_logic;
        reset : in std_logic;        
        cam_href_2d : in std_logic;    -- CMOSカメラからのHREFのラッチ出力
        cam_href_3d : in std_logic;    -- CMOSカメラからのHREFのラッチ出力(3クロック遅延)
        r_w : in std_logic; -- Read / Write
        master_sync : in std_logic; -- 同期信号
        cam_ydata_2d : in std_logic_vector(7 downto 0); -- CMOSカメラからの輝度データ
        ydata_out : out std_logic_vector(7 downto 0); -- 比較した値
        mem_addr : out std_logic_vector(17 downto 0) -- CMOSカメラのデータを書き込むSRAMのアドレス
    );
end component;
component IOBUF
    port(
        O : out std_logic;
        IO : inout std_logic;
        I : in std_logic;
        T : in std_logic
    );
end component;

signal mclk : std_logic;
signal reset : std_logic;
signal mclk_locked : std_logic;
signal master_sync : std_logic;
signal r_w : std_logic;
signal cam_href_1d, cam_vsync_1d : std_logic;
signal cam_href_3d : std_logic;
signal cam_href_2d, cam_vsync_2d : std_logic;
signal cam_ydata_1d, cam_ydata_2d : std_logic_vector(7 downto 0);
signal w_r : std_logic;
signal vga_blank : std_logic;
signal vga_mem_addr : std_logic_vector(17 downto 0);
signal vga_mem_addr_19 : std_logic_vector(18 downto 0);
signal cam_mem_addr : std_logic_vector(17 downto 0);
signal clk48 : std_logic;
signal mem_data_out : std_logic_vector(15 downto 0); -- SRAMのデータ出力。CMOSカメラのデータを2つ集めたもの。
signal mem_data_oe : std_logic; -- SRAMのmemory data出力のOutput Enable
signal n_mem_data_oe : std_logic;
signal vga_ydata : std_logic_vector(15 downto 0);
signal input_mem_data : std_logic_vector(15 downto 0);
signal cam_clk_node, cam_clk_locked : std_logic;
signal n_cam_clk_node, cam_clk_reset : std_logic;
signal n_mclk : std_logic;
signal ydata_YUV : std_logic_vector(7 downto 0);

begin
    cam_sio_c <= '1';
    cam_sio_d <= 'Z';
    cam_reset <= '0'; -- OV7640のRESETはアクティブハイ
    
    DCM_module_24MHz_inst : DCM_module_24MHz port map(
        clk48MHz_in => clk,
        clk48_out => clk48,
        cam_clk_out => cam_clk_node,
        pclk_in => cam_pclk,
        mclk_out => mclk,
        cam_clk_locked => cam_clk_locked,
        mclk_locked => mclk_locked
    );
    reset <= not mclk_locked;
    
    -- CMOSカメラ用クロックの生成
    n_cam_clk_node <= not cam_clk_node;
    cam_clk_reset <= not cam_clk_locked;
    ODDR2_for_cam_clk : ODDR2 generic map(
        SRTYPE => "ASYNC"
    ) port map(
        Q => cam_clk,
        C0 => cam_clk_node,
        C1 => n_cam_clk_node,
        CE => '1',
        D0 => '1',
        D1 => '0',
        R => cam_clk_reset,
        S => '0'
    );
    
    -- DAC用クロックの生成
    n_mclk <= not mclk;
    ODDR2_for_dac_clk : ODDR2 generic map(
        SRTYPE => "ASYNC"
    ) port map(
        Q => dac_clk,
        C0 => mclk,
        C1 => n_mclk,
        CE => '1',
        D0 => '1',
        D1 => '0',
        R => reset,
        S => '0'
    );
    
    -- CMOSカメラからの入力は一度IOBのFFを通す
    process(mclk) begin
        if mclk'event and mclk='1' then
            if reset='1' then
                cam_vsync_1d <= '0';
                cam_href_1d <= '0';
                cam_ydata_1d <= (others => '0');
                cam_vsync_2d <= '0';
                cam_href_2d <= '0';
                cam_ydata_2d <= (others => '0');
                cam_href_3d <= '0';
            else
                cam_vsync_1d <= cam_vsync;
                cam_href_1d <= cam_href;
                cam_ydata_1d <= cam_ydata;
                cam_vsync_2d <= cam_vsync_1d;
                cam_href_2d <= cam_href_1d;
                cam_ydata_2d <= cam_ydata_1d;
                cam_href_3d <= cam_href_2d;
            end if;
        end if;
    end process;
    
    -- CMOSカメラの書き込みアドレスの生成
    
    synchronizer_inst : synchronizer port map(
        clk => mclk,
        reset => reset,
        cam_vsync_1d => cam_vsync_1d,
        cam_href_1d => cam_href_1d,
        master_sync => master_sync,
        r_w => r_w
    );
    
    -- mem_dataの入力FF
    process(mclk) begin
        if mclk'event and mclk='1' then
            if reset='1' then
                vga_ydata <= (others => '0');
            else
                vga_ydata <= input_mem_data;
            end if;
        end if;
    end process;
    w_r <= not r_w;
    
    VGA_Display_Controller_inst : VGA_Display_Controller port map(
        clk => mclk,
        reset => reset,
        master_sync => master_sync,
        pixel_y_data => vga_ydata,
        data_enable => w_r,
        h_count_out => open,
        v_count_out => open,
        red_out => dac_red,
        green_out => dac_green,
        blue_out => dac_blue,
        blank_out => vga_blank,
        h_syncx_out => vga_hsync,
        v_syncx_out => vga_vsync,
        mem_addr => vga_mem_addr_19
    );
    n_dac_blank <= not vga_blank;
    
    Camera_Controller_inst : Camera_Controller port map(
        clk => mclk,
        reset => reset,
        cam_href_2d => cam_href_2d,
        cam_href_3d => cam_href_3d,
        r_w => r_w,
        master_sync => master_sync,
        cam_ydata_2d => cam_ydata_2d,
        ydata_out => ydata_YUV,
        mem_addr => cam_mem_addr
    );
    
    vga_mem_addr <= vga_mem_addr_19(18 downto 1);
    SRAM_Controller_inst : SRAM_Controller port map(
        clk => mclk,
        clk48 => clk48,
        reset => reset,
        r_w => r_w,
        cam_href_3d => cam_href_3d,
        master_sync => master_sync,
        cam_mem_addr => cam_mem_addr,
        cam_ydata_in => ydata_YUV,
        vga_mem_addr => vga_mem_addr,
        mem_data_out => mem_data_out,
        mem_data_oe => mem_data_oe,
        n_mem_we => n_mem_we,
        n_mem_cs0 => n_mem_cs0,
        n_mem_cs1 => n_mem_cs1,
        n_mem_cs2 => n_mem_cs2,
        n_mem_cs3 => n_mem_cs3,
        n_mem_lowerB => n_mem_lowerB,
        n_mem_upperB => n_mem_upperB,
        mem_addr => mem_addr
    );
    n_mem_data_oe <= not mem_data_oe;
    
    MEM_DATA_GEN : for i in 15 downto 0 generate
        IOBUF_inst : IOBUF port map(
            O => input_mem_data(i),
            IO => mem_data(i),
            I => mem_data_out(i),
            T => n_mem_data_oe
        );
    end generate MEM_DATA_GEN;
    n_mem_rd <= not r_w;
end RTL;


次はOV7640_Model.vhd だが、これは変更点のYデータを出力するprocessだけを示す。

    -- ydata の出力
    process(reset, pclock) begin -- Yデータ
        if reset='1' then
            y_data <= "00000001";
        elsif pclock'event and pclock='1' then
            if h_count<H_ACTIVE_VIDEO and h_count(0)='1' then -- 水平の表示区間で2クロックに1回シフト
                if y_data=128 then
                    y_data <= "00000001";
                else
                    y_data <= y_data(6 downto 0) & '0';
                end if;
            elsif h_count=0 and v_count=0 then -- 1フレーム描画の最初にリセット
                y_data <= "00000001";
            end if;
        end if;
    end process;


これでシミュレーションをしてみた。その結果を下の図に示す。
edge_detect_2_091102.png

cam_ydata_2d信号の四角で囲ったデータがY(輝度)データだ。その値は左シフトしているので、16進数で0x40, 0x80, 0x01 と変化している。それぞれ、Edge_Threshold_Defの64と同じか超えているので、Camera_Controller.vhdはydata_out出力に128、16進数で0x80を出力している。その様子が矢印で書かれている。つまりinput_mem_dataに80と出力されている。
これでシミュレーションはうまく行ったようなので、実機で確かめてみることにする。

#今朝はおしまい。今から録画してあったアブダビF1を見ます。今年の最終戦です。ジェンソン・バトン、チャンピオンおめでとう。

(修正)Camera_Controller.vhdが間違っていたので修正しました。
  1. 2009年11月02日 05:41 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/1279-5c790e3c
この記事にトラックバックする(FC2ブログユーザー)