Forum: FPGA, VHDL & Co. OV7670 Kamera am Zybo


von Daniel K. (daniel_k80)


Lesenswert?

Hallo zusammen,

ich möchte gerne das oben genannte Kameramodul an mein FPGA anschließen.
Jetzt habe ich ein paar "Probleme":
1. Das FPGA besitzt nicht genug BRAM um das komplette Image zu 
speichern. Meine Idee ist nun einfach jedes 8. Pixel zu nehmen und das 
dann zu speichern. Dadurch wird das Bild kleiner und ich kann es 
problemlos in meinem BRAM speichern.
Gibt es vielleicht auch noch eine andere (ggf. bessere) Lösung?

2. Da ich das Modul noch nicht habe erstelle ich mir gerade eine 
Testbench in VHDL um nachher auch meine Hardware damit zu testen.
Beim lesen des Datenblattes der Kamera 
(http://www.voti.nl/docs/OV7670.pdf) bin ich nun auf eine Unstimmigkeit 
gestoßen.
Auf S.7 wird im "VGA Frame Timing" Diagramm gesagt, dass eine Zeile 
784xt_p lang ist (t_p ist bei mir in der Testbench 1/25Mhz).
Eine komplette Zeile soll laut Datenblatt 480xt_line lang sein. 
Allerdings ist das Diagramm an der Stelle (meiner Meinung nach) ein 
bisschen Missverständlich.
Müsste eine komplette Zeile nicht 480xt_line - 144*t_p lang sein, da das 
Synch-Signal fehlt und es im Diagramm quasi in dem Bereich 10*t_line 
steckt?

von Markus K. (markus-)


Lesenswert?

Wenn Du nur jedes 8. Pixel nimmst, dann machst Du eine Unterabtastung 
und das gibt ganz schreckliche Bilder. Allerdings kann der Imager doch 
die Bilder auch schon verkleinert ausgeben.

von endox (Gast)


Lesenswert?

Wieso willst Du denn ein ganzes Bild im BRAM speichern? Du hast doch 
512MB DDR3.

von Maik H. (littlechip)


Lesenswert?

Markus K. schrieb:
> Wenn Du nur jedes 8. Pixel nimmst, dann machst Du eine Unterabtastung
> und das gibt ganz schreckliche Bilder. Allerdings kann der Imager doch
> die Bilder auch schon verkleinert ausgeben.

Oder zumindest eine Mittelwertbildung machen

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

danke für die Hinweise.
Ich dachte ich nutze erst einmal die Default-Einstellungen von dem 
Imager, weil ich sonst noch I²C implementieren muss....wobei ich das 
auch einfach vom ARM nehmen könnte.

Kann ich den Imager während des Betriebs umkonfigurieren?

Und wo kann ich die Größe des Bildes ändern? Ich habe da jetzt nichts 
gefunden (oder übersehen).

Bzgl. des RAMs...
Der RAM hängt doch direkt an den Pins vom Processing System, sprich den 
kann ich nicht mit der programmierbaren Logik ansprechen (oder?).

von endox (Gast)


Lesenswert?

Die Pins des RAM hängen am PS, das stimmt. Du kannst aber über ein 
DMA-Modul, das Vivado als IP-Core mitbringt, sowohl lesen als auch 
schreiben. Da gibt es Tutorials von Xilinx bzw über Google.

http://www.xilinx.com/support/answers/57561.html
http://www.fpgadeveloper.com/2014/08/using-the-axi-dma-in-vivado.html

Wenn Du den ARM komplett aussen vor lassen willst, wird es wohl 
schwierig. :)

Vielleicht anders gefragt. Was hast Du denn mit dem Bild vor? Musst Du 
es am Stück speichern?

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

danke für die Links. Das schaut interessant aus. Ich merk mir das auf 
jeden Fall mal für später (glaube das wird erst mal ein bisschen viel, 
wenn ich noch ein AXI-Interface einfügen will).

Erst einmal habe ich noch nichts direkt mit dem Bild vor, aber ich würde 
gerne später mal (wenn die Kamera läuft) ein bisschen Bildverarbeitung 
etc. probieren wollen (einfach ein bisschen rum spielen :) ).
Das Bild soll dann auch über VGA ausgegeben werden. Den VGA-Controller 
habe ich schon, allerdings kann der, bedingt durch den niedrigen 
Speicher, bisher nur Text darstellen. Die Möglichkeit das Bild der 
Kamera zu speichern ist damit auch quasi eine Methode um ein 640x480 
Bild für meinen VGA-Controller zu speichern.

Also das erste was ich realisieren möchte ist Bild der Kamera einlesen 
und das Bild mittels VGA auf einen Monitor geben. Ich habe dazu auch 
schon was gefunden:

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

Allerdings hat er genau das selbe Problem wie ich...und ich möchte es 
halt auch gerne selber machen und irgendwie nicht abkopieren etc. ;) 
(alleine schon weil ich bisher so wenig mit VHDL gemacht habe)....

von endox (Gast)


Lesenswert?

OK ich verstehe. Um jetzt ein Bild einzulesen und über den VGA-Core 
auszugeben brauchst du aber nicht das komplette Bild speichern.
Solange der Pixeltakt, den der Sensor ausgibt kleiner ist als der Takt, 
mit dem der VGA-Core arbeitet, brauchst du die FIFO aus deinem Beispiel 
nur als Taktübergang und kleinen Buffer.
Damit aus den einzelnen Datenportionen dann ein zusammenhängendes Bild 
entsteht gibt es ja HSync und Vsync. :)

von endox (Gast)


Lesenswert?

Der Sensor kann wohl minimal 5MHz Pixeltakt ausgeben. Im Beispiel 
arbeitet der VGA-Core mit 25MHz. Damit kann die Größe deiner FIFO 
minimal sein.

von Daniel K. (daniel_k80)


Lesenswert?

An die Methode habe ich auch schon gedacht.
Ist (denke ich mal) für den Anfang die einfachste. Damit kann ich 
zumindest schon mal den IP-Core für die Kamera machen. Sobald das läuft 
mache ich dann den Rest und schaue wie ich das Bild gespeichert bekomme.

Mein VGA-Controller arbeitet im Moment mit 25,175MHz (wie es der 
Standard vorschreibt).

Wie sieht den das bzgl. meiner Frage zu dem Timing aus? Habe ich da ein 
Verständnisproblem oder ist das einfach nur undeutlich (falsch) im 
Datenblatt dokumentiert?

von endox (Gast)


Lesenswert?

Nö das passt schon so. :) Natürlich müsste die Bemaßung theoretisch noch 
ein Stückchen weiter nach rechts gehen. Nämlich um 144xt_p.

Grundlegend sagt dir der Sensor ja zwei wichtige Dinge.
- Anfang und Ende des Bilds
- Anfang und Ende einer Zeile

Damit weißt Du schon alles was Du wissen willst. Du musst dann auch 
keine Pixel zählen. Wie groß das Bild ist weißt Du ja. 640x480. Und nur 
innerhalb des HREF befinden sich valide Daten. Mit diesen Informationen 
kannst Du jetzt die Daten vom Sensor durch die FIFO über VGA 
rausschieben.

In der Testbench würde ich eh kein komplettes Bild simulieren. Da reicht 
ein 10x10 Pixelbild. Solange sich Vsync und HREF wie bei deinem Sensor 
verhalten passt das.

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

ok, das habe ich mir schon fast gedacht. Sprich die rechte Pause ist 
eigentlich 10xt_line + 144xt_p lang.
Zu der Testbench...die sieht im Moment so aus (nicht schön, aber ich 
habe erst einmal nur auf die Funktion geachtet):
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
-- Uncomment the following library declaration if instantiating
6
-- any Xilinx leaf cells in this code.
7
--library UNISIM;
8
--use UNISIM.VComponents.all;
9
10
entity OV7670_TB is
11
--  Port ( );
12
end OV7670_TB;
13
14
architecture OV7670_TB_Arch of OV7670_TB is
15
16
    constant ClockCycle : time := 20 ns;
17
18
    signal PixelData : std_logic_vector(15 downto 0) := (others => '0');
19
    
20
    signal Clock : std_logic := '0';
21
    signal D    : std_logic_vector(7 downto 0) := (others => '0');
22
    signal HRef : std_logic := '0';    
23
    signal VSync: std_logic := '0';    
24
25
begin
26
    
27
    Clock <= not Clock after ClockCycle / 2;
28
    
29
    process
30
    begin
31
32
        -- Generate VSync with width 3xt_line
33
        VSync <= '1';
34
        for Delay in 0 to 2351 loop
35
            wait until falling_edge(Clock);
36
            wait until falling_edge(Clock);
37
        end loop;
38
        VSync <= '0'; 
39
            
40
        -- Generate break with width 17xt_line
41
        for Delay in 0 to 13327 loop
42
            wait until falling_edge(Clock);
43
            wait until falling_edge(Clock);
44
        end loop;    
45
        HRef <= '1';
46
     
47
        -- Write one Frame
48
        for Frame in 0 to 479 loop
49
            
50
                -- Write one Line
51
                for Pixel in 1 to 640 loop
52
                    PixelData <= std_logic_vector(to_unsigned(Pixel, PixelData'length));
53
                    D <= PixelData(15 downto 8);
54
                    wait until falling_edge(Clock);
55
                    D <= PixelData(7 downto 0);
56
                    wait until falling_edge(Clock);
57
                end loop;
58
            
59
                -- Create HSync
60
                for Delay in 0 to 143 loop
61
                    HRef <= '0';
62
                    PixelData <= (others => '0');
63
                    wait until falling_edge(Clock);
64
                    wait until falling_edge(Clock);
65
                end loop;
66
                
67
                HRef <= '1';
68
                
69
            end loop;
70
            
71
            HRef <= '0';
72
            for Delay in 0 to 7695 loop
73
                wait until falling_edge(Clock);
74
                wait until falling_edge(Clock);
75
            end loop;      
76
        
77
    end process;
78
79
end OV7670_TB_Arch;

Von den Timings her sieht das alles recht passend aus (vielleicht magst 
du das mal gegen checken :) ). Das einzige was sich unterscheidet ist 
das Stück wo die Daten gesendet werden. Das ist bei mir in der 
Simulation 144xt_p zu kurz (gegenüber einem berechneten Wert mit den 
Werten aus dem Datenblatt).

Aber du hast schon recht. Im Prinzip ist die Länge da egal....Hauptsache 
die Synch-Signale kommen rechtzeitig.

von endox (Gast)


Lesenswert?

Wie lange die Pause wirklich ist, wirst Du dann sehen wenn das Modul vor 
Dir liegt. ;) Manche Datenblätter sind voll von kleinen Unstimmigkeiten. 
Wäre ja sonst auch langweilig. :D

Den Code schau ich mir morgen mal an. Falls es bis dahin kein andere 
getan hat.

von endox (Gast)


Lesenswert?

Dir fehlt der zweite VSync-Puls. Das ist nicht der Anfang vom zweiten 
Bild, sondern das Ende des ersten. Sonst wäre ja die Framerate fix.

von Daniel K. (daniel_k80)


Lesenswert?

Hallo,

ich habe die TB mal ergänzt:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
-- Uncomment the following library declaration if instantiating
6
-- any Xilinx leaf cells in this code.
7
--library UNISIM;
8
--use UNISIM.VComponents.all;
9
10
entity OV7670_TB is
11
--  Port ( );
12
end OV7670_TB;
13
14
architecture OV7670_TB_Arch of OV7670_TB is
15
16
    constant ClockCycle : time := 20 ns;
17
18
    signal PixelData : std_logic_vector(15 downto 0) := (others => '0');
19
    
20
    signal Clock : std_logic := '0';
21
    signal D    : std_logic_vector(7 downto 0) := (others => '0');
22
    signal HRef : std_logic := '0';    
23
    signal VSync: std_logic := '0';    
24
25
begin
26
    
27
    Clock <= not Clock after ClockCycle / 2;
28
    
29
    process
30
    begin
31
32
        wait for 1 ms;
33
34
        -- Generate VSync with width 3xt_line
35
        VSync <= '1';
36
        for Delay in 0 to 2351 loop
37
            wait until falling_edge(Clock);
38
            wait until falling_edge(Clock);
39
        end loop;
40
        VSync <= '0'; 
41
            
42
        -- Generate break with width 17xt_line
43
        for Delay in 0 to 13327 loop
44
            wait until falling_edge(Clock);
45
            wait until falling_edge(Clock);
46
        end loop;    
47
        HRef <= '1';
48
     
49
        -- Write one Frame
50
        for Frame in 0 to 479 loop
51
            
52
                -- Write one Line
53
                for Pixel in 1 to 640 loop
54
                    PixelData <= std_logic_vector(to_unsigned(Pixel, PixelData'length));
55
                    D <= PixelData(15 downto 8);
56
                    wait until falling_edge(Clock);
57
                    D <= PixelData(7 downto 0);
58
                    wait until falling_edge(Clock);
59
                end loop;
60
            
61
                -- Create HSync
62
                for Delay in 0 to 143 loop
63
                    HRef <= '0';
64
                    PixelData <= (others => '0');
65
                    wait until falling_edge(Clock);
66
                    wait until falling_edge(Clock);
67
                end loop;
68
                
69
                HRef <= '1';
70
                
71
            end loop;
72
            
73
            HRef <= '0';
74
              
75
        for Delay in 0 to 7696 loop
76
            wait until falling_edge(Clock);
77
            wait until falling_edge(Clock);
78
        end loop;
79
        VSync <= '1';  
80
        
81
82
        for Delay in 0 to 2351 loop
83
            wait until falling_edge(Clock);
84
            wait until falling_edge(Clock);
85
        end loop;
86
        VSync <= '0'; 
87
         
88
    end process;
89
    
90
end OV7670_TB_Arch;

: Bearbeitet durch User
von Daniel K. (daniel_k80)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe die Kameramodule vor ein paar Tagen erhalten und will nun in
einem ersten Schritt die Bildausgabe per VGA implementieren. Dazu möchte
ich das Bild im QVGA Format ausgeben (laut Wikipedia sind das 320x240
Pixel und damit kann ich das komplette Bild im RAM vom FPGA speichern).
Die Konfiguration der Kamera mache ich mit einem Mikrocontroller, bis
ich den I²C Bus im FPGA implementiert habe.
Jetzt habe ich die Kamera heute mal mit einem 25MHz Takt versorgt und
HRef und VSync und PCLK an nen LA angeschlossen. Allerdings wird das
Ergebnis ein paar Fragen auf (siehe Screenshot).
Die Abstände zwischen den HSync (bzw. HRef) bzw. VSync Impulsen müssten
doch viel größer sein oder habe ich da einfach nur ein
Verständnisproblem?
Ich betreibe die Kamera mit den Default-Einstellungen nach dem Power-Up.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.