【OV7670】基于FPGA的OV7670摄像头介绍和使用
时间:2023-05-23 09:07:00
1.软件版本
quartusii12.1
2.本算法理论知识
OV7670摄像头模块
带384Kb FIFO 数字摄像头 手动变焦
OV7670总共有656*488个像素中有640个*有效像素480个W)。
支持RawRGB、RGB(GBR4:2:2,RGB565/RGB555/RGB444),YUV(4:2:2)和YCbCr(4:2:2)输出格式。
RGB565彩色模式,一个像素占两个字节
前五个低字节用来表示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 像素数据
本部分的写作内容请参考我提供的文献:
这个里面详细介绍了OV其实7640的介绍是完全一样的,你可以全面参考这部分。
在设计这部分时,我主要使用以下技术点:
第一,相机驱动
二是摄像头视频采集
写论文时,请参考上述论文 这部分,稍微改一下,基本上我们的摄像头和这篇论文基本一致。然后你可以用我上面写的关于这个摄像头的介绍。
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:{16{1'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 <= {12{1'b1}};
i_count <= {3{1'b0}};
end
else
begin
i_addr <= {12{1'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 <= {
{1{1'b0}},3'h2};
i_count <= 1;
i_next <= 3'b010;
end // 3'b001
3'b010: begin
i_cmd <= {
{1{1'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 <= {
{1{1'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 <= {
{1{1'b0}},3'h0};
i_addr <= {
{2{1'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 ? { {4{1'b0}},f_addr[7 : 0] } : { {4{1'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 <= {
{1{1'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 <= {
{1{1'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 <= {12{1'b1}};
// precharge all if arf, else precharge csn_decode
if (refresh_request)
m_cmd <= {
{1{1'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 <= {
{1{1'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 <= {3{1'b0}};
else
rd_valid <= (rd_valid << 1) | { {2{1'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