OV7670基于FPGA的OV7670摄像头介绍和使用
Posted fpga和matlab
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OV7670基于FPGA的OV7670摄像头介绍和使用相关的知识,希望对你有一定的参考价值。
1.软件版本
quartusii12.1
2.本算法理论知识
OV7670摄像头模块
带384Kb FIFO 数字摄像头 手动变焦
OV7670总共有656*488个像素,其中640*480个有效(即有效像素为30W)。
支持RawRGB、RGB(GBR4:2:2,RGB565/RGB555/RGB444),YUV(4:2:2)和YCbCr(4:2:2)输出格式。
RGB565彩色模式,一个像素占两个字节
低字节的前5位用来表示B(BLUE)
低字节的后三位+高字节的前三位用来表示G(Green)
高字节的后5位用来表示R(RED)
VGA,即分辨率为640*480的输出模式。VGA最早指的是显示器640X480这种显示模式。
QVGA,即分辨率为320*240的输出格式,也就是本文档我们需要用到的格式。QVGA即"Quarter VGA"。顾名思义即VGA的四分之一尺寸,亦即在液晶屏幕(LCD)上输出的分辨率是240×320像素。
QQVGA,即分辨率为160*120的输出格式。
数字摄像头成像原理
数字摄像头使用感光阵列转换光信号,感光阵列如下图。
感光阵列将摄像头透镜中传过来的光采集,转换形成数字信号。感光阵列越密像素越大。常见感光阵列有CCD、COMS。就CCD和CMOS而言:ADC的位置和数量是最大的不同。CCD曝光结束后,进行电信号转移,将每一行中每一个象元的电荷信号依行序依次传入每行的“缓冲器”中,由底端线路依次将每行的电信号引导输出至 CCD 旁的放大器进行放大,再串联 ADC 输出,此为线阵CCD;另外一种是每行均有放大电路,各行同时将捕捉的当前图像信息进行ADC,既可以同时接受一幅完整的图像信息,此为面阵CCD。CMOS 的设计中每个像素就直接连着 ADC,电信号直接放大并转换成数字信号。
比较:CCD的特色在于充分保持信号在传输时不失真,透过每一个像素集合至单一放大器上再做统一处理,可以保持图像的完整性;CMOS的制程较简单,没有专属通道的设计,因此必须先行放大再整合各个像素的资料。由此可见,CMOS的成像过程更加容易出现坏点,使图像失真,但随着他工艺的可兼容性和功耗,使得它更加适合嵌入式的需要,同时,产生的图像失真可以通过相应的算法进行补偿,但这里需要注意的是,CMOS的成像特点决定了它非常容易受到外界环境光的影响,在使用的过程中,一定要设计出符合要求的光源。
这里用的这个摄像头,其几个管脚的含义是:
1 VCC 电源输入:2.5v---3.0V
2 GND 地
3 SCL S 模块内部带上拉电阻
4 SDA 模块内部带上拉电阻
5 VSYNC 帧同步
6 HREF 行同步
7 PCLK 像素时钟
8 XCLK 主时钟输入
9 D7 DOUT9 像素数据
10 D6 DOUT8 像素数据
11 D5 DOUT7 像素数据
12 D4 DOUT6 像素数据
13 D3 DOUT5 像素数据
14 D2 DOUT4 像素数据
16 D1 DOUT3 像素数据
16 D0 DOUT2 像素数据
关于论文中,这个部分的书写内容,参考我提供的文献:
这个里面详细介绍了OV7640的介绍,他们其实完全一样的,你可以全面参考这个部分。
我在设计这个部分的时候,用到的技术点主要有如下几个方面:
第一,摄像头的驱动
第二,摄像头的视频采集
那么你写论文的时候,参考上面论文中 这个部分,稍微改下就可以了,基本上我们这个摄像头和这个论文中的基本完全一致的。然后我上面写的关于这个摄像头的介绍,你可以用进去。
2.TFT液晶屏
这个部分的时序较为复杂,具体来讲,主要的内容如下所示:
这个是关于这款液晶屏的重要时序说明文档。写论文可以参考。
然后关于这个液晶屏,好像,写的论文比较少,我这里主要把涉及到的一些技术点写一下。
那么这里,主要参考的文献是
http://www.docin.com/p-456301691.html
你可以根据这个里面的内容写。
然后如果你要介绍这个TFT结构的话,那么参考如下的网站
http://www.go-gddq.com/html/s185/2013-05/1124316.htm
3.喇叭控制输出音频
蜂鸣器不多解释了,我们在设计的时候,通过一个三极管进行控制,当低电平时,三极管导通,蜂鸣器响;当高电平,三极管截止,蜂鸣器不响。通过高低电平的不同频率的切换,输出对应的音频信息。这个部分比较简单,这里不多做介绍了。
然后,我们介绍一下,我们这个整个系统的设计构架:
注意,你这里没有提供SD卡,我把需要播放的音频和视频保存在FPGA自带的存储器中,这个不影响整个系统的实际原理。
设计中需要对 SD 卡内音频、视频进行相应操作,所以需要为设计建立文件系统。虽然 Nios II IDE 集成开发环境支持 ZIP 文件系统,但它却是针对Flash 中烧写的 ZIP 打包文件,操作还必须通过工具烧写 Flash。这样不仅操作不便,而且对于音频容量、视频远远不够。需要嵌入一个合适的文件系统。本设计中在实现 SD 卡初始化和读写功能的基础上,对 Nios II 进行了文件系统的移植,使相关文件的操作问题得以解决。设计中还要实现音频播放的快进、快退和暂停,这些功能的实现又与文件系统密切相关。
3.部分源码
//Legal Notice: (C)2014 Altera Corporation. All rights reserved. Your
//use of Altera Corporation's design tools, logic functions and other
//software and tools, and its AMPP partner logic functions, and any
//output files any of the foregoing (including device programming or
//simulation files), and any associated documentation or information are
//expressly subject to the terms and conditions of the Altera Program
//License Subscription Agreement or other applicable license agreement,
//including, without limitation, that your use is for the sole purpose
//of programming logic devices manufactured by Altera and sold by Altera
//or its authorized distributors. Please refer to the applicable
//agreement for further details.
// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on
// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030
module sdram_input_efifo_module (
// inputs:
clk,
rd,
reset_n,
wr,
wr_data,
// outputs:
almost_empty,
almost_full,
empty,
full,
rd_data
)
;
output almost_empty;
output almost_full;
output empty;
output full;
output [ 40: 0] rd_data;
input clk;
input rd;
input reset_n;
input wr;
input [ 40: 0] wr_data;
wire almost_empty;
wire almost_full;
wire empty;
reg [ 1: 0] entries;
reg [ 40: 0] entry_0;
reg [ 40: 0] entry_1;
wire full;
reg rd_address;
reg [ 40: 0] rd_data;
wire [ 1: 0] rdwr;
reg wr_address;
assign rdwr = rd, wr;
assign full = entries == 2;
assign almost_full = entries >= 1;
assign empty = entries == 0;
assign almost_empty = entries <= 1;
always @(entry_0 or entry_1 or rd_address)
begin
case (rd_address) // synthesis parallel_case full_case
1'd0: begin
rd_data = entry_0;
end // 1'd0
1'd1: begin
rd_data = entry_1;
end // 1'd1
default: begin
end // default
endcase // rd_address
end
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
wr_address <= 0;
rd_address <= 0;
entries <= 0;
end
else
case (rdwr) // synthesis parallel_case full_case
2'd1: begin
// Write data
if (!full)
begin
entries <= entries + 1;
wr_address <= (wr_address == 1) ? 0 : (wr_address + 1);
end
end // 2'd1
2'd2: begin
// Read data
if (!empty)
begin
entries <= entries - 1;
rd_address <= (rd_address == 1) ? 0 : (rd_address + 1);
end
end // 2'd2
2'd3: begin
wr_address <= (wr_address == 1) ? 0 : (wr_address + 1);
rd_address <= (rd_address == 1) ? 0 : (rd_address + 1);
end // 2'd3
default: begin
end // default
endcase // rdwr
end
always @(posedge clk)
begin
//Write data
if (wr & !full)
case (wr_address) // synthesis parallel_case full_case
1'd0: begin
entry_0 <= wr_data;
end // 1'd0
1'd1: begin
entry_1 <= wr_data;
end // 1'd1
default: begin
end // default
endcase // wr_address
end
endmodule
// synthesis translate_off
`timescale 1ns / 1ps
// synthesis translate_on
// turn off superfluous verilog processor warnings
// altera message_level Level1
// altera message_off 10034 10035 10036 10037 10230 10240 10030
module sdram (
// inputs:
az_addr,
az_be_n,
az_cs,
az_data,
az_rd_n,
az_wr_n,
clk,
reset_n,
// outputs:
za_data,
za_valid,
za_waitrequest,
zs_addr,
zs_ba,
zs_cas_n,
zs_cke,
zs_cs_n,
zs_dq,
zs_dqm,
zs_ras_n,
zs_we_n
)
;
output [ 15: 0] za_data;
output za_valid;
output za_waitrequest;
output [ 11: 0] zs_addr;
output [ 1: 0] zs_ba;
output zs_cas_n;
output zs_cke;
output zs_cs_n;
inout [ 15: 0] zs_dq;
output [ 1: 0] zs_dqm;
output zs_ras_n;
output zs_we_n;
input [ 21: 0] az_addr;
input [ 1: 0] az_be_n;
input az_cs;
input [ 15: 0] az_data;
input az_rd_n;
input az_wr_n;
input clk;
input reset_n;
wire [ 23: 0] CODE;
reg ack_refresh_request;
reg [ 21: 0] active_addr;
wire [ 1: 0] active_bank;
reg active_cs_n;
reg [ 15: 0] active_data;
reg [ 1: 0] active_dqm;
reg active_rnw;
wire almost_empty;
wire almost_full;
wire bank_match;
wire [ 7: 0] cas_addr;
wire clk_en;
wire [ 3: 0] cmd_all;
wire [ 2: 0] cmd_code;
wire cs_n;
wire csn_decode;
wire csn_match;
wire [ 21: 0] f_addr;
wire [ 1: 0] f_bank;
wire f_cs_n;
wire [ 15: 0] f_data;
wire [ 1: 0] f_dqm;
wire f_empty;
reg f_pop;
wire f_rnw;
wire f_select;
wire [ 40: 0] fifo_read_data;
reg [ 11: 0] i_addr;
reg [ 3: 0] i_cmd;
reg [ 2: 0] i_count;
reg [ 2: 0] i_next;
reg [ 2: 0] i_refs;
reg [ 2: 0] i_state;
reg init_done;
reg [ 11: 0] m_addr /* synthesis ALTERA_ATTRIBUTE = "FAST_OUTPUT_REGISTER=ON" */;
reg [ 1: 0] m_bank /* synthesis ALTERA_ATTRIBUTE = "FAST_OUTPUT_REGISTER=ON" */;
reg [ 3: 0] m_cmd /* synthesis ALTERA_ATTRIBUTE = "FAST_OUTPUT_REGISTER=ON" */;
reg [ 2: 0] m_count;
reg [ 15: 0] m_data /* synthesis ALTERA_ATTRIBUTE = "FAST_OUTPUT_REGISTER=ON ; FAST_OUTPUT_ENABLE_REGISTER=ON" */;
reg [ 1: 0] m_dqm /* synthesis ALTERA_ATTRIBUTE = "FAST_OUTPUT_REGISTER=ON" */;
reg [ 8: 0] m_next;
reg [ 8: 0] m_state;
reg oe /* synthesis ALTERA_ATTRIBUTE = "FAST_OUTPUT_ENABLE_REGISTER=ON" */;
wire pending;
wire rd_strobe;
reg [ 2: 0] rd_valid;
reg [ 13: 0] refresh_counter;
reg refresh_request;
wire rnw_match;
wire row_match;
wire [ 23: 0] txt_code;
reg za_cannotrefresh;
reg [ 15: 0] za_data /* synthesis ALTERA_ATTRIBUTE = "FAST_INPUT_REGISTER=ON" */;
reg za_valid;
wire za_waitrequest;
wire [ 11: 0] zs_addr;
wire [ 1: 0] zs_ba;
wire zs_cas_n;
wire zs_cke;
wire zs_cs_n;
wire [ 15: 0] zs_dq;
wire [ 1: 0] zs_dqm;
wire zs_ras_n;
wire zs_we_n;
assign clk_en = 1;
//s1, which is an e_avalon_slave
assign zs_cs_n, zs_ras_n, zs_cas_n, zs_we_n = m_cmd;
assign zs_addr = m_addr;
assign zs_cke = clk_en;
assign zs_dq = oe?m_data:161'bz;
assign zs_dqm = m_dqm;
assign zs_ba = m_bank;
assign f_select = f_pop & pending;
assign f_cs_n = 1'b0;
assign cs_n = f_select ? f_cs_n : active_cs_n;
assign csn_decode = cs_n;
assign f_rnw, f_addr, f_dqm, f_data = fifo_read_data;
sdram_input_efifo_module the_sdram_input_efifo_module
(
.almost_empty (almost_empty),
.almost_full (almost_full),
.clk (clk),
.empty (f_empty),
.full (za_waitrequest),
.rd (f_select),
.rd_data (fifo_read_data),
.reset_n (reset_n),
.wr ((~az_wr_n | ~az_rd_n) & !za_waitrequest),
.wr_data (az_wr_n, az_addr, az_wr_n ? 2'b0 : az_be_n, az_data)
);
assign f_bank = f_addr[21],f_addr[8];
// Refresh/init counter.
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
refresh_counter <= 10000;
else if (refresh_counter == 0)
refresh_counter <= 1562;
else
refresh_counter <= refresh_counter - 1'b1;
end
// Refresh request signal.
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
refresh_request <= 0;
else if (1)
refresh_request <= ((refresh_counter == 0) | refresh_request) & ~ack_refresh_request & init_done;
end
// Generate an Interrupt if two ref_reqs occur before one ack_refresh_request
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
za_cannotrefresh <= 0;
else if (1)
za_cannotrefresh <= (refresh_counter == 0) & refresh_request;
end
// Initialization-done flag.
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
init_done <= 0;
else if (1)
init_done <= init_done | (i_state == 3'b101);
end
// **** Init FSM ****
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
i_state <= 3'b000;
i_next <= 3'b000;
i_cmd <= 4'b1111;
i_addr <= 121'b1;
i_count <= 31'b0;
end
else
begin
i_addr <= 121'b1;
case (i_state) // synthesis parallel_case full_case
3'b000: begin
i_cmd <= 4'b1111;
i_refs <= 3'b0;
//Wait for refresh count-down after reset
if (refresh_counter == 0)
i_state <= 3'b001;
end // 3'b000
3'b001: begin
i_state <= 3'b011;
i_cmd <= 11'b0,3'h2;
i_count <= 1;
i_next <= 3'b010;
end // 3'b001
3'b010: begin
i_cmd <= 11'b0,3'h1;
i_refs <= i_refs + 1'b1;
i_state <= 3'b011;
i_count <= 7;
// Count up init_refresh_commands
if (i_refs == 3'h1)
i_next <= 3'b111;
else
i_next <= 3'b010;
end // 3'b010
3'b011: begin
i_cmd <= 11'b0,3'h7;
//WAIT til safe to Proceed...
if (i_count > 1)
i_count <= i_count - 1'b1;
else
i_state <= i_next;
end // 3'b011
3'b101: begin
i_state <= 3'b101;
end // 3'b101
3'b111: begin
i_state <= 3'b011;
i_cmd <= 11'b0,3'h0;
i_addr <= 21'b0,1'b0,2'b00,3'h3,4'h0;
i_count <= 4;
i_next <= 3'b101;
end // 3'b111
default: begin
i_state <= 3'b000;
end // default
endcase // i_state
end
end
assign active_bank = active_addr[21],active_addr[8];
assign csn_match = active_cs_n == f_cs_n;
assign rnw_match = active_rnw == f_rnw;
assign bank_match = active_bank == f_bank;
assign row_match = active_addr[20 : 9] == f_addr[20 : 9];
assign pending = csn_match && rnw_match && bank_match && row_match && !f_empty;
assign cas_addr = f_select ? 41'b0,f_addr[7 : 0] : 41'b0,active_addr[7 : 0] ;
// **** Main FSM ****
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
begin
m_state <= 9'b000000001;
m_next <= 9'b000000001;
m_cmd <= 4'b1111;
m_bank <= 2'b00;
m_addr <= 12'b000000000000;
m_data <= 16'b0000000000000000;
m_dqm <= 2'b00;
m_count <= 3'b000;
ack_refresh_request <= 1'b0;
f_pop <= 1'b0;
oe <= 1'b0;
end
else
begin
f_pop <= 1'b0;
oe <= 1'b0;
case (m_state) // synthesis parallel_case full_case
9'b000000001: begin
//Wait for init-fsm to be done...
if (init_done)
begin
//Hold bus if another cycle ended to arf.
if (refresh_request)
m_cmd <= 11'b0,3'h7;
else
m_cmd <= 4'b1111;
ack_refresh_request <= 1'b0;
//Wait for a read/write request.
if (refresh_request)
begin
m_state <= 9'b001000000;
m_next <= 9'b010000000;
m_count <= 1;
active_cs_n <= 1'b1;
end
else if (!f_empty)
begin
f_pop <= 1'b1;
active_cs_n <= f_cs_n;
active_rnw <= f_rnw;
active_addr <= f_addr;
active_data <= f_data;
active_dqm <= f_dqm;
m_state <= 9'b000000010;
end
end
else
begin
m_addr <= i_addr;
m_state <= 9'b000000001;
m_next <= 9'b000000001;
m_cmd <= i_cmd;
end
end // 9'b000000001
9'b000000010: begin
m_state <= 9'b000000100;
m_cmd <= csn_decode,3'h3;
m_bank <= active_bank;
m_addr <= active_addr[20 : 9];
m_data <= active_data;
m_dqm <= active_dqm;
m_count <= 2;
m_next <= active_rnw ? 9'b000001000 : 9'b000010000;
end // 9'b000000010
9'b000000100: begin
// precharge all if arf, else precharge csn_decode
if (m_next == 9'b010000000)
m_cmd <= 11'b0,3'h7;
else
m_cmd <= csn_decode,3'h7;
//Count down til safe to Proceed...
if (m_count > 1)
m_count <= m_count - 1'b1;
else
m_state <= m_next;
end // 9'b000000100
9'b000001000: begin
m_cmd <= csn_decode,3'h5;
m_bank <= f_select ? f_bank : active_bank;
m_dqm <= f_select ? f_dqm : active_dqm;
m_addr <= cas_addr;
//Do we have a transaction pending?
if (pending)
begin
//if we need to ARF, bail, else spin
if (refresh_request)
begin
m_state <= 9'b000000100;
m_next <= 9'b000000001;
m_count <= 2;
end
else
begin
f_pop <= 1'b1;
active_cs_n <= f_cs_n;
active_rnw <= f_rnw;
active_addr <= f_addr;
active_data <= f_data;
active_dqm <= f_dqm;
end
end
else
begin
//correctly end RD spin cycle if fifo mt
if (~pending & f_pop)
m_cmd <= csn_decode,3'h7;
m_state <= 9'b100000000;
end
end // 9'b000001000
9'b000010000: begin
m_cmd <= csn_decode,3'h4;
oe <= 1'b1;
m_data <= f_select ? f_data : active_data;
m_dqm <= f_select ? f_dqm : active_dqm;
m_bank <= f_select ? f_bank : active_bank;
m_addr <= cas_addr;
//Do we have a transaction pending?
if (pending)
begin
//if we need to ARF, bail, else spin
if (refresh_request)
begin
m_state <= 9'b000000100;
m_next <= 9'b000000001;
m_count <= 2;
end
else
begin
f_pop <= 1'b1;
active_cs_n <= f_cs_n;
active_rnw <= f_rnw;
active_addr <= f_addr;
active_data <= f_data;
active_dqm <= f_dqm;
end
end
else
begin
//correctly end WR spin cycle if fifo empty
if (~pending & f_pop)
begin
m_cmd <= csn_decode,3'h7;
oe <= 1'b0;
end
m_state <= 9'b100000000;
end
end // 9'b000010000
9'b000100000: begin
m_cmd <= csn_decode,3'h7;
//Count down til safe to Proceed...
if (m_count > 1)
m_count <= m_count - 1'b1;
else
begin
m_state <= 9'b001000000;
m_count <= 1;
end
end // 9'b000100000
9'b001000000: begin
m_state <= 9'b000000100;
m_addr <= 121'b1;
// precharge all if arf, else precharge csn_decode
if (refresh_request)
m_cmd <= 11'b0,3'h2;
else
m_cmd <= csn_decode,3'h2;
end // 9'b001000000
9'b010000000: begin
ack_refresh_request <= 1'b1;
m_state <= 9'b000000100;
m_cmd <= 11'b0,3'h1;
m_count <= 7;
m_next <= 9'b000000001;
end // 9'b010000000
9'b100000000: begin
m_cmd <= csn_decode,3'h7;
//if we need to ARF, bail, else spin
if (refresh_request)
begin
m_state <= 9'b000000100;
m_next <= 9'b000000001;
m_count <= 1;
end
else //wait for fifo to have contents
if (!f_empty)
//Are we 'pending' yet?
if (csn_match && rnw_match && bank_match && row_match)
begin
m_state <= f_rnw ? 9'b000001000 : 9'b000010000;
f_pop <= 1'b1;
active_cs_n <= f_cs_n;
active_rnw <= f_rnw;
active_addr <= f_addr;
active_data <= f_data;
active_dqm <= f_dqm;
end
else
begin
m_state <= 9'b000100000;
m_next <= 9'b000000001;
m_count <= 1;
end
end // 9'b100000000
// synthesis translate_off
default: begin
m_state <= m_state;
m_cmd <= 4'b1111;
f_pop <= 1'b0;
oe <= 1'b0;
end // default
// synthesis translate_on
endcase // m_state
end
end
assign rd_strobe = m_cmd[2 : 0] == 3'h5;
//Track RD Req's based on cas_latency w/shift reg
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
rd_valid <= 31'b0;
else
rd_valid <= (rd_valid << 1) | 21'b0, rd_strobe ;
end
// Register dq data.
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
za_data <= 0;
else
za_data <= zs_dq;
end
// Delay za_valid to match registered data.
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
za_valid <= 0;
else if (1)
za_valid <= rd_valid[2];
end
assign cmd_code = m_cmd[2 : 0];
assign cmd_all = m_cmd;
//synthesis translate_off
SIMULATION-ONLY CONTENTS
assign txt_code = (cmd_code == 3'h0)? 24'h4c4d52 :
(cmd_code == 3'h1)? 24'h415246 :
(cmd_code == 3'h2)? 24'h505245 :
(cmd_code == 3'h3)? 24'h414354 :
(cmd_code == 3'h4)? 24'h205752 :
(cmd_code == 3'h5)? 24'h205244 :
(cmd_code == 3'h6)? 24'h425354 :
(cmd_code == 3'h7)? 24'h4e4f50 :
24'h424144;
assign CODE = &(cmd_all|4'h7) ? 24'h494e48 : txt_code;
END SIMULATION-ONLY CONTENTS
//synthesis translate_on
endmodule
A13-12
以上是关于OV7670基于FPGA的OV7670摄像头介绍和使用的主要内容,如果未能解决你的问题,请参考以下文章
VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)
VerilogFPGA驱动Ov7670/Ov7725搭建视频通路(RGB565灰度图)