FPGA:ov7725摄像头通过VGA/HDMI显示RGB565格式的图像

前言

VGA显示模式:分辨率为640*480,刷新速率为60hz
ov7725输出格式:RGB565
ov7725摄像头分辨率:640×480
ov7725输出时序:VGA时序
需求:处理ov7725传输到FPGA的图像,经过图像处理后,再将图像使用VGA传输到显示屏

正文

在这里插入图片描述

原理图上摄像头和FPGA的引脚相连的地方,下图位为野火开发板原理图:
在这里插入图片描述
在这里插入图片描述

一、摄像头配置模块

在使用ov7725之前,需要对ov7725里面的寄存器进行配置,这里使用I2C对其内部寄存器进行配置
在这里插入图片描述

1.1 寄存器配置模块

这个模块用来提供寄存器的地址和数据
如对所有寄存器进行复位配置:7'd0 : i2c_data <= {8'h12, 8'h80};
在ov7725中,高8位是地址,低8位是数据

通过这些寄存器的配置,可以控制摄像头输出图像的分辨率大小、图像格式、图像处理及图像方向

// 功能:把数据传给i2c驱动,进而实现对摄像头内部70个寄存器进行配置
// 传地址和数据给驱动
// 注意信号间的依赖关系

module i2c_ov7725_rgb565_cfg(
	input 				clk,//由i2c驱动模块输出的时钟
	input 				i2c_done,// i2c完成一次传输,从 start——> stop 完成一个寄存器的配置,一共要配置70个
	input 				rst_n,
	
	output reg 			init_done,//初始化完成信号:70个寄存器都配置完成,并且最后一个寄存器的i2c_done也传回来了,此时即可拉高该信号
	output reg [15:0] 	i2c_data,//高8位地址、低8位数据
	output reg 			i2c_exec //拉高时, i2c 开始工作(start信号)
);

parameter REG_70 = 70; //需要配置的寄存器总数

// 延时计数器 
// 延时2次:
// 1、复位释放后延时
// 2、第一个寄存器配置完成后,延时
reg [10:0] cnt_delay_2;//实现延迟2次的计数器,计到1023清零
reg [6:0]  cnt_reg_70;

// =============================================================================
// 延时的目的:等待ov7725稳定下来,ov7725数据手册规定,复位后需要延时
// 复位:1、硬件复位:上电复位、复位按键复位
//       2、软件复位:配置的第一个寄存器,复位所有寄存器,即对ov7725进行软件复位
//
//					 第一个输出信号:i2c_exec
// =============================================================================

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		cnt_delay_2 <= 10'd0;
	end	
	else if(cnt_delay_2 < 10'd1023)begin
		cnt_delay_2 <= cnt_delay_2 + 10'd1;
	end
	else if((cnt_reg_70 == 7'd1) && (i2c_done))begin// 完成第一个寄存器的配置
		cnt_delay_2 <= 10'd0;
	end	

end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		cnt_reg_70 <= 7'd0;
	end	
	else if(i2c_exec)begin
		cnt_reg_70 <= cnt_reg_70 + 7'd1;// i2c开始信号到来,就代表开始配置寄存器
	end
	else
		cnt_reg_70 <= cnt_reg_70;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		i2c_exec <= 1'd0;
	end
	else if(cnt_delay_2 == 10'd1022)begin //上电复位后已经延时1ms,才能去配置第一个寄存器;然后配置完第一个复位寄存器后,再延时1ms
		i2c_exec <= 1'd1;
	end
	else if(init_done && (cnt_reg_70 != 7'd1) && (cnt_reg_70 < REG_70)) // 上一个配置完成+除去第一个复位寄存器+还没配置完70个
		i2c_exec <= 1'd1;
	else
		i2c_exec <= 1'd0;
end

// =============================================================================
//					 第二个输出信号:init_done
// =============================================================================
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		init_done <= 1'd0;
	end
	else if((cnt_reg_70 == REG_70) && (i2c_done))begin //配置70个寄存器且最后一个配置完成信号传回来了
		init_done <= 1'd1;
	end
	else
		init_done <= 1'd0;
	
end
// =============================================================================
//					 第三个输出信号:[15:0]i2c_data
// =============================================================================   
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		i2c_data <= 16'd0;
	end
	else begin
		case(cnt_reg_70)
            //先对寄存器进行软件复位,使寄存器恢复初始值
            //寄存器软件复位后,需要延时1ms才能配置其它寄存器
            7'd0  : i2c_data <= {8'h12, 8'h80}; //COM7 BIT[7]:复位所有的寄存器
            7'd1  : i2c_data <= {8'h3d, 8'h03}; //COM12 模拟过程直流补偿
            7'd2  : i2c_data <= {8'h15, 8'h00}; //COM10 href/vsync/pclk/data信号控制
            7'd3  : i2c_data <= {8'h17, 8'h26}; //HSTART 水平起始位置
            7'd4  : i2c_data <= {8'h18, 8'ha0}; //HSIZE 水平尺寸
            7'd5  : i2c_data <= {8'h19, 8'h07}; //VSTRT 垂直起始位置
            7'd6  : i2c_data <= {8'h1a, 8'hf0}; //VSIZE 垂直尺寸 

            7'd7  : i2c_data <= {8'h32, 8'h00}; //HREF 图像开始和尺寸控制,控制低位
            7'd8  : i2c_data <= {8'h29, 8'ha0}; //HOutSize 水平输出尺寸
            7'd9  : i2c_data <= {8'h2a, 8'h00}; //EXHCH 虚拟像素MSB
            7'd10 : i2c_data <= {8'h2b, 8'h00}; //EXHCL 虚拟像素LSB
            7'd11 : i2c_data <= {8'h2c, 8'hf0}; //VOutSize 垂直输出尺寸
            7'd12 : i2c_data <= {8'h0d, 8'h41}; //COM4 PLL倍频设置(multiplier)  设置4倍频
                                                //Bit[7:6]:  0:1x 1:4x 2:6x 3:8x
            7'd13 : i2c_data <= {8'h11, 8'h00}; //CLKRC 内部时钟配置 
                                                //Freq=multiplier/[(CLKRC[5:0]+1)*2]
            7'd14 : i2c_data <= {8'h12, 8'h06}; //COM7 输出VGA RGB565格式                                     
            7'd15 : i2c_data <= {8'h0c, 8'h10}; //COM3 Bit[0]: 0:图像数据 1:彩条测试
            //DSP 控制
            7'd16 : i2c_data <= {8'h42, 8'h7f}; //TGT_B 黑电平校准蓝色通道目标值
            7'd17 : i2c_data <= {8'h4d, 8'h09}; //FixGain 模拟增益放大器
            7'd18 : i2c_data <= {8'h63, 8'hf0}; //AWB_Ctrl0 自动白平衡控制字节0
            7'd19 : i2c_data <= {8'h64, 8'hff}; //DSP_Ctrl1 DSP控制字节1
            7'd20 : i2c_data <= {8'h65, 8'h00}; //DSP_Ctrl2 DSP控制字节2
            7'd21 : i2c_data <= {8'h66, 8'h00}; //DSP_Ctrl3 DSP控制字节3
            7'd22 : i2c_data <= {8'h67, 8'h00}; //DSP_Ctrl4 DSP控制字节4    
            //AGC AEC AWB        
            //COM8 Bit[2]:自动增益使能 Bit[1]:自动白平衡使能 Bit[0]:自动曝光功能
            7'd23 : i2c_data <= {8'h13, 8'hff}; //COM8 
            7'd24 : i2c_data <= {8'h0f, 8'hc5}; //COM6
            7'd25 : i2c_data <= {8'h14, 8'h11};  
            7'd26 : i2c_data <= {8'h22, 8'h98}; 
            7'd27 : i2c_data <= {8'h23, 8'h03};  
            7'd28 : i2c_data <= {8'h24, 8'h40}; 
            7'd29 : i2c_data <= {8'h25, 8'h30};  
            7'd30: i2c_data <= {8'h26, 8'ha1};      
            7'd31: i2c_data <= {8'h6b, 8'haa}; 
            7'd32: i2c_data <= {8'h13, 8'hff};  
            //matrix sharpness brightness contrast UV
            7'd33 : i2c_data <= {8'h90, 8'h0a}; //EDGE1 边缘增强控制1
            //DNSOff 降噪阈值下限,仅在自动模式下有效
            7'd34 : i2c_data <= {8'h91, 8'h01}; //DNSOff 
            7'd35 : i2c_data <= {8'h92, 8'h01}; //EDGE2 锐度(边缘增强)强度上限
            7'd36 : i2c_data <= {8'h93, 8'h01}; //EDGE3 锐度(边缘增强)强度下限
            7'd37 : i2c_data <= {8'h94, 8'h5f}; //MTX1 矩阵系数1
            7'd38 : i2c_data <= {8'h95, 8'h53}; //MTX1 矩阵系数2
            7'd39 : i2c_data <= {8'h96, 8'h11}; //MTX1 矩阵系数3
            7'd40 : i2c_data <= {8'h97, 8'h1a}; //MTX1 矩阵系数4
            7'd41 : i2c_data <= {8'h98, 8'h3d}; //MTX1 矩阵系数5
            7'd42 : i2c_data <= {8'h99, 8'h5a}; //MTX1 矩阵系数6
            7'd43 : i2c_data <= {8'h9a, 8'h1e}; //MTX_Ctrl 矩阵控制
            7'd44 : i2c_data <= {8'h9b, 8'h3f}; //BRIGHT 亮度
            7'd45 : i2c_data <= {8'h9c, 8'h25}; //CNST 对比度            
            7'd46 : i2c_data <= {8'h9e, 8'h81}; 
            7'd47 : i2c_data <= {8'ha6, 8'h06}; //SDE 特殊数字效果控制
            7'd48 : i2c_data <= {8'ha7, 8'h65}; //USAT "U"饱和增益
            7'd49 : i2c_data <= {8'ha8, 8'h65}; //VSAT "V"饱和增益            
            7'd50 : i2c_data <= {8'ha9, 8'h80}; //VSAT "V"饱和增益   
            7'd51 : i2c_data <= {8'haa, 8'h80}; //VSAT "V"饱和增益
            //伽马控制 :伽马校准和文档上的一样
            7'd52 : i2c_data <= {8'h7e, 8'h0c}; 
            7'd53 : i2c_data <= {8'h7f, 8'h16}; 
            7'd54 : i2c_data <= {8'h80, 8'h2a}; 
            7'd55 : i2c_data <= {8'h81, 8'h4e}; 
            7'd56 : i2c_data <= {8'h82, 8'h61}; 
            7'd57 : i2c_data <= {8'h83, 8'h6f}; 
            7'd58 : i2c_data <= {8'h84, 8'h7b}; 
            7'd59 : i2c_data <= {8'h85, 8'h86};   
            7'd60 : i2c_data <= {8'h86, 8'h8e}; 
            7'd61 : i2c_data <= {8'h87, 8'h97}; 
            7'd62 : i2c_data <= {8'h88, 8'ha4}; 
            7'd63 : i2c_data <= {8'h89, 8'haf}; 
            7'd64 : i2c_data <= {8'h8a, 8'hc5}; 
            7'd65 : i2c_data <= {8'h8b, 8'hd7}; 
            7'd66 : i2c_data <= {8'h8c, 8'he8}; 
            7'd67 : i2c_data <= {8'h8d, 8'h20}; 
            
            7'd68 : i2c_data <= {8'h0e, 8'h65}; //COM5
            7'd69 : i2c_data <= {8'h09, 8'h00}; //COM2  Bit[1:0] 输出电流驱动能力
            //只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
            default:i2c_data <=	{8'h1C, 8'h7F}; //MIDH 制造商ID 高8位
        endcase
	end
	else	
end
endmodule

1.2 i2c驱动

这个模块用来传输需要配置的寄存器的值

//
module i2c_dri(
	input                clk        , //250K   
    input                rst_n      ,   
                   

				   
    //i2c 接口                      
    input                i2c_exec   ,  //I2C触发执行信号
    input                bit_ctrl   ,  //字地址位控制(16b/8b)
    input                i2c_rh_wl  ,  //I2C读写控制信号
    input        [15:0]  i2c_addr   ,  //I2C器件内地址
    input        [ 7:0]  i2c_data_w ,  //I2C要写的数据
    output  reg  [ 7:0]  i2c_data_r ,  //I2C读出的数据
    output  reg          i2c_done   ,  //I2C一次操作完成
    output  reg          i2c_ack    ,  //I2C应答标志 0:应答 1:未应答
    output  reg          scl        ,  //I2C的SCL时钟信号
    inout                sda        ,  //I2C的SDA信号
                                       
    //用户接口                   
    output  reg          dri_clk       //驱动scl、sda这2根信号线,从clk  4倍频得到25K*4=1M
);

parameter SLAVE_ADDR = 7'b1010000;// 从机地址
parameter CLK_FREQ   = 26'd50_000_000;
parameter I2C_FREQ   = 18'd250_000;

parameter DIV_CLK_MAX = (clk_cnt_24 >> 1'd1)-1'd1;//分频计数器计满值

// 定义状态量
localparam IDLE 	  = 8'b0000_0001;
localparam SLAVE_ADDR = 8'b0000_0010;
localparam ADDR_H	  = 8'b0000_0100;
localparam ADDR_L 	  = 8'b0000_1000;
localparam W_DATA     = 8'b0001_0000;
localparam R_ADDR	  = 8'b0010_0000;
localparam R_DATA 	  = 8'b0100_0000;
localparam STOP       = 8'b1000_0000;
reg state;
reg next_state;


assign sda 	  = sda_en ? sda_out : 1'bz;
assign sda_in = sda;
// ============================================================
// 求分频系数
// 分频系数越小,频率越大
wire [5:0] div_50; //分频系数
assign div_50 = (CLK_FREQ/I2C_FREQ) >> 2'd2;

// 通过求得的分频系数,作为分频数
reg [4:0] clk_cnt_24;

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		clk_cnt_24 <= 5'd0;
	end
	else if(clk_cnt_24 == DIV_CLK_MAX)begin
		clk_cnt_24 <= 5'd0;
	end
	else	
		clk_cnt_24 <= clk_cnt_24 + 5'd1;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		dri_clk <= 1'd0;
	end
	else if(clk_cnt_24 == DIV_CLK_MAX)begin
		dri_clk <= ~dri_clk;
	end
	else
		dri_clk <= dri_clk;
end
// ============================================================

// ----------------------------------------------------------------------
// 状态机第一段
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		state <= IDLE;
	end
	else begin
		state <= next_state;
	end
end

// 状态机第二段
// 状态之间的转换看跳转图!!很清晰
reg now_state_done;
reg bit_ctrl;
always@(*)begin
	next_state <= IDLE;
	case(state)
		IDLE:
			if(i2c_exec)
				next_state = SLAVE_ADDR;
			else
				next_state = next_state;
		SLAVE_ADDR:
			if(now_state_done)begin
				if(bit_ctrl)// bit_ctrl=1表示传16bit地址
					next_state = ADDR_H;//高8位
				else
					next_state = ADDR_L;
			else
				next_state = SLAVE_ADDR;
			end
		ADDR_H:
			if(now_state_done)
				next_state = ADDR_L;
			else
				next_state = ADDR_H;
		// 读数据		
		ADDR_L:
			if(now_state_done)begin
				if(wr_flage)//读
					next_state = R_ADDR;
				else
					next_state = W_DATA;
			end
			else 
				next_state = ADDR_L;
		R_ADDR:
			if(now_state_done)
				next_state = R_DATA;
			else
				next_state = R_ADDR;
		R_DATA:
			if(now_state_done)
				next_state = STOP;
			else
				next_state = R_DATA;
		//写数据
		W_DATA:
			if(now_state_done)
				next_state = STOP;
			else
				next_state = W_DATA;
		STOP:
			if(now_state_done)
				next_state = IDLE;
			else
				next_state = STOP;
		default: 
			next_state = IDLE;
	endcase
	
end
// 状态机第三段
//i2c的2个接口在空闲状态是高电平
reg      	now_state_done;
reg [6:0]	cnt_always_add;
reg 		i2c_rh_wl_reg;
reg [7:0]   i2c_data_w_reg;
reg [7:0]   i2c_data_r_reg;
reg [15:0]	i2c_addr_reg;
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		scl 	<= 1'd1;
		sda_out <= 1'd1;
		sda_en  <= 1'd1;
		// 一直在的信号
		now_state_done  <= 1'd0;
		cnt_always_add  <= 7'd0;
		
		i2c_rh_wl_reg   <= 1'd0;
		i2c_data_w_reg  <= 8'd0;
		i2c_data_r_reg  <= 8'd0;
		i2c_addr_reg    <= 16'd0;
		i2c_ack 		<= 1'd0;	
	end
	// 在外部让now_state_done=0,只有在内部每个状态完成后,才将其拉高
	now_state_done <= 1'd0;
	// 在外部一直+1,只有在内部结束才清零
	cnt_always_add <= cnt_always_add + 7'd1;
	case(state)
		IDLE:
			if(i2c_exec)begin//驱动IDLE的都是i2c_exec信号,将输入信号寄存到内部寄存器
				i2c_rh_wl_reg   <= i2c_rh_wl;//其他模块传来的读写控制信号
				i2c_data_w_reg  <= i2c_data_w;
				i2c_addr_reg 	<= i2c_addr;	
			end
		SLAVE_ADDR:begin // sda_out在scl低电平中间更新
			case(cnt_always_ad)
			7'd1:	scl 	<= 1'd1;
			7'd3:	scl 	<= 1'd0;
			7'd4:	sda_out <= SLAVE_ADDR[6];
			7'd5:	scl 	<= 1'd1;		
			7'd7:	scl 	<= 1'd0;		
			7'd8:	sda_out <= SLAVE_ADDR[5];
			7'd9:	scl 	<= 1'd1;		
			7'd11:	scl     <= 1'd0;
			7'd12:	sda_out <= SLAVE_ADDR[4];
			7'd13:	scl 	<= 1'd1;		
			7'd15:	scl 	<= 1'd0;
			7'd16:  sda_out <= SLAVE_ADDR[3];
			7'd17:	scl 	<= 1'd1;
			7'd19:	scl		<= 1'd0;
			7'd20:	sda_out <= SLAVE_ADDR[2];
			7'd21:  scl 	<= 1'd1;
			7'd23:	scl 	<= 1'd0;
			7'd24:	sda_out <= SLAVE_ADDR[1];
			7'd25:  scl 	<= 1'd1;
			7'd27:	scl 	<= 1'd0;
			7'd28:	sda_out <= SLAVE_ADDR[0];	
			7'd29:  scl 	<= 1'd1;			
			7'd31:	scl 	<= 1'd0;//=======================================================以上相同
			7'd32:	sda_out <= 1'd0;//最低位=0:写, 到此传完8bit数据
			7'd23:  scl 	<= 1'd1;			
			7'd35:	scl 	<= 1'd0;			
			7'd36:	sda_en  <= 1'd0;// sda为输入端口,输入从机给的应答信号		
			7'd37: scl 	<= 1'd1; 		
			7'd38:  begin
					st_done <= 1'b1;
					if(sda_in)//从机给的无应答信号
						i2c_ack <= 1'd1;//拉高,无应答
			end
//虽然在38时st_done已经拉高,此时状态已经跳转,但是跳转的是next_state,而此处case里面是state,state比next_state慢一拍,因此还是要执行39			
			7'd39:  begin
				cnt_always_add  <= 6'd0;
				scl 			<= 1'd0;//?????
			end
			default: ;
			endcase
		end
		
		ADDR_H:begin
			case(cnt_always_ad)
			7'd0:	begin
				sda_en 		<= 1'd1;//让sda位输出端口
				sda_out     <= i2c_addr_reg[15];//将寄存的地址输出
			end
			7'd1:	scl 	<= 1'd1;
			7'd3:	scl 	<= 1'd0;					
			7'd4:	sda_out <= i2c_addr_reg[14];			
			7'd5:	scl 	<= 1'd1;					
			7'd7:	scl 	<= 1'd0;					
			7'd8:	sda_out <= i2c_addr_reg[13];			
			7'd9:	scl 	<= 1'd1;					
			7'd11:	scl     <= 1'd0;			
			7'd12:	sda_out <= i2c_addr_reg[12];			
			7'd13:	scl 	<= 1'd1;					
			7'd15:	scl 	<= 1'd0;			
			7'd16:  sda_out <= i2c_addr_reg[11];			
			7'd17:	scl 	<= 1'd1;			
			7'd19:	scl		<= 1'd0;			
			7'd20:	sda_out <= i2c_addr_reg[10];			
			7'd21:  scl 	<= 1'd1;			
			7'd23:	scl 	<= 1'd0;			
			7'd24:	sda_out <= i2c_addr_reg[9];			
			7'd25:  scl 	<= 1'd1;			
			7'd27:	scl 	<= 1'd0;
			7'd28:	sda_out <= i2c_addr_reg[8];	
			7'd29:  scl 	<= 1'd1;			
			7'd31:	scl 	<= 1'd0;			
			7'd32:	sda_en 	<= 1'd1;			
			7'd33: scl 	<= 1'd1; 			
			7'd34:  begin
					st_done <= 1'b1;
					if(sda_in)//从机给的无应答信号
						i2c_ack <= 1'd1;//拉高,无应答
			end
			7'd35:  begin
				cnt_always_add  <= 6'd0;
				scl 			<= 1'd0;//?????
			end			
			default:;
			endcase
		end
		
		ADDR_L:begin
		case(cnt_always_add)
			7'd0:	begin
				sda_en 		<= 1'd1;//让sda位输出端口
				sda_out     <= i2c_addr_reg[7];//将寄存的地址输出
			end
			7'd1:	scl 	<= 1'd1;
			7'd3:	scl 	<= 1'd0;					
			7'd4:	sda_out <= i2c_addr_reg[6];			
			7'd5:	scl 	<= 1'd1;					
			7'd7:	scl 	<= 1'd0;					
			7'd8:	sda_out <= i2c_addr_reg[5];			
			7'd9:	scl 	<= 1'd1;					
			7'd11:	scl     <= 1'd0;			
			7'd12:	sda_out <= i2c_addr_reg[4];			
			7'd13:	scl 	<= 1'd1;					
			7'd15:	scl 	<= 1'd0;			
			7'd16:  sda_out <= i2c_addr_reg[3];			
			7'd17:	scl 	<= 1'd1;			
			7'd19:	scl		<= 1'd0;			
			7'd20:	sda_out <= i2c_addr_reg[2];		
			7'd21:  scl 	<= 1'd1;			
			7'd23:	scl 	<= 1'd0;			
			7'd24:	sda_out <= i2c_addr_reg[1];			
			7'd25:  scl 	<= 1'd1;			
			7'd27:	scl 	<= 1'd0;
			7'd28:	sda_out <= i2c_addr_reg[0];	
			7'd29:  scl 	<= 1'd1;			
			7'd31:	scl 	<= 1'd0;			
			7'd32:	sda_en 	<= 1'd1;			
			7'd33: scl 	<= 1'd1; 
			
			7'd34:  begin
					st_done <= 1'b1;
					if(sda_in)//从机给的无应答信号
						i2c_ack <= 1'd1;//拉高,无应答
			end
			7'd35:  begin
				cnt_always_add  <= 6'd0;
				scl 			<= 1'd0;//?????
			end			
			default:;
		endcase
	end
		
		W_DATA:begin
		case(cnt_always_add)
			7'd0:	begin
				sda_en 		<= 1'd1;
				sda_out     <= i2c_data_w_reg[7];
			end
			7'd1:	scl 	<= 1'd1;
			7'd3:	scl 	<= 1'd0;					
			7'd4:	sda_out <= i2c_data_w_reg[6];			
			7'd5:	scl 	<= 1'd1;					
			7'd7:	scl 	<= 1'd0;					
			7'd8:	sda_out <= i2c_data_w_reg[5];			
			7'd9:	scl 	<= 1'd1;					
			7'd11:	scl     <= 1'd0;			
			7'd12:	sda_out <= i2c_data_w_reg[4];			
			7'd13:	scl 	<= 1'd1;					
			7'd15:	scl 	<= 1'd0;		
			7'd16:  sda_out <= i2c_data_w_reg[3];		
			7'd17:	scl 	<= 1'd1;			
			7'd19:	scl		<= 1'd0;			
			7'd20:	sda_out <= i2c_data_w_reg[2];			
			7'd21:  scl 	<= 1'd1;			
			7'd23:	scl 	<= 1'd0;			
			7'd24:	sda_out <= i2c_data_w_reg[1];			
			7'd25:  scl 	<= 1'd1;			
			7'd27:	scl 	<= 1'd0;
			7'd28:	sda_out <= i2c_data_w_reg[0];	
			7'd29:  scl 	<= 1'd1;			
			7'd31:	scl 	<= 1'd0;			
			7'd32:	sda_en 	<= 1'd1;			
			7'd33: scl 	<= 1'd1; 			
			7'd34:  begin
					st_done <= 1'b1;
					if(sda_in)//从机给的无应答信号
						i2c_ack <= 1'd1;//拉高,无应答
			end
			7'd35:  begin
				cnt_always_add  <= 6'd0;
				scl 			<= 1'd0;//?????
			end				
		
			default:;
		endcase
	end
		
		R_ADDR:begin // 从哪个从机读
			case(cnt_always_ad)
			7'd0:	sda_en  <= 1'd1;			
			7'd1:	scl 	<= 1'd1;
			7'd3:	scl 	<= 1'd0;					
			7'd4:	sda_out <= SLAVE_ADDR[6];			
			7'd5:	scl 	<= 1'd1;					
			7'd7:	scl 	<= 1'd0;					
			7'd8:	sda_out <= SLAVE_ADDR[5];			
			7'd9:	scl 	<= 1'd1;					
			7'd11:	scl     <= 1'd0;			
			7'd12:	sda_out <= SLAVE_ADDR[4];			
			7'd13:	scl 	<= 1'd1;					
			7'd15:	scl 	<= 1'd0;			
			7'd16:  sda_out <= SLAVE_ADDR[3];			
			7'd17:	scl 	<= 1'd1;			
			7'd19:	scl		<= 1'd0;			
			7'd20:	sda_out <= SLAVE_ADDR[2];			
			7'd21:  scl 	<= 1'd1;			
			7'd23:	scl 	<= 1'd0;			
			7'd24:	sda_out <= SLAVE_ADDR[1];			
			7'd25:  scl 	<= 1'd1;			
			7'd27:	scl 	<= 1'd0;
			7'd28:	sda_out <= SLAVE_ADDR[0];	
			7'd29:  scl 	<= 1'd1;			
			7'd31:	scl 	<= 1'd0;//=======================================================以上相同
			7'd32:	sda_out <= 1'd1;//最低位=1:读, 到此传完8bit数据
			7'd33:  scl 	<= 1'd1;		
			7'd35:	scl 	<= 1'd0;			
			7'd36:	sda_en  <= 1'd0;// sda为输入端口,输入从机给的应答信号			
			7'd37: scl 	<= 1'd1; 			
			7'd38:  begin
					st_done <= 1'b1;
					if(sda_in)//从机给的无应答信号
						i2c_ack <= 1'd1;//拉高,无应答
			end
//虽然在38时st_done已经拉高,此时状态已经跳转,但是跳转的是next_state,而此处case里面是state,state比next_state慢一拍,因此还是要执行39			
			7'd39:  begin
				cnt_always_add  <= 6'd0;
				scl 			<= 1'd0;//?????
			end
			default: ;
			endcase
		end
		
		R_DATA:begin
			case(cnt_always_ad)
			7'd0:	sda_en  <= 1'd0;				
			7'd1:	i2c_data_r_reg[7] <= sda_in;
			7'd3:	scl 	<= 1'd0;			
			7'd5:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[6] <= sda_in;
			end				
			7'd7:	scl 	<= 1'd0;			
			7'd9:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[5] <= sda_in;
			end						
			7'd11:	scl     <= 1'd0;		
			7'd13:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[4] <= sda_in;
			end	
			7'd15:	scl 	<= 1'd0;			
			7'd17:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[3] <= sda_in;
			end				
			7'd19:	scl		<= 1'd0;
			7'd21:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[2] <= sda_in;
			end				
			7'd23:	scl 	<= 1'd0;			
			7'd25:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[1] <= sda_in;
			end				
			7'd27:	scl 	<= 1'd0;
			7'd29:	begin
				scl 			  <= 1'd1;
				i2c_data_r_reg[0] <= sda_in;
			end	
			
			7'd31:	scl 	<= 1'd0;//=======================================================以上相同
			7'd32:	sda_en  <= 1'd1;//不需要从机应答
			7'd33:  scl 	<= 1'd1;			
			7'd34:  now_state_done 	<= 1'd1;
			
			7'd35:	begin
				scl 		<= 1'd0;
				cnt 		<= 1'd0;
				i2c_data_r	<=	i2c_data_r_reg;
			end
			
			default: ;
			endcase			
		end
		
		STOP: begin
                case(cnt)
                    7'd0: begin
                        sda_en <= 1'b1;             //结束I2C
                        sda_out <= 1'b0;
                    end
                    7'd1 : scl     <= 1'b1;
                    7'd3 : sda_out <= 1'b1;
                    7'd15: now_state_done <= 1'b1;
                    7'd16: begin
                        cnt      <= 1'b0;
                        i2c_done <= 1'b1;            //向上层模块传递I2C结束信号
                    end
                    default  : ;
                endcase			
		end	
	default: ;	
	endcase
end

endmodule 

二、图像采集模块

对摄像头的内存器配置完成后,摄像头就可以开始工作啦

2.1 ov7725输出的时序图

要看懂ov7725的VGA输出时序,才能在正确的地方采集到摄像头传输过来的数据
在这里插入图片描述
上面的信号是摄像头输出的,其中href和hsync用同一个引脚输出的,为了方便,使用href作为行同步信号
通过观察上图可知:

  1. 场同步信号有效部分传输480行
  2. 行同步信号有效部分传输640个像素点
  3. 只有当 行同步信号=1 && 场同步信号=0 时,此时数据线上的数据才有效
行同步信号有效:传一行数据

将上面图中其中一段有效部分放大得到OV7725输出RGB565格式的时序图:在这里插入图片描述
由于ov7725在传输RGB图像时,只用到[9:2]这8根数据引脚,因此要传输GRB565的16位数据,需要分成2次传输

2.2 采集摄像头输出的图像

从上面可知,摄像头有8根数据线用来传输图像数据,但是要表示一个像素的完整信息,需要用16位数据,因此,需要将摄像头传输来的数据拼接成16bit

从摄像头采集数据,这个数据将存在SDRAM中

// 采集摄像头输出的图像数据
// RGB565格式的图像,一个像素点要用5+6+5=16bit 数据来表示
// 而ov7725的数据线只有8位,因此需要用2个时钟传输16bit数据

module cmos_capture_data(
	input 				    rst_n,
	// 从摄像头传入的信号
	input 				    cam_pclk,// 像素时钟周期
	input 				    cam_vsync,// 场同步
	input 				    cam_href, // 行同步
	input 	   	[7:0]		cam_data,
	// 用户接口	
	output reg 				coms_frame_vsync,// 帧有效
	output reg 				coms_frame_href, // 行有效
	output reg 				coms_frame_valid,// 该信号拉高,表示此时数据有效
	output reg 	[15:0] 		coms_frame_data	 // 采集到的有效数据
);

// 70个寄存器配置完成后,不采集前10帧图像
//ov7725数据手册:Table5中的ts
parameter WAIT_10_frame = 4'd10;

// 采集 场同步信号 的上升沿
reg		cam_vsync_reg1;
reg		cam_vsync_reg2;
reg		cam_href_reg1;
reg		cam_href_reg2;
wire pose_vsync;
always@(posedge cam_pclk or negedge rst_n)begin
	if(!rst_n )begin
		cam_vsync_reg1 <= 1'd0;
		cam_vsync_reg2 <= 1'd0;
	end
	else begin
		cam_vsync_reg1 <= cam_vsync;
		cam_vsync_reg2 <= cam_vsync_reg1;		
	end	
end

always@(posedge cam_pclk or negedge rst_n)begin
	if(!rst_n )begin
		cam_href_reg1 <= 1'd0;
		cam_href_reg2 <= 1'd0;
	end
	else begin
		cam_href_reg1  <= cam_href;
		cam_href_reg2 <= cam_href_reg1;		
	end	
end

assign pose_vsync = (~cam_vsync_reg2) && cam_vsync_reg1;
// 帧有效、行有效打2拍的原因:采集高8位用了1 clk,采集低8位用了1 clk ,拼接用了 1clk
// 因此数据满了2 clk,于是要将帧有效、行有效打2拍来和拼接后的数据同步
assign coms_frame_vsync = frame_flage ? cam_vsync_reg2  : 1'd0;
assign coms_frame_href  = frame_flage ? cam_href_reg2   : 1'd0;
// 这里对数据有效信号打1拍也是为了和拼接后的数据同步
assign coms_frame_valid = frame_flage ? byte_flage_reg1 : 1'd0;
assign coms_frame_data  = frame_flage ? data_contact_16 : 16'd0;

// 计数器:计数丢弃的10帧
reg [3:0] wait_10_frame;
always@(posedge cam_pclk or negedge rst_n)begin
	if(!rst_n )begin
		wait_10_frame <= 4'd0;
	end
	else if((pose_vsync) && (wait_10_frame < WAIT_10_frame))begin
		wait_10_frame <= wait_10_frame + 4'd1;
	end
	else
		wait_10_frame <= wait_10_frame;
end

// 等10帧后,有个信号告诉其他信号,数据已经可以用了
reg		frame_flage;
always@(posedge cam_pclk or negedge rst_n)begin
	if(!rst_n )begin
		frame_flage <= 1'd0;
	end
	else if((pose_vsync) && (wait_10_frame == WAIT_10_frame))begin
		frame_flage <= 1'd1;// 摄像头数据有效
	end
	else
		frame_flage <= frame_flage;	
end

// 8bit拼接为16bit
reg [7:0]  data_reg1;
reg [15:0] data_contact_16;
reg		   byte_flage;

always@(posedge cam_pclk or negedge rst_n)begin
	if(!rst_n )begin
		data_reg1    <= 8'd0;
		data_contact_16 <= 16'd0;
		byte_flage   <= 1'd0;
	end
	else if(cam_href)begin //行有效
		data_reg1  <= cam_data;// 先把高8位寄存起来
		byte_flage <= ~ byte_flage;// 表示已经传完8位,byte_flage=0,传输高8位;byte_flage=1,传输低8位
			if(byte_flage)begin
				data_contact_16 <= {data_reg1,cam_data};
			end
	end
	else begin
		data_reg1    <= 8'd0;
		//data_contact_16 <= 16'd0;
		byte_flage   <= 1'd0;
	end
end

// 将byte_flage打一拍
// 目的:使得数据有效信号和拼接后的数据同步
reg		byte_flage_reg1;
always@(posedge cam_pclk or negedge rst_n)begin
	if(!rst_n )begin
		byte_flage_reg1 <= 1'd0;
	end
	else begin
		byte_flage_reg1 <= byte_flage;
	end
end
endmodule 

三、图像存储模块

3.1 SDRAM

这部分先放一放

四、图像显示模块

4.1 VGA显示

该模块功能
1.给显示器输出行同步、场同步、图像数据
2.给SDRAM输入x、y坐标后,接收SDRAM传来的改 坐标下的图像数据

需要按照下面的时序,输出行同步、场同步,以及输出有效数据
在这里插入图片描述
在这里插入图片描述

module vga_div(
	input 				    vga_clk,//25M
	input 				    rst_n,//vga_clk稳定后,释放复位
	//给显示器
	output reg 				vga_hs,
	output reg 				vga_vs,
	output reg 	[15:0] 		vga_rgb,//三原色
	
	output					data_req,//向SADRAM请求输入像素点数据	
	output		[10:0]		pixel_x,
	output		[10:0]		pixel_y,
	input       [15:0] 		pixel_data//根据输入到SDRAM中的x、y坐标,得到从SFRAM输出的该店坐标的像素点数据
	
);

	//640*480 60FPS_25MHz
	parameter  H_SYNC   =  10'd96;    //行同步
	parameter  H_BACK   =  10'd48;    //行显示后沿
	parameter  H_DISP   =  10'd640;   //行有效数据
	parameter  H_FRONT  =  10'd16;    //行显示前沿
	parameter  H_TOTAL  =  10'd800;   //行扫描周期

	parameter  V_SYNC   =  10'd2;     //场同步
	parameter  V_BACK   =  10'd33;    //场显示后沿
	parameter  V_DISP   =  10'd480;   //场有效数据
	parameter  V_FRONT  =  10'd10;    //场显示前沿
	parameter  V_TOTAL  =  10'd525;   //场扫描周期

	// 声明2个计数器,分别用于行计数和场计数
	reg [10:0] cnt_h;
	reg [10:0] cnt_v;

	wire vga_en;
	wire data_req;

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			cnt_h <= 11'd0;
		end
		else if(cnt_h < H_TOTAL-1'd1)begin//一行计到799
			cnt_h <= cnt_h + 11'd1;
		end
		else
			cnt_h <= 11'd0;
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			cnt_v <= 11'd0;
		end
		else if(cnt_v < V_TOTAL-1'd1)begin//一行计到799
			cnt_v <= cnt_v + 11'd1;
		end
		else
			cnt_v <= 11'd0;
	end

	//根据VGA行同步时序、场同步时序得到行同步信号、场同步信号
	always@(*)begin
		if((cnt_h > V_SYNC + H_BACK) && (cnt_h < H_TOTAL - H_FRONT))begin//有效数据部分
			vga_hs <= 1'd1;
		end
		else 
			vga_hs <= 1'd0;
	end

	always@(*)begin
		if((cnt_v > H_SYNC + V_BACK) && (cnt_v < V_TOTAL - V_FRONT))begin//有效数据部分
			vga_vs <= 1'd1;
		end
		else 
			vga_vs <= 1'd0;
	end

	assign vga_en = ((cnt_h > V_SYNC + H_BACK) && (cnt_h < H_TOTAL - H_FRONT))
					&&((cnt_v > H_SYNC + V_BACK) && (cnt_v < V_TOTAL - V_FRONT))
					? 1'd1:1'd0;//标志出有效数据部分
	assign vga_rgb = vga_en ? pixel_data : 16'd0;

	//请求像素点颜色数据输入                
	assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
					  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
					  ?  1'b1 : 1'b0;//需要从SDRAM得到传回数据,因此需要提前一个时钟周期 

	//像素点坐标                
	assign pixel_x = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
	assign pixel_y = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

endmodule 


VGA接口的缺点:
1.传输的信号是模拟信号,信号容易受干扰
2.体积大

4.2 VGA转HDMI

HDMI接口的有点:
1.抗干扰能力强
2.体积小
3.兼容性好,可以同时传输数字信号和音频

4.2.1 编码

8bit并行数据——10bit并行数据
直流均衡:0=1个数

module  encode
(
    input   wire            sys_clk     ,   //时钟信号
    input   wire            sys_rst_n   ,   //复位信号,低有效
    input   wire    [7:0]   data_in     ,   //输入8bit待编码数据
    input   wire            c0          ,   //控制信号c0
    input   wire            c1          ,   //控制信号c1
    input   wire            de          ,   //使能信号

    output  reg     [9:0]   data_out        //输出编码后的10bit数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   DATA_OUT0   =   10'b1101010100,
            DATA_OUT1   =   10'b0010101011,
            DATA_OUT2   =   10'b0101010100,
            DATA_OUT3   =   10'b1010101011;

//wire  define
wire            condition_1 ;   //条件1
wire            condition_2 ;   //条件2
wire            condition_3 ;   //条件3
wire    [8:0]   q_m         ;   //第一阶段转换后的9bit数据

//reg   define
reg     [3:0]   data_in_n1  ;   //待编码数据中1的个数
reg     [7:0]   data_in_reg ;   //待编码数据打一拍
reg     [3:0]   q_m_n1      ;   //转换后9bit数据中1的个数
reg     [3:0]   q_m_n0      ;   //转换后9bit数据中0的个数
reg     [4:0]   cnt         ;   //视差计数器,0-1个数差别,最高位为符号位
reg             de_reg1     ;   //使能信号打一拍
reg             de_reg2     ;   //使能信号打两拍
reg             c0_reg1     ;   //控制信号c0打一拍
reg             c0_reg2     ;   //控制信号c0打两拍
reg             c1_reg1     ;   //控制信号c1打一拍
reg             c1_reg2     ;   //控制信号c1打两拍
reg     [8:0]   q_m_reg     ;   //q_m信号打一拍

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//data_in_n1:待编码数据中1的个数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_in_n1  <=  4'd0;
    else
        data_in_n1  <=  data_in[0] + data_in[1] + data_in[2]
                        + data_in[3] + data_in[4] + data_in[5]
                        + data_in[6] + data_in[7];

//data_in_reg:待编码数据打一拍
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_in_reg <=  8'b0;
    else
        data_in_reg <=  data_in;

//condition_1:条件1
assign  condition_1 = ((data_in_n1 > 4'd4) || ((data_in_n1 == 4'd4)//??直接写>=4不行吗
                        && (data_in_reg[0] == 1'b0)));

//q_m:第一阶段转换后的9bit数据
assign q_m[0] = data_in_reg[0];
assign q_m[1] = (condition_1) ? (q_m[0] ^~ data_in_reg[1]) : (q_m[0] ^ data_in_reg[1]);
assign q_m[2] = (condition_1) ? (q_m[1] ^~ data_in_reg[2]) : (q_m[1] ^ data_in_reg[2]);
assign q_m[3] = (condition_1) ? (q_m[2] ^~ data_in_reg[3]) : (q_m[2] ^ data_in_reg[3]);
assign q_m[4] = (condition_1) ? (q_m[3] ^~ data_in_reg[4]) : (q_m[3] ^ data_in_reg[4]);
assign q_m[5] = (condition_1) ? (q_m[4] ^~ data_in_reg[5]) : (q_m[4] ^ data_in_reg[5]);
assign q_m[6] = (condition_1) ? (q_m[5] ^~ data_in_reg[6]) : (q_m[5] ^ data_in_reg[6]);
assign q_m[7] = (condition_1) ? (q_m[6] ^~ data_in_reg[7]) : (q_m[6] ^ data_in_reg[7]);
assign q_m[8] = (condition_1) ? 1'b0 : 1'b1;

//q_m_n1:转换后9bit数据中1的个数
//q_m_n0:转换后9bit数据中0的个数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            q_m_n1  <=  4'd0;
            q_m_n0  <=  4'd0;
        end
    else
        begin
            q_m_n1  <=  q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
            q_m_n0  <=  4'd8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
        end

//condition_2:条件2
assign  condition_2 = ((cnt == 5'd0) || (q_m_n1 == q_m_n0));

//condition_3:条件3
assign  condition_3 = (((~cnt[4] == 1'b1) && (q_m_n1 > q_m_n0))
                        || ((cnt[4] == 1'b1) && (q_m_n0 > q_m_n1)));

//数据打拍,为了各数据同步
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            de_reg1 <=  1'b0;
            de_reg2 <=  1'b0;
            c0_reg1 <=  1'b0;
            c0_reg2 <=  1'b0;
            c1_reg1 <=  1'b0;
            c1_reg2 <=  1'b0;
            q_m_reg <=  9'b0;
        end
    else
        begin
            de_reg1 <=  de;
            de_reg2 <=  de_reg1;
            c0_reg1 <=  c0;
            c0_reg2 <=  c0_reg1;
            c1_reg1 <=  c1;
            c1_reg2 <=  c1_reg1;
            q_m_reg <=  q_m;
        end

//data_out:输出编码后的10bit数据
//cnt:视差计数器,0-1个数差别,最高位为符号位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            data_out    <=  10'b0;
            cnt         <=  5'b0;
        end
    else
        begin
            if(de_reg2 == 1'b1)
                begin
                    if(condition_2 == 1'b1)
                        begin
                            data_out[9]     <=  ~q_m_reg[8]; 
                            data_out[8]     <=  q_m_reg[8]; 
                            data_out[7:0]   <=  (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];
                            cnt <=  (~q_m_reg[8]) ? (cnt + q_m_n0 - q_m_n1) : (cnt + q_m_n1 - q_m_n0);
                        end
                    else
                        begin
                            if(condition_3 == 1'b1)
                                begin
                                    data_out[9]     <= 1'b1;
                                    data_out[8]     <= q_m_reg[8];
                                    data_out[7:0]   <= ~q_m_reg[7:0];
                                    cnt <=  cnt + {q_m_reg[8], 1'b0} + (q_m_n0 - q_m_n1);
                                end
                            else
                                begin
                                    data_out[9]     <= 1'b0;
                                    data_out[8]     <= q_m_reg[8];
                                    data_out[7:0]   <= q_m_reg[7:0];
                                    cnt <=  cnt - {~q_m_reg[8], 1'b0} + (q_m_n1 - q_m_n0);
                                end
                            
                        end
                end
            else
                begin
                    case    ({c1_reg2, c0_reg2})
                        2'b00:  data_out <= DATA_OUT0;
                        2'b01:  data_out <= DATA_OUT1;
                        2'b10:  data_out <= DATA_OUT2;
                        default:data_out <= DATA_OUT3;
                    endcase
                    cnt <=  5'b0;
                end
        end

endmodule
4.2.2 并串转换

10bit并行数据——10bit串行数据
单端信号——差分信号
单沿采样——双沿采样

module par_to_ser
(
    input   wire            clk_5x      ,   //输入系统时钟
    input   wire    [9:0]   par_data    ,   //输入并行数据

    output  wire            ser_data_p  ,   //输出串行差分数据
    output  wire            ser_data_n      //输出串行差分数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire  define
wire    [4:0]   data_rise = {par_data[8],par_data[6],
                            par_data[4],par_data[2],par_data[0]};
wire    [4:0]   data_fall = {par_data[9],par_data[7],
                            par_data[5],par_data[3],par_data[1]};

//reg   define
reg     [4:0]   data_rise_s = 0;
reg     [4:0]   data_fall_s = 0;
reg     [2:0]   cnt = 0;

always @ (posedge clk_5x)
    begin
        cnt <= (cnt[2]) ? 3'd0 : cnt + 3'd1;
        data_rise_s  <= cnt[2] ? data_rise : data_rise_s[4:1];//计满就将5位上升沿数据补上,没计满就移位
        data_fall_s  <= cnt[2] ? data_fall : data_fall_s[4:1];

    end

//********************************************************************//
//**************************** Instantiate ***************************//
//********************************************************************//
//------------- ddio_out_inst0 -------------
ddio_out    ddio_out_inst0
(
    .datain_h   (data_rise_s[0] ),
    .datain_l   (data_fall_s[0] ),
    .outclock   (~clk_5x        ),
    .dataout    (ser_data_p     )
);

//------------- ddio_out_inst1 -------------
ddio_out    ddio_out_inst1
(
    .datain_h   (~data_rise_s[0]),
    .datain_l   (~data_fall_s[0]),
    .outclock   (~clk_5x        ),
    .dataout    (ser_data_n     )
);

endmodule

五、附录

5.1 摄像头

本篇使用的摄像头引脚含义:
在这里插入图片描述

5.2 开发板

使用了下面几款开发板,其中列出摄像头模块的原理图

5.2.1 野火

在这里插入图片描述

5.2.2 黑金

在这里插入图片描述

  • 1
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值