当前位置:AIGC资讯 > 数据采集 > 正文

基于FPGA的图像实时采集

文章目录

一、系统框架 1.摄像头模块 摄像头配置 摄像头数据处理 2.SDRAM模块 SDRAM控制模块 SDRAM读写仲裁 SDRAM接口 读写FIFO 3.vga显示模块 4.PLL时钟模块 二、部分模块实现代码 1.摄像头配置 I2C接口 摄像头配置 摄像头数据处理 2.SDRAM控制模块 3.vga模块 4.顶层文件 三、源码

一、系统框架

首先整个系统由摄像头模块、SDRAM数据缓存模块、vga显示模块、PLL时钟模块以及图像处理模块组成,这里先不用图像处理模块。

1.摄像头模块

摄像头配置

摄像头模块里面负责处理摄像头采集的数据,根据ov5640摄像头手册说明,我们需要先通过I2C协议去配置摄像头相关寄存器的参数。在摄像头上电后需要等待20ms。然后再通过I2C发送设备ID、写地址和数据,其中地址先发送高8位再发送低8位。这里包含摄像头时钟、图像大小、帧率以及其他和图像相关的参数。这里最重要的配置参数就是摄像头的图像分辨率和图像的色彩格式,这里通过配置的分辨率为1280*720,RGB565格式。

摄像头数据处理

根据时序图可以看到,当场同步信号到来后,会跟随着许多HREF数据有效信号,我们在HREF高电平的时候去采集数据即可。

但是摄像头的数据是把16位RGB拆分为高八位和低八位发送的,我们需要通过移位+位拼接的方式把两个8bit数据合并成16bit数据输出。同时为了SDRAM模块更好的识别帧头和帧尾,在图像的第一个像素点以及最后一个像素点的时候分别拉高sop和eop信号,其余像素点这拉低。

    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            data <= 0;
        end
        else begin
            data <= {data[7:0],din};//左移
        end
    end

    assign pixel = data;
    assign sop = cnt_h == 1 && cnt_v == 0;
    assign eop = data_eop;
    assign vld = cnt_h[0];

2.SDRAM模块

SDRAM控制模块

由于摄像头数据时钟(84M)和vga时钟(75M)不一样,为了避免读写数据速度不一致就需要把摄像头的数据进行缓存,常见的缓存可以通过fifo,但是这里的数据量十分庞大,故需要通过SDRAM进行缓存。缓存的方式则是通过乒乓缓存,把SDRAM的两个blank单独作为数据的写入和读出,并在读写完成后切换读写blank,并且需要通过丢帧和复读的操作来保证读写图像的完整性。

SDRAM读写仲裁

由于摄像头的数据输出和vga的数据请求都是源源不断的,此时到底是读还是写SDRAM就需要一个规则约束。首先为了保证写FIFO数据过多或者读FIFO数据过少。当写FIFO数据大于阈值后就会发起一个写请求,当读FIFO的数据低于阈值后就会发起读请求。仲裁机制根据读写请求进行仲裁,如果同一时间只有单独的读或者写请求直接执行即可,如果同时存在两种请求,则会根据上次执行的是读或者写来进行相反的请求。

//flag_sel    ;//标记上一次操作
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag_sel <= 0;
        end 
        else if(read2done)begin 
            flag_sel <= 1;
        end 
        else if(write2done)begin 
            flag_sel <= 0;
        end 
    end

//prior_flag  ;//优先级标志 0:写优先级高   1:读优先级高     仲裁读、写的优先级
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            prior_flag <= 0;
        end 
        else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin   //突发写优先级高
            prior_flag <= 1'b0;
        end 
        else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin   //突发读优先级高
            prior_flag <= 1'b1;
        end 
    end

SDRAM接口

这里直接使用quartus自带的SDRAM接口ip。

需要注意的是SDRAM的blank数据是数据位的最高位和第10位,行地址为第23位到11位,列地址则为低9位当然前提是这里的为4blank13行9列。如图为ip接口部分源码

所以地址需要这样进行拼接

    assign avm_addr   = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]}
                       :((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]}
                       :0);

读写FIFO

整个SDRAM模块涉及到摄像头模块到SDRAM模块(慢时钟域到快时钟域),SDRAM数据到vga模块(快时钟域到慢时钟域)的跨时钟域数据同步的问题,这里使用两个异步FIFO来缓存两个跨时钟域的数据。

首先是写FIFO(这里的读写相对于SDRAM)
写FIFO的写使能条件是FIFO非满、输入数据有效、旧的一帧被vga读完且新的一帧到来或者是当前帧数据。
写FIFO的读使能条件是当前需要往SDRAM写数据且FIFO非空,这里通过SDRAM控制模块的仲裁机制决定。

    assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag);
    assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest;

然后是读FIFO
读FIFO的写使能是当前把SDRAM读出来的数据有效且FIFO非满
读FIFO的读使能为当前FIFO非空且vga发起数据请求。

    assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld;
    assign rfifo_rdreq = ~rfifo_empty & rdreq;

3.vga显示模块

vga显示这边使用1280*780@60HZ参数输出图像,在行场有效区域内通过拉发起请求读取读FIFO的数据内的数据,再输出到屏幕上面。可以参考前面的博客基于FPGA的VGA显示彩条、字符、图片,这里不多描述。

4.PLL时钟模块

整个系统由50M的基准时钟驱动,并且通过PLL生成配置摄像头的驱动时钟(24M)、SDRAM接口驱动时钟(100M),vga驱动时钟(75M)还有输出到SDRAM外设的100M带相位偏移的时钟。


二、部分模块实现代码

1.摄像头配置

I2C接口

`include "param.v"

module i2c_intf(
    input               clk         ,
    input               rst_n       ,

    input               req         ,
    input       [3:0]   cmd         ,
    input       [7:0]   din         ,

    output      [7:0]   dout        ,
    output              done        ,
    output              slave_ack   ,
    output              i2c_scl     ,
    input               i2c_sda_i   ,
    output              i2c_sda_o   ,
    output              i2c_sda_oe     
    );

//状态机参数定义

    localparam  IDLE  = 7'b000_0001,
                START = 7'b000_0010,
                WRITE = 7'b000_0100,
                RACK  = 7'b000_1000,
                READ  = 7'b001_0000,
                SACK  = 7'b010_0000,
                STOP  = 7'b100_0000;

//信号定义

    reg     [6:0]       state_c     ;
    reg     [6:0]       state_n     ;

    reg     [8:0]       cnt_scl     ;//产生i2c时钟
    wire                add_cnt_scl ;
    wire                end_cnt_scl ;
    reg     [3:0]       cnt_bit     ;//传输数据 bit计数器
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;
    reg     [3:0]       bit_num     ;
    
    reg                 scl         ;//输出寄存器
    reg                 sda_out     ;
    reg                 sda_out_en  ;

    reg     [7:0]       rx_data     ;
    reg                 rx_ack      ;
    reg     [3:0]       command     ;
    reg     [7:0]       tx_data     ;//发送数据

    wire                idle2start  ; 
    wire                idle2write  ; 
    wire                idle2read   ; 
    wire                start2write ; 
    wire                start2read  ; 
    wire                write2rack  ; 
    wire                read2sack   ; 
    wire                rack2stop   ; 
    wire                sack2stop   ; 
    wire                rack2idle   ; 
    wire                sack2idle   ; 
    wire                stop2idle   ; 


//状态机
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            state_c <= IDLE ;
        end
        else begin
            state_c <= state_n;
       end
    end
    
    always @(*) begin 
        case(state_c)  
            IDLE :begin
                if(idle2start)
                    state_n = START ;
                else if(idle2write)
                    state_n = WRITE ;
                else if(idle2read)
                    state_n = READ ;
                else 
                    state_n = state_c ;
            end
            START :begin
                if(start2write)
                    state_n = WRITE ;
                else if(start2read)
                    state_n = READ ;
                else 
                    state_n = state_c ;
            end
            WRITE :begin
                if(write2rack)
                    state_n = RACK ;
                else 
                    state_n = state_c ;
            end
            RACK :begin
                if(rack2stop)
                    state_n = STOP ;
                else if(rack2idle)
                    state_n = IDLE ;
                else 
                    state_n = state_c ;
            end
            READ :begin
                if(read2sack)
                    state_n = SACK ;
                else 
                    state_n = state_c ;
            end
            SACK :begin
                if(sack2stop)
                    state_n = STOP ;
                else if(sack2idle)
                    state_n = IDLE ;
                else 
                    state_n = state_c ;
            end
            STOP :begin
                if(stop2idle)
                    state_n = IDLE ;
                else 
                    state_n = state_c ;
            end
            default : state_n = IDLE ;
        endcase
    end
    
    assign idle2start  = state_c==IDLE  && (req && (cmd&`CMD_START));
    assign idle2write  = state_c==IDLE  && (req && (cmd&`CMD_WRITE));
    assign idle2read   = state_c==IDLE  && (req && (cmd&`CMD_READ ));
    assign start2write = state_c==START && (end_cnt_bit && (command&`CMD_WRITE));
    assign start2read  = state_c==START && (end_cnt_bit && (command&`CMD_READ ));
    assign write2rack  = state_c==WRITE && (end_cnt_bit);
    assign read2sack   = state_c==READ  && (end_cnt_bit);
    assign rack2stop   = state_c==RACK  && (end_cnt_bit && (command&`CMD_STOP ));
    assign sack2stop   = state_c==SACK  && (end_cnt_bit && (command&`CMD_STOP ));
    assign rack2idle   = state_c==RACK  && (end_cnt_bit && (command&`CMD_STOP ) == 0);
    assign sack2idle   = state_c==SACK  && (end_cnt_bit && (command&`CMD_STOP ) == 0);
    assign stop2idle   = state_c==STOP  && (end_cnt_bit );
    
//计数器
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            cnt_scl <= 0; 
        end
        else if(add_cnt_scl) begin
            if(end_cnt_scl)
                cnt_scl <= 0; 
            else
                cnt_scl <= cnt_scl+1 ;
       end
    end
    assign add_cnt_scl = (state_c != IDLE);
    assign end_cnt_scl = add_cnt_scl  && cnt_scl == (`SCL_PERIOD)-1 ;

    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            cnt_bit <= 0; 
        end
        else if(add_cnt_bit) begin
            if(end_cnt_bit)
                cnt_bit <= 0; 
            else
                cnt_bit <= cnt_bit+1 ;
       end
    end
    assign add_cnt_bit = (end_cnt_scl);
    assign end_cnt_bit = add_cnt_bit  && cnt_bit == (bit_num)-1 ;

    always  @(*)begin
        if(state_c == WRITE | state_c == READ) begin
            bit_num = 8;
        end
        else begin 
            bit_num = 1;
        end 
    end
//command
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            command <= 0;
        end
        else if(req)begin
            command <= cmd;
        end
    end

//tx_data
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            tx_data <= 0;
        end
        else if(req)begin
            tx_data <= din;
        end
    end

//scl
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            scl <= 1'b1;
        end
        else if(idle2start | idle2write | idle2read)begin//开始发送时,拉低
            scl <= 1'b0;
        end
        else if(add_cnt_scl && cnt_scl == `SCL_HALF-1)begin 
            scl <= 1'b1;
        end 
        else if(end_cnt_scl && ~stop2idle)begin 
            scl <= 1'b0;
        end 
    end

//sda_out
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            sda_out <= 1'b1;
        end
        else if(state_c == START)begin          //发起始位
            if(cnt_scl == `LOW_HLAF)begin       //时钟低电平时拉高sda总线
                sda_out <= 1'b1;
            end
            else if(cnt_scl == `HIGH_HALF)begin    //时钟高电平时拉低sda总线 
                sda_out <= 1'b0;                //保证从机能检测到起始位
            end 
        end 
        else if(state_c == WRITE && cnt_scl == `LOW_HLAF)begin  //scl低电平时发送数据   并串转换
            sda_out <= tx_data[7-cnt_bit];      
        end 
        else if(state_c == SACK && cnt_scl == `LOW_HLAF)begin  //发应答位
            sda_out <= (command&`CMD_STOP)?1'b1:1'b0;
        end 
        else if(state_c == STOP)begin //发停止位
            if(cnt_scl == `LOW_HLAF)begin       //时钟低电平时拉低sda总线
                sda_out <= 1'b0;
            end
            else if(cnt_scl == `HIGH_HALF)begin    //时钟高电平时拉高sda总线 
                sda_out <= 1'b1;                //保证从机能检测到停止位
            end 
        end 
    end

//sda_out_en  总线输出数据使能
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            sda_out_en <= 1'b0;
        end
        else if(idle2start | idle2write | read2sack | rack2stop)begin
            sda_out_en <= 1'b1;
        end
        else if(idle2read | start2read | write2rack | stop2idle)begin 
            sda_out_en <= 1'b0;
        end 
    end

//rx_data       接收读入的数据
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            rx_data <= 0;
        end
        else if(state_c == READ && cnt_scl == `HIGH_HALF)begin
            rx_data[7-cnt_bit] <= i2c_sda_i;    //串并转换
        end
    end

//rx_ack
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            rx_ack <= 1'b1;
        end
        else if(state_c == RACK && cnt_scl == `HIGH_HALF)begin
            rx_ack <= i2c_sda_i;
        end
    end


//输出信号

    assign i2c_scl    = scl         ;
    assign i2c_sda_o  = sda_out     ;
    assign i2c_sda_oe = sda_out_en  ;
   
    assign dout = rx_data;
    assign done = rack2idle | sack2idle | stop2idle;
    assign slave_ack = rx_ack;

endmodule


摄像头配置

`include "param.v"
module cmos_config(
    input               clk         ,
    input               rst_n       ,
    //i2c_master
    output              req         ,
    output      [3:0]   cmd         ,
    output      [7:0]   dout        ,
    input               done        ,
    
    output              config_done 
);

//定义参数

    localparam  WAIT   = 4'b0001,//上电等待20ms
                IDLE   = 4'b0010,
                WREQ   = 4'b0100,//发写请求
                WRITE  = 4'b1000;//等待一个字节写完
    parameter   DELAY  = 1000_000;//上电延时20ms开始配置
//信号定义

    reg     [3:0]       state_c     ;
    reg     [3:0]       state_n     ;
    
    reg     [19:0]      cnt0        ;
    wire                add_cnt0/* synthesis syn_keep*/    ;
    wire                end_cnt0/* synthesis syn_keep*/    ;
    reg     [1:0]       cnt1        ;
    wire                add_cnt1/* synthesis syn_keep*/    ;
    wire                end_cnt1/* synthesis syn_keep*/    ;
    reg                 config_flag ;//1:表示在配置摄像头 0:表示配置完成
    reg     [23:0]      lut_data    ;

    reg                 tran_req    ; 
    reg      [3:0]      tran_cmd    ; 
    reg      [7:0]      tran_dout   ; 

    wire                wait2idle   ; 
    wire                idle2wreq   ; 
    wire                write2wreq  ; 
    wire                write2idle  ; 


//状态机

    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin        
            state_c <= WAIT;
        end
        else begin
            state_c <= state_n;
        end
    end

    always  @(*)begin
        case(state_c)
            WAIT :begin 
                if(wait2idle)
                   state_n = IDLE;
                else 
                   state_n = state_c; 
            end 
            IDLE :begin 
                if(idle2wreq)
                    state_n = WREQ; 
                else 
                    state_n = state_c; 
            end  
            WREQ  :state_n = WRITE;
            WRITE :begin 
                if(write2wreq)
                    state_n = WREQ; 
                else if(write2idle)
                    state_n = IDLE;
                else 
                    state_n = state_c; 
            end 
            default:state_n = IDLE; 
        endcase 
    end

    assign wait2idle  = state_c == WAIT  && end_cnt0; 
    assign idle2wreq  = state_c == IDLE  && config_flag; 
    assign write2wreq = state_c == WRITE && done && ~end_cnt1; 
    assign write2idle = state_c == WRITE && end_cnt1; 

//计数器
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt0 <= 0;
        end
        else if(add_cnt0)begin
            if(end_cnt0)
                cnt0 <= 0;
            else
                cnt0 <= cnt0 + 1;
        end
    end
    
    assign add_cnt0 = state_c == WAIT || state_c == WRITE && end_cnt1;
    assign end_cnt0 = add_cnt0 && cnt0 == ((state_c == WAIT)?(DELAY-1):(`REG_NUM-1));

    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt1 <= 0;
        end
        else if(add_cnt1)begin
            if(end_cnt1)
                cnt1 <= 0;
            else
                cnt1 <= cnt1 + 1;
        end
    end
    
    assign add_cnt1 = state_c == WRITE && done;
    assign end_cnt1 = add_cnt1 && cnt1 == 4-1;

//config_flag
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            config_flag <= 1'b1;
        end
        else if(config_flag & end_cnt0 & state_c != WAIT)begin    //所有寄存器配置完,flag拉低
            config_flag <= 1'b0;
        end
    end

//输出寄存器

    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            tran_req <= 0;
            tran_cmd <= 0;
            tran_dout <= 0;
        end
        else if(state_c == WREQ)begin
            case(cnt1)
                0:begin 
                    tran_req <= 1;
                    tran_cmd <= {`CMD_START | `CMD_WRITE};
                    tran_dout <= `WR_ID;
                end 
                1:begin 
                    tran_req <= 1;
                    tran_cmd <= `CMD_WRITE;
                    tran_dout <= lut_data[23:16];
                end
                2:begin 
                    tran_req <= 1;
                    tran_cmd <= `CMD_WRITE;
                    tran_dout <= lut_data[15:8];
                end
                3:begin 
                    tran_req <= 1;
                    tran_cmd <= {`CMD_STOP | `CMD_WRITE};
                    tran_dout <= lut_data[7:0];
                end
                default:tran_req <= 0;
            endcase 
        end
		else begin
		    tran_req  <= 0;
            tran_cmd  <= 0;
            tran_dout <= 0;
		end 
    end

//输出

    assign config_done = ~config_flag;
    assign req = tran_req;
    assign cmd = tran_cmd;
    assign dout = tran_dout;


//lut_data   
    always@(*)begin
	    case(cnt0)			  
		 //15fps VGA YUV output
		 // 24MHz input clock, 84MHz PCLK
		 0  :lut_data	= 	{24'h3103_11}; // system clock from pad, bit[1]
		 1  :lut_data	= 	{24'h3008_82}; // software reset, bit[7]
		 2  :lut_data	= 	{24'h3008_42}; // software power down, bit[6]
		 3  :lut_data	= 	{24'h3103_03}; // system clock from PLL, bit[1]
		 4  :lut_data	= 	{24'h3017_ff}; // FREX, Vsync, HREF, PCLK, D[9:6] output enable
		 5  :lut_data	= 	{24'h3018_ff}; // D[5:0], GPIO[1:0] output enable
		 6  :lut_data	= 	{24'h3034_1a}; // MIPI 10-bit
		 7  :lut_data	= 	{24'h3037_13}; // PLL root divider, bit[4], PLL pre-divider, bit[3:0]
		 8  :lut_data	= 	{24'h3108_01}; // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2]
		 9  :lut_data	= 	{24'h3630_36};//SCLK root divider, bit[1:0]
		 10 :lut_data	= 	{24'h3631_0e};
		 11 :lut_data	= 	{24'h3632_e2};
		 12 :lut_data	= 	{24'h3633_12};
		 13 :lut_data	= 	{24'h3621_e0};
		 14 :lut_data	= 	{24'h3704_a0};
		 15 :lut_data	= 	{24'h3703_5a};
		 16 :lut_data	= 	{24'h3715_78};
		 17 :lut_data	= 	{24'h3717_01};
		 18 :lut_data	= 	{24'h370b_60};
		 19 :lut_data	= 	{24'h3705_1a};
		 20 :lut_data	= 	{24'h3905_02};
		 21 :lut_data	= 	{24'h3906_10};
		 22 :lut_data	= 	{24'h3901_0a};
		 23 :lut_data	= 	{24'h3731_12};
		 24 :lut_data	= 	{24'h3600_08}; // VCM control
		 25 :lut_data	= 	{24'h3601_33}; // VCM control
		 26 :lut_data	= 	{24'h302d_60}; // system control
		 27 :lut_data	= 	{24'h3620_52};
		 28 :lut_data	= 	{24'h371b_20};
		 29 :lut_data	= 	{24'h471c_50};
		 30 :lut_data	= 	{24'h3a13_43}; // pre-gain = 1.047x
		 31 :lut_data	= 	{24'h3a18_00}; // gain ceiling
		 32 :lut_data	= 	{24'h3a19_f8}; // gain ceiling = 15.5x
		 33 :lut_data	= 	{24'h3635_13};
		 34 :lut_data	= 	{24'h3636_03};
		 35 :lut_data	= 	{24'h3634_40};
		 36 :lut_data	= 	{24'h3622_01};
		// 50/60Hz detection 50/60Hz 灯光条纹过滤
		 37 :lut_data	= 	{24'h3c01_34}; // Band auto, bit[7]
		 38 :lut_data	= 	{24'h3c04_28}; // threshold low sum
		 39 :lut_data	= 	{24'h3c05_98}; // threshold high sum
		 40 :lut_data	= 	{24'h3c06_00}; // light meter 1 threshold[15:8]
		 41 :lut_data	= 	{24'h3c07_08}; // light meter 1 threshold[7:0]
		 42 :lut_data	= 	{24'h3c08_00}; // light meter 2 threshold[15:8]
		 43 :lut_data	= 	{24'h3c09_1c}; // light meter 2 threshold[7:0]
		 44 :lut_data	= 	{24'h3c0a_9c}; // sample number[15:8]
		 45 :lut_data	= 	{24'h3c0b_40}; // sample number[7:0]
		 46 :lut_data	= 	{24'h3810_00}; // Timing Hoffset[11:8]
		 47 :lut_data	= 	{24'h3811_10}; // Timing Hoffset[7:0]
		 48 :lut_data	= 	{24'h3812_00}; // Timing Voffset[10:8]
		 49 :lut_data	= 	{24'h3708_64};
		 50 :lut_data	= 	{24'h4001_02}; // BLC start from line 2
		 51 :lut_data	= 	{24'h4005_1a}; // BLC always update
		 52 :lut_data	= 	{24'h3000_00}; // enable blocks
		 53 :lut_data	= 	{24'h3004_ff}; // enable clocks
		 54 :lut_data	= 	{24'h300e_58}; //MIPI power down,DVP enable
		 55 :lut_data	= 	{24'h302e_00};
		 56 :lut_data	= 	{24'h4300_61}; // RGB,
		 57 :lut_data	= 	{24'h501f_01}; // ISP RGB
		 58 :lut_data	= 	{24'h440e_00};
		 59 :lut_data	= 	{24'h5000_a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on
		// AEC target 自动曝光控制
		 60 :lut_data	= 	{24'h3a0f_30}; // stable range in high
		 61 :lut_data	= 	{24'h3a10_28}; // stable range in low
		 62 :lut_data	= 	{24'h3a1b_30}; // stable range out high
		 63 :lut_data	= 	{24'h3a1e_26}; // stable range out low
		 64 :lut_data	= 	{24'h3a11_60}; // fast zone high
		 65 :lut_data	= 	{24'h3a1f_14}; // fast zone low
		// Lens correction for ? 镜头补偿
		 66 :lut_data	= 	{24'h5800_23};
		 67 :lut_data	= 	{24'h5801_14};
		 68 :lut_data	= 	{24'h5802_0f};
		 69 :lut_data	= 	{24'h5803_0f};
		 70 :lut_data	= 	{24'h5804_12};
		 71 :lut_data	= 	{24'h5805_26};
		 72 :lut_data	= 	{24'h5806_0c};
		 73 :lut_data	= 	{24'h5807_08};
		 74 :lut_data	= 	{24'h5808_05};
		 75 :lut_data	= 	{24'h5809_05};
		 76 :lut_data	= 	{24'h580a_08};
		 77 :lut_data	= 	{24'h580b_0d};
		 78 :lut_data	= 	{24'h580c_08};
		 79 :lut_data	= 	{24'h580d_03};
		 80 :lut_data	= 	{24'h580e_00};
		 81 :lut_data	= 	{24'h580f_00};
		 82 :lut_data	= 	{24'h5810_03};
		 83 :lut_data	= 	{24'h5811_09};
		 84 :lut_data	= 	{24'h5812_07};
		 85 :lut_data	= 	{24'h5813_03};
		 86 :lut_data	= 	{24'h5814_00};
		 87 :lut_data	= 	{24'h5815_01};
		 88 :lut_data	= 	{24'h5816_03};
		 89 :lut_data	= 	{24'h5817_08};
		 90 :lut_data	= 	{24'h5818_0d};
		 91 :lut_data	= 	{24'h5819_08};
		 92 :lut_data	= 	{24'h581a_05};
		 93 :lut_data	= 	{24'h581b_06};
		 94 :lut_data	= 	{24'h581c_08};
		 95 :lut_data	= 	{24'h581d_0e};
		 96 :lut_data	= 	{24'h581e_29};
		 97 :lut_data	= 	{24'h581f_17};
		 98 :lut_data	= 	{24'h5820_11};
		 99 :lut_data	= 	{24'h5821_11};
		 100:lut_data	= 	{24'h5822_15};
		 101:lut_data	= 	{24'h5823_28};
		 102:lut_data	= 	{24'h5824_46};
		 103:lut_data	= 	{24'h5825_26};
		 104:lut_data	= 	{24'h5826_08};
		 105:lut_data	= 	{24'h5827_26};
		 106:lut_data	= 	{24'h5828_64};
		 107:lut_data	= 	{24'h5829_26};
		 108:lut_data	= 	{24'h582a_24};
		 109:lut_data	= 	{24'h582b_22};
		 110:lut_data	= 	{24'h582c_24};
		 111:lut_data	= 	{24'h582d_24};
		 112:lut_data	= 	{24'h582e_06};
		 113:lut_data	= 	{24'h582f_22};
		 114:lut_data	= 	{24'h5830_40};
		 115:lut_data	= 	{24'h5831_42};
		 116:lut_data	= 	{24'h5832_24};
		 117:lut_data	= 	{24'h5833_26};
		 118:lut_data	= 	{24'h5834_24};
		 119:lut_data	= 	{24'h5835_22};
		 120:lut_data	= 	{24'h5836_22};
		 121:lut_data	= 	{24'h5837_26};
		 122:lut_data	= 	{24'h5838_44};
		 123:lut_data	= 	{24'h5839_24};
		 124:lut_data	= 	{24'h583a_26};
		 125:lut_data	= 	{24'h583b_28};
		 126:lut_data	= 	{24'h583c_42};
		 127:lut_data	= 	{24'h583d_ce}; // lenc BR offset
		// AWB 自动白平衡
		 128:lut_data	= 	{24'h5180_ff}; // AWB B block
		 129:lut_data	= 	{24'h5181_f2}; // AWB control
		 130:lut_data	= 	{24'h5182_00}; // [7:4] max local counter, [3:0] max fast counter
		 131:lut_data	= 	{24'h5183_14}; // AWB advanced
		 132:lut_data	= 	{24'h5184_25};
		 133:lut_data	= 	{24'h5185_24};
		 134:lut_data	= 	{24'h5186_09};
		 135:lut_data	= 	{24'h5187_09};
		 136:lut_data	= 	{24'h5188_09};
		 137:lut_data	= 	{24'h5189_75};
		 138:lut_data	= 	{24'h518a_54};
		 139:lut_data	= 	{24'h518b_e0};
		 140:lut_data	= 	{24'h518c_b2};
		 141:lut_data	= 	{24'h518d_42};
		 142:lut_data	= 	{24'h518e_3d};
		 143:lut_data	= 	{24'h518f_56};
		 144:lut_data	= 	{24'h5190_46};
		 145:lut_data	= 	{24'h5191_f8}; // AWB top limit
		 146:lut_data	= 	{24'h5192_04}; // AWB bottom limit
		 147:lut_data	= 	{24'h5193_70}; // red limit
		 148:lut_data	= 	{24'h5194_f0}; // green limit
		 149:lut_data	= 	{24'h5195_f0}; // blue limit
		 150:lut_data	= 	{24'h5196_03}; // AWB control
		 151:lut_data	= 	{24'h5197_01}; // local limit
		 152:lut_data	= 	{24'h5198_04};
		 153:lut_data	= 	{24'h5199_12};
		 154:lut_data	= 	{24'h519a_04};
		 155:lut_data	= 	{24'h519b_00};
		 156:lut_data	= 	{24'h519c_06};
		 157:lut_data	= 	{24'h519d_82};
		 158:lut_data	= 	{24'h519e_38}; // AWB control
		// Gamma 伽玛曲线
		 159:lut_data	= 	{24'h5480_01}; //Gamma bias plus on, bit[0]
		 160:lut_data	= 	{24'h5481_08};
		 161:lut_data	= 	{24'h5482_14};
		 162:lut_data	= 	{24'h5483_28};
		 163:lut_data	= 	{24'h5484_51};
		 164:lut_data	= 	{24'h5485_65};
		 165:lut_data	= 	{24'h5486_71};
		 166:lut_data	= 	{24'h5487_7d};
		 167:lut_data	= 	{24'h5488_87};
		 168:lut_data	= 	{24'h5489_91};
		 169:lut_data	= 	{24'h548a_9a};
		 170:lut_data	= 	{24'h548b_aa};
		 171:lut_data	= 	{24'h548c_b8};
		 172:lut_data	= 	{24'h548d_cd};
		 173:lut_data	= 	{24'h548e_dd};
		 174:lut_data	= 	{24'h548f_ea};
		 175:lut_data	= 	{24'h5490_1d};
		// color matrix 色彩矩阵
		 176:lut_data	= 	{24'h5381_1e}; // CMX1 for Y
		 177:lut_data	= 	{24'h5382_5b}; // CMX2 for Y
		 178:lut_data	= 	{24'h5383_08}; // CMX3 for Y
		 179:lut_data	= 	{24'h5384_0a}; // CMX4 for U
		 180:lut_data	= 	{24'h5385_7e}; // CMX5 for U
		 181:lut_data	= 	{24'h5386_88}; // CMX6 for U
		 182:lut_data	= 	{24'h5387_7c}; // CMX7 for V
		 183:lut_data	= 	{24'h5388_6c}; // CMX8 for V
		 184:lut_data	= 	{24'h5389_10}; // CMX9 for V
		 185:lut_data	= 	{24'h538a_01}; // sign[9]
		 186:lut_data	= 	{24'h538b_98}; // sign[8:1]
		// UV adjust UV 色彩饱和度调整
		 187:lut_data	= 	{24'h5580_06}; // saturation on, bit[1]
		 188:lut_data	= 	{24'h5583_40};
		 189:lut_data	= 	{24'h5584_10};
		 190:lut_data	= 	{24'h5589_10};
		 191:lut_data	= 	{24'h558a_00};
		 192:lut_data	= 	{24'h558b_f8};
		 193:lut_data	= 	{24'h501d_40}; // enable manual offset of contrast
		// CIP 锐化和降噪
		 194:lut_data	= 	{24'h5300_08}; //CIP sharpen MT threshold 1
		 195:lut_data	= 	{24'h5301_30}; //CIP sharpen MT threshold 2
		 196:lut_data	= 	{24'h5302_10}; // CIP sharpen MT offset 1
		 197:lut_data	= 	{24'h5303_00}; // CIP sharpen MT offset 2
		 198:lut_data	= 	{24'h5304_08}; // CIP DNS threshold 1
		 199:lut_data	= 	{24'h5305_30}; // CIP DNS threshold 2
		 200:lut_data	= 	{24'h5306_08}; // CIP DNS offset 1
		 201:lut_data	= 	{24'h5307_16}; // CIP DNS offset 2
		 202:lut_data	= 	{24'h5309_08}; //CIP sharpen TH threshold 1
		 203:lut_data	= 	{24'h530a_30}; //CIP sharpen TH threshold 2
		 204:lut_data	= 	{24'h530b_04}; //CIP sharpen TH offset 1
		 205:lut_data	= 	{24'h530c_06}; //CIP sharpen TH offset 2
		 206:lut_data	= 	{24'h5025_00};
		 207:lut_data	= 	{24'h3008_02}; //wake up from standby,bit[6]
		// input clock 24Mhz, PCLK 84Mhz
		 208:lut_data	= 	{24'h3035_21}; // PLL
		 209:lut_data	= 	{24'h3036_69}; // PLL
		 210:lut_data	= 	{24'h3c07_07}; // lightmeter 1 threshold[7:0]
		 211:lut_data	= 	{24'h3820_47}; // flip
		 212:lut_data	= 	{24'h3821_01}; // no mirror
		 213:lut_data	= 	{24'h3814_31}; // timing X inc
		 214:lut_data	= 	{24'h3815_31}; // timing Y inc
		 215:lut_data	= 	{24'h3800_00}; // HS
		 216:lut_data	= 	{24'h3801_00}; // HS
		 217:lut_data	= 	{24'h3802_00}; // VS
		 218:lut_data	= 	{24'h3803_fa}; // VS
		 219:lut_data	= 	{24'h3804_0a}; // HW  :   	 
		 220:lut_data	= 	{24'h3805_3f}; // HW  :   	
		 221:lut_data	= 	{24'h3806_06}; // VH  :   	
		 222:lut_data	= 	{24'h3807_a9}; // VH  :   	
		 223:lut_data	= 	{24'h3808_05}; // DVPHO 1280
		 224:lut_data	= 	{24'h3809_00}; // DVPHO
		 225:lut_data	= 	{24'h380a_02}; // DVPVO 720
		 226:lut_data	= 	{24'h380b_d0}; // DVPVO
		 227:lut_data	= 	{24'h380c_07}; // HTS
		 228:lut_data	= 	{24'h380d_64}; // HTS
		 229:lut_data	= 	{24'h380e_02}; // VTS
		 230:lut_data	= 	{24'h380f_e4}; // VTS
		 231:lut_data	= 	{24'h3813_04}; // timing V offset
		 232:lut_data	= 	{24'h3618_00};
		 233:lut_data	= 	{24'h3612_29};
		 234:lut_data	= 	{24'h3709_52};
		 235:lut_data	= 	{24'h370c_03};
		 236:lut_data	= 	{24'h3a02_02}; // 60Hz max exposure
		 237:lut_data	= 	{24'h3a03_e0}; // 60Hz max exposure
		 238:lut_data	= 	{24'h3a14_02}; // 50Hz max exposure
		 239:lut_data	= 	{24'h3a15_e0}; // 50Hz max exposure
		 240:lut_data	= 	{24'h4004_02}; // BLC line number
		 241:lut_data	= 	{24'h3002_1c}; // reset JFIFO, SFIFO, JPG
		 242:lut_data	= 	{24'h3006_c3}; // disable clock of JPEG2x, JPEG
		 243:lut_data	= 	{24'h4713_03}; // JPEG mode 3
		 244:lut_data	= 	{24'h4407_04}; // Quantization scale
		 245:lut_data	= 	{24'h460b_37};
		 246:lut_data	= 	{24'h460c_20};
		 247:lut_data	= 	{24'h4837_16}; // MIPI global timing
		 248:lut_data	= 	{24'h3824_04}; // PCLK manual divider
		 249:lut_data	= 	{24'h5001_83}; // SDE on, CMX on, AWB on
		 250:lut_data	= 	{24'h3503_00}; // AEC/AGC on             
		 251:lut_data	= 	{24'h4740_20}; // VS 1
		 252:lut_data	= 	{24'h503d_00}; // color bar
		 253:lut_data	= 	{24'h4741_00}; //
		default:lut_data	=	0;
	    endcase
    end

endmodule 


摄像头数据处理

`include "param.v"
module capture (
    input			wire						clk,
    input			wire						rst_n,
    input			wire						vsync,
    input			wire						href,
    input			wire		[ 7:0 ]		    din,
    input			wire						din_vld,

    output			wire						sop,
    output			wire						eop,
    output			wire						vld,
    output			wire		[ 15:0 ]		pixel	
);
// localparam	red    = 16'd63488;
// localparam	orange = 16'd64384;
// localparam	yellow = 16'd65472;
// localparam	green  = 16'd1024;
// localparam	blue   = 16'd31;
// localparam	indigo = 16'd18448;
// localparam	purple = 16'd32784;
// localparam	white  = 16'd65503;
// localparam	black  = 16'd0;


//信号定义
    reg     [11:0]      cnt_h       ;
    wire                add_cnt_h   ;
    wire                end_cnt_h   ;
    reg     [9:0]       cnt_v       ;
    wire                add_cnt_v   ;
    wire                end_cnt_v   ;
    
    reg     [1:0]       vsync_r     ;//同步打拍
    wire                vsync_nedge ;//下降沿
    reg                 flag        ;//串并转换标志
    
    reg     [15:0]      data        ;
    reg                 data_vld    ;
    reg                 data_sop    ;
    reg                 data_eop    ;

//计数器
    
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            cnt_h <= 0; 
        end
        else if(add_cnt_h) begin
            if(end_cnt_h)
                cnt_h <= 0; 
            else
                cnt_h <= cnt_h+1 ;
       end
    end
    assign add_cnt_h = flag & href;
    assign end_cnt_h = add_cnt_h  && cnt_h == (`H_AP << 1)-1;
    
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            cnt_v <= 0; 
        end
        else if(add_cnt_v) begin
            if(end_cnt_v)
                cnt_v <= 0; 
            else
                cnt_v <= cnt_v+1 ;
       end
    end
    assign add_cnt_v = end_cnt_h;
    assign end_cnt_v = add_cnt_v  && cnt_v == `V_AP-1 ;

//vsync同步打拍
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            vsync_r <= 2'b00;
        end
        else begin
            vsync_r <= {vsync_r[0],vsync};
        end
    end
    assign vsync_nedge = vsync_r[1] & ~vsync_r[0];

    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            flag <= 1'b0;
        end
        else if(din_vld & vsync_nedge)begin  //摄像头配置完成且场同步信号拉低之后开始采集有效数据
            flag <= 1'b1;
        end
        else if(end_cnt_v)begin     //一帧数据采集完拉低
            flag <= 1'b0;   
        end
    end

//data
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            data <= 0;
        end
        else begin
            data <= {data[7:0],din};//左移
        end
    end

//data_sop
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            data_sop <= 1'b0;
            data_eop <= 1'b0;
            data_vld <= 1'b0;
        end
        else begin
            data_sop <= add_cnt_h && cnt_h == 2-1 && cnt_v == 0;
            data_eop <= end_cnt_v;
            data_vld <= add_cnt_h && cnt_h[0] == 1'b0 ;
        end
    end

    assign pixel = data;
    assign sop = cnt_h == 1 && cnt_v == 0;
    assign eop = data_eop;
    assign vld = cnt_h[0];
    
endmodule //capture

2.SDRAM控制模块

`include"param.v"
module sdram_ctrl (
    input               clk             ,
    input               clk_in          ,
    input               clk_out         ,
    input               rst_n           ,
    //数据输入
    input   [15:0]      din             ,//摄像头输入像素数据
    input               din_sop         ,
    input               din_eop         ,    
    input               din_vld         ,
    //数据输出
    input               rdreq           ,//vga的读数据请求
    output  [15:0]      dout            ,//输出给vga的数据
    output              dout_vld        ,//输出给vga的数据有效标志
    //sdram_interface
    output              avm_write       ,//输出给sdram 接口 IP 的写请求
    output              avm_read        ,//输出给sdram 接口 IP 的读请求
    output  [23:0]      avm_addr        ,//输出给sdram 接口 IP 的读写地址
    output  [15:0]      avm_wrdata      ,//输出给sdram 接口 IP 的写数据
    input   [15:0]      avs_rddata      ,//sdram 接口 IP 输入的读数据
    input               avs_rddata_vld  ,
    input               avs_waitrequest    
);

//参数定义
    localparam  IDLE  = 4'b0001,
                WRITE = 4'b0010,
                READ  = 4'b0100,
                DONE  = 4'b1000;

//信号定义

    reg     [3:0]       state_c     ;
    reg     [3:0]       state_n     ;

    reg     [8:0]       cnt         ;//突发读写计数器
    wire                add_cnt     ;
    wire                end_cnt     ;
    
    reg     [1:0]       wr_bank     ;//写bank
    reg     [1:0]       rd_bank     ;//读bank
    reg     [21:0]      wr_addr     ;//写地址   行地址 + 列地址
    wire                add_wr_addr ;
    wire                end_wr_addr ;
    reg     [21:0]      rd_addr     ;//读地址   行地址 + 列地址
    wire                add_rd_addr ;
    wire                end_rd_addr ;

    reg                 change_bank ;//切换bank 
    reg                 wr_finish   ;//一帧数据写完
    reg     [1:0]       wr_finish_r ;//同步到写侧
    reg                 wr_data_flag;//wrfifo写数据的标志

    reg                 wr_flag     ;
    reg                 rd_flag     ;
    reg                 flag_sel    ;
    reg                 prior_flag  ;

    wire                idle2write  ;  
    wire                idle2read   ;
    wire                write2done  ;
    wire                read2done   ;

    reg     [15:0]      rd_data     ;//rfifo读数据输出
    reg                 rd_data_vld ;

    wire    [17:0]      wfifo_data  ; 
    wire                wfifo_rdreq ;
    wire                wfifo_wrreq ;
    wire    [17:0]      wfifo_q     ;
    wire                wfifo_empty ;
    wire    [10:0]      wfifo_usedw ;
    wire                wfifo_full  ;

    wire    [15:0]      rfifo_data  ;
    wire                rfifo_rdreq ;
    wire                rfifo_wrreq ;
    wire    [15:0]      rfifo_q     ;
    wire                rfifo_empty ;
    wire                rfifo_full  ;
    wire    [10:0]      rfifo_usedw ;

//状态机
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    always  @(*)begin
        case(state_c)
            IDLE  :begin 
                if(idle2write)
                    state_n = WRITE;
                else if(idle2read)
                    state_n = READ;
                else 
                    state_n = state_c;
            end 
            WRITE :begin 
                if(write2done)
                    state_n = DONE;
                else 
                    state_n = state_c;
            end     
            READ  :begin 
                if(read2done)
                    state_n = DONE;
                else 
                    state_n = state_c;
            end 
            DONE  :state_n = IDLE;
            default:state_n = IDLE;
        endcase  
    end

    assign idle2write = state_c == IDLE  && (~prior_flag && wfifo_usedw >= `USER_BL);
    assign idle2read  = state_c == IDLE  && prior_flag && rfifo_usedw <= `RD_UT;
    assign write2done = state_c == WRITE && end_cnt;
    assign read2done  = state_c == READ  && end_cnt;

//计数器
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(add_cnt)begin
            if(end_cnt)
                cnt <= 0;
            else
                cnt <= cnt + 1;
        end
    end

    assign add_cnt = (state_c == WRITE | state_c == READ) & ~avs_waitrequest; 
    assign end_cnt = add_cnt && cnt== `USER_BL-1;  

/************************读写优先级仲裁*****************************/
//rd_flag     ;//读请求标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rd_flag <= 0;
        end 
        else if(rfifo_usedw <= `RD_LT)begin   
            rd_flag <= 1'b1;
        end 
        else if(rfifo_usedw > `RD_UT)begin 
            rd_flag <= 1'b0;
        end 
    end

//wr_flag     ;//写请求标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wr_flag <= 0;
        end 
        else if(wfifo_usedw >= `USER_BL)begin 
            wr_flag <= 1'b1;
        end 
        else begin 
            wr_flag <= 1'b0;
        end 
    end

//flag_sel    ;//标记上一次操作
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag_sel <= 0;
        end 
        else if(read2done)begin 
            flag_sel <= 1;
        end 
        else if(write2done)begin 
            flag_sel <= 0;
        end 
    end

//prior_flag  ;//优先级标志 0:写优先级高   1:读优先级高     仲裁读、写的优先级
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            prior_flag <= 0;
        end 
        else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin   //突发写优先级高
            prior_flag <= 1'b0;
        end 
        else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin   //突发读优先级高
            prior_flag <= 1'b1;
        end 
    end

/******************************************************************/    

/********************      地址设计    ****************************/    

//wr_bank  rd_bank
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            wr_bank <= 2'b00;
            rd_bank <= 2'b11;
        end
        else if(change_bank)begin
            wr_bank <= ~wr_bank;
            rd_bank <= ~rd_bank;
        end
    end

// wr_addr   rd_addr
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            wr_addr <= 0; 
        end
        else if(add_wr_addr) begin
            if(end_wr_addr)
                wr_addr <= 0; 
            else
                wr_addr <= wr_addr+1 ;
       end
    end
    assign add_wr_addr = (state_c == WRITE) && ~avs_waitrequest;
    assign end_wr_addr = add_wr_addr  && wr_addr == `BURST_MAX-1 ;
    
    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            rd_addr <= 0; 
        end
        else if(add_rd_addr) begin
            if(end_rd_addr)
                rd_addr <= 0; 
            else
                rd_addr <= rd_addr+1 ;
       end
    end
    assign add_rd_addr = (state_c == READ) && ~avs_waitrequest;
    assign end_rd_addr = add_rd_addr  && rd_addr == `BURST_MAX-1;

//wr_finish     一帧数据全部写到SDRAM
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            wr_finish <= 1'b0;
        end
        else if(~wr_finish & end_wr_addr)begin  //写完  从wrfifo读出eop
            wr_finish <= 1'b1;
        end
        else if(wr_finish && end_rd_addr)begin  //读完
            wr_finish <= 1'b0;
        end
    end

//change_bank ;//切换bank 
    always  @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            change_bank <= 1'b0;
        end
        else begin
            change_bank <= wr_finish && end_rd_addr;
        end
    end

/****************************************************************/

/*********************** wrfifo 写数据   ************************/
//控制像素数据帧 写入 或 丢帧

    always  @(posedge clk_in or negedge rst_n)begin
        if(~rst_n)begin
            wr_data_flag <= 1'b0;
        end 
        else if(~wr_data_flag & ~wr_finish_r[1] & din_sop)begin//可以向wrfifo写数据 
            wr_data_flag <= 1'b1;
        end
        else if(/*wr_finish_r[1] && din_sop*/wr_data_flag & din_eop)begin//不可以向wrfifo写入数据
            wr_data_flag <= 1'b0;
        end
    end

    always  @(posedge clk_in or negedge rst_n)begin //把wr_finish从wrfifo的读侧同步到写侧
        if(~rst_n)begin
            wr_finish_r <= 0;
        end
        else begin
            wr_finish_r <= {wr_finish_r[0],wr_finish};
        end
    end

/****************************************************************/

    always  @(posedge clk_out or negedge rst_n)begin
        if(~rst_n)begin
            rd_data <= 0;
            rd_data_vld <= 1'b0;
        end
        else begin
            rd_data <= rfifo_q;
            rd_data_vld <= rfifo_rdreq;
        end
    end

wfifo	wrfifo_inst (
	.aclr   (~rst_n     ),
	.data   (wfifo_data ),
	.rdclk  (clk        ),
	.rdreq  (wfifo_rdreq),
	.wrclk  (clk_in     ),
	.wrreq  (wfifo_wrreq),
	.q      (wfifo_q    ),
	.rdempty(wfifo_empty),
	.rdusedw(wfifo_usedw),
	.wrfull (wfifo_full )
	);

    assign wfifo_data = {din_eop,din_sop,din};
    assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag);
    assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest;

rfifo u_rdfifo(
	.aclr       (~rst_n     ),
	.data       (rfifo_data ),
	.rdclk      (clk_out    ),
	.rdreq      (rfifo_rdreq),
	.wrclk      (clk        ),
	.wrreq      (rfifo_wrreq),
	.q          (rfifo_q    ), 
	.rdempty    (rfifo_empty),
	.wrfull     (rfifo_full ),
	.wrusedw    (rfifo_usedw)
);

    assign rfifo_data = avs_rddata;
    assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld;
    assign rfifo_rdreq = ~rfifo_empty & rdreq;

//输出
    assign dout       = rd_data;
    assign dout_vld   = rd_data_vld;
    assign avm_wrdata = wfifo_q[15:0];
    assign avm_write  = ~(state_c == WRITE && ~avs_waitrequest);
    assign avm_read   = ~(state_c == READ && ~avs_waitrequest);
    assign avm_addr   = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]}
                       :((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]}
                       :0);

endmodule 



3.vga模块

    module vga_dirve (input			wire						clk,            //系统时钟
                    input			wire						rst_n,          //复位
                    input			wire		[ 15:0 ]		rgb_data,       //16位RGB对应值
                    output			reg							h_sync,     //行同步信号
                    output			reg							v_sync,     //场同步信号
                    output			reg		[ 11:0 ]				addr_h, //行地址
                    output			reg		[ 11:0 ]				addr_v,  //列地址
                    output			wire		[ 4:0 ]				rgb_r,  //红基色
                    output			wire		[ 5:0 ]				rgb_g,  //绿基色
                    output			wire		[ 4:0 ]				rgb_b  //蓝基色
    );

//1280 * 640 60HZ
localparam	 H_FRONT = 110; // 行同步前沿信号周期长
localparam	 H_SYNC  = 40; // 行同步信号周期长
localparam	 H_BLACK = 220; // 行同步后沿信号周期长
localparam	 H_ACT   = 1280; // 行显示周期长
localparam	 V_FRONT = 5; // 场同步前沿信号周期长
localparam	 V_SYNC  = 5; // 场同步信号周期长
localparam	 V_BLACK = 20; // 场同步后沿信号周期长
localparam	 V_ACT   = 720; // 场显示周期长
// 640 * 480 60HZ
// localparam	 H_FRONT = 16; // 行同步前沿信号周期长
// localparam	 H_SYNC  = 96; // 行同步信号周期长
// localparam	 H_BLACK = 48; // 行同步后沿信号周期长
// localparam	 H_ACT   = 640; // 行显示周期长
// localparam	 V_FRONT = 11; // 场同步前沿信号周期长
// localparam	 V_SYNC  = 2; // 场同步信号周期长
// localparam	 V_BLACK = 31; // 场同步后沿信号周期长
// localparam	 V_ACT   = 480; // 场显示周期长

// 800 * 600 72HZ
// localparam	 H_FRONT = 40; // 行同步前沿信号周期长
// localparam	 H_SYNC  = 120; // 行同步信号周期长
// localparam	 H_BLACK = 88; // 行同步后沿信号周期长
// localparam	 H_ACT   = 800; // 行显示周期长
// localparam	 V_FRONT = 37; // 场同步前沿信号周期长
// localparam	 V_SYNC  = 6; // 场同步信号周期长
// localparam	 V_BLACK = 23; // 场同步后沿信号周期长
// localparam	 V_ACT   = 600; // 场显示周期长


localparam	H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期
localparam	V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期

reg			[ 11:0 ]			cnt_h			; // 行计数器
reg			[ 11:0 ]			cnt_v			; // 场计数器
reg			[ 15:0 ]			rgb			; // 对应显示颜色值

// 对应计数器开始、结束、计数信号
wire							flag_enable_cnt_h			;
wire							flag_clear_cnt_h			;
wire							flag_enable_cnt_v			;
wire							flag_clear_cnt_v			;
wire							flag_add_cnt_v  			;
wire							valid_area      			;



// 行计数
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_h <= 0;
    end
    else if ( flag_enable_cnt_h ) begin
        if ( flag_clear_cnt_h ) begin
            cnt_h <= 0;
        end
        else begin
            cnt_h <= cnt_h + 1;
        end
    end
    else begin
        cnt_h <= 0;
    end
end
assign flag_enable_cnt_h = 1;
assign flag_clear_cnt_h  = cnt_h == H_TOTAL - 1;

// 行同步信号
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        h_sync <= 1;
    end
    else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1
        h_sync <= 0;
    end
    else if ( flag_clear_cnt_h ) begin // 其余为0
        h_sync <= 1;
    end
    else begin
        h_sync <= h_sync;
    end
end

// 场计数
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        cnt_v <= 0;
    end
    else if ( flag_enable_cnt_v ) begin
        if ( flag_clear_cnt_v ) begin
            cnt_v <= 0;
        end
        else if ( flag_add_cnt_v ) begin
            cnt_v <= cnt_v + 1;
        end
        else begin
            cnt_v <= cnt_v;
        end
    end
    else begin
        cnt_v <= 0;
    end
end
assign flag_enable_cnt_v = flag_enable_cnt_h;
assign flag_clear_cnt_v  = cnt_v == V_TOTAL - 1;
assign flag_add_cnt_v    = flag_clear_cnt_h;

// 场同步信号
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        v_sync <= 1;
    end
    else if ( cnt_v == V_SYNC - 1 ) begin
        v_sync <= 0;
    end
    else if ( flag_clear_cnt_v ) begin
        v_sync <= 1;
    end
end

// 对应有效区域行地址 1-640
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_h <= 0;
    end
    else if ( valid_area ) begin
        addr_h <= cnt_h - H_SYNC - H_BLACK + 1;
    end
    else begin
        addr_h <= 0;
    end
end
// 对应有效区域列地址 1-480
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        addr_v <= 0;
    end
    else if ( valid_area ) begin
        addr_v <= cnt_v -V_SYNC - V_BLACK + 1;
    end
    else begin
        addr_v <= 0;
    end
end
// 有效显示区域
assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h < H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v < V_SYNC + V_BLACK + V_ACT;


// 显示颜色
always @( posedge clk or negedge rst_n ) begin
    if ( !rst_n ) begin
        rgb <= 16'h0;
    end
    else if ( valid_area ) begin
        rgb <= rgb_data;
    end
    else begin
        rgb <= 16'b0;
    end
end
assign rgb_r = rgb[ 15:11 ];
assign rgb_g = rgb[ 10:5 ];
assign rgb_b = rgb[ 4:0 ];

endmodule // vga_dirve

4.顶层文件

module camera_top (
    input			wire						clk,
    input			wire						rst_n,
    /* 配置寄存器 */
    output			wire						cmos_pwdn,
    output			wire						cmos_reset,
    output			wire						cmos_sioc,
    output			wire						cmos_siod,
    output			wire						cmos_xclk,

    input			wire						cmos_pclk,
    input			wire						cmos_vsync,
    input			wire						cmos_href,
    input			wire		[7:0]			cmos_din,

    output                                      sdram_clk       ,
    output                                      sdram_cke       ,   
    output                                      sdram_csn       ,   
    output                                      sdram_rasn      ,   
    output                                      sdram_casn      ,   
    output                                      sdram_wen       ,   
    output                      [1:0 ]          sdram_bank      ,   
    output                      [12:0]          sdram_addr      ,   
    inout                       [15:0]          sdram_dq        ,   
    output                      [1: 0]          sdram_dqm       ,

    //vga
    output			wire						h_sync,
    output			wire						v_sync,
    output			wire		[15:0]			vga_rgb

);

wire							clk_24			;
wire							clk_50			;
wire							clk_75			;
wire							clk_100			;
wire							clk_84			;
wire							clk_100_s			;
wire							clk_150			;
wire							clk_200			;
wire							pclk			;
wire							has_config			;
wire							rd_req			;
wire		[ 15:0 ]			dout			;
wire							sop			;
wire							eop			;
wire							vld			;
wire		[ 15:0 ]			data			;
wire							dout_vld			;
wire		[ 11:0 ]			addr_h			;
wire		[ 11:0 ]			addr_v			;
wire		[ 15:0 ]			rgb_data			;

//PLL
    pll	pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk ),
	.c0 ( clk_50 ),//50M
	.c1 ( clk_24 ),//24M
	.c2 ( clk_75 ),//75M
	.c3 ( clk_100 ),//100M
    .c4 ( clk_84 )
	);

    pll1	pll1_inst (
        .areset ( ~rst_n ),
        .inclk0 ( clk ),
        .c0 ( clk_100_s ),
        .c1 ( clk_150 ),
        .c2 ( clk_200 )
        );

    iobuf u_iobuf(
	.datain     (cmos_pclk  ),
    .dataout    (pclk       )
    );

    assign sdram_clk = clk_100_s;
    assign cmos_xclk = clk_24;

    cmos_top u_cmos_top(
        .clk        ( clk           ),
        .rst_n      ( rst_n         ),
        .scl        ( cmos_sioc      ),
        .sda        (  cmos_siod     ),
        .pwdn       ( cmos_pwdn      ),
        .reset      ( cmos_reset      ),
        .cfg_done   ( has_config    )
    );
    // camera_config_drive u_camera_config_drive(
    //     .clk   ( clk   ),
    //     .rst_n ( rst_n ),
    //     .pwdn  ( cmos_pwdn  ),
    //     .reset ( cmos_reset ),
    //     .sioc  ( cmos_sioc  ),
    //     .siod  ( cmos_siod  ),
    //     .done  ( has_config )
    // );

    capture u_capture(
        .clk   ( pclk   ),
        .rst_n ( rst_n ),
        .vsync ( cmos_vsync ),
        .href  ( cmos_href  ),
        .din   ( cmos_din   ),
        .din_vld(has_config),
        .sop   ( sop   ),
        .eop   ( eop   ),
        .vld   ( vld   ),
        .pixel  ( data  )
    );


    sdram_controller u_sdram_controller(
        .clk            ( clk_100       ),
        .clk_in         ( pclk          ),
        .clk_out        ( clk_75        ),
        .rst_n          ( rst_n          ),
        .sop            ( sop            ),
        .eop            ( eop            ),
        .din            ( data            ),
        .din_vld        ( vld           ),
        .rd_req         ( rd_req         ),
        .dout           ( dout           ),
        .dout_vld       ( dout_vld       ),
        .mem_cke            (sdram_cke     ),
        .mem_csn            (sdram_csn     ),
        .mem_rasn           (sdram_rasn    ),
        .mem_casn           (sdram_casn    ),
        .mem_wen            (sdram_wen     ),
        .mem_bank           (sdram_bank    ),
        .mem_addr           (sdram_addr    ),
        .mem_dq             (sdram_dq      ),
        .mem_dqm            (sdram_dqm     )  
    );

    vga_control u_vga_control(
        .clk    ( clk_75  ),
        .rst_n  ( rst_n  ),
        .din    ( dout   ),
        .din_vld ( dout_vld ),
        .addr_h ( addr_h ),
        .addr_v ( addr_v ),
        .rd_req ( rd_req ),
        .rgb_data  ( rgb_data  )
    );
    vga_dirve u_vga_dirve(
        .clk      ( clk_75    ),
        .rst_n    ( rst_n    ),
        .rgb_data ( rgb_data ),
        .h_sync   ( h_sync   ),
        .v_sync   ( v_sync   ),
        .addr_h   ( addr_h   ),
        .addr_v   ( addr_v   ),
        .rgb_r    ( rgb_r    ),
        .rgb_g    ( rgb_g    ),
        .rgb_b    ( rgb_b    )
    );
    // vga_interface u_vga(
    // /*input           */.clk      (clk_75   ),
    // /*input           */.rst_n    (rst_n ),
    // /*input   [15:0]  */.din      (dout      ),
    // /*input           */.din_vld  (dout_vld  ),
    // /*output          */.rdy      (rd_req    ),
    // /*output  [15:0]  */.vga_rgb  (rgb_data   ),
    // /*output          */.vga_hsync(h_sync ),
    // /*output          */.vga_vsync(v_sync )
    // );
    assign vga_rgb = rgb_data;
endmodule //camera_top

三、源码

https://github.com/TangtangSix/ov5640

更新时间 2023-11-08