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?
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.
Wieso willst Du denn ein ganzes Bild im BRAM speichern? Du hast doch 512MB DDR3.
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
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?).
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?
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)....
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. :)
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.
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?
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.
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.
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.
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.