一、FPGA Cyclone Ⅳ OV5640图像实时采集系统设计

1、系统框架

在这里插入图片描述

2、摄像头配置模块

使用SCCB协议配置摄像头

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:摄像头配置模块		
*********************************************************************************/

`include "param.v"

module sccb( 
    input		    clk		    ,//系统时钟50MHz
    input		    rst_n	    ,//复位信号

    output  reg     done        ,//配置完成标志
    output          cmos_rst_n  ,//摄像头复位信号
    output          cmos_pwdn   ,//摄像头掉电使能信号
    output	reg     cmos_scl	,//时钟线
    inout	        cmos_sda	 //数据线
);			
    //参数定义			 
    localparam  IDLE  = 3'd0    ,//初始化
                START = 3'd1    ,//起始
                D_ADD = 3'd2    ,//写入器件地址
                DC    = 3'd3    ,//响应(don't care)
                H_ADD = 3'd4    ,//写入寄存器地址高八位
                L_ADD = 3'd5    ,//写入寄存器地址低八位
                WR    = 3'd6    ,//写数据
                STOP  = 3'd7    ;//停止   

    //中间信号定义		 
    wire            idle2start  ;//状态跳转条件    
    wire            start2d_add ;        
    wire            d_add2dc    ;    
    wire            dc2h_add    ;    
    wire            h_add2dc    ;
    wire            dc2l_add    ; 
    wire            l_add2dc    ;   
    wire            dc2wr       ;
    wire            wr2dc       ;
    wire            dc2stop     ;    
    wire            stop2idle   ;    
    reg     [2:0]   state_c     ;//现态
    reg     [2:0]   state_n     ;//次态
    reg     [5:0]   cnt_1m      ;//50Mhz时钟做50分频产生1Mhz时钟        
    wire            add_cnt_1m  ;              
    wire            end_cnt_1m  ; 
    reg             clk_1m      ;//1Mhz时钟             
    reg     [1:0]   cnt_250k    ;//1Mhz时钟做4分频产生250Khz时钟 
    reg     [3:0]   cnt_bit     ;//bit计数
    wire            add_cnt_bit ;    
    reg     [7:0]   num         ;//配置寄存器个数
    reg     [20:0]  cnt_ms      ;//20ms计数        
    wire            add_cnt_ms  ;              
    wire            end_cnt_ms  ; 
    reg             en          ;//sccb使能
    reg     [31:0]  lut_data    ;//写入数据(分别是器件地址,寄存器地址和寄存器数据,各8bit)
    reg     [1:0]   flag_dc     ;//计数进入DC状态次数
    reg             sda         ;//sccb写数据寄存

    //cmos_sda:sccb写数据赋值
    assign cmos_sda = (state_c != DC)?sda:1'bz;

    //cnt_ms:20ms计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_ms <= 21'b0;
        end 
        else if(add_cnt_ms)begin 
            if(end_cnt_ms)begin 
                cnt_ms <= 21'b0;
            end
            else begin 
                cnt_ms <= cnt_ms + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_ms = cnt_ms != `CNT_MS;
    assign end_cnt_ms = add_cnt_ms && cnt_ms == `CNT_MS + 1'b1; 
    
    //cmos_rst_n:摄像头复位信号
    assign cmos_rst_n = cnt_ms > `RST_CMOS;

    //cmos_pwdn:摄像头掉电使能信号
    assign cmos_pwdn = cnt_ms < `PWDN;

    //en:sccb使能
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            en <= 1'b0;
        end 
        else if(cnt_ms == `CNT_MS - 1'b1)begin 
            en <= 1'b1;
        end 
        else if(num == `NUM_CFG)begin 
            en <= 1'b0;
        end 
    end           

    //cnt_1m:产生1Mhz时钟计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_1m <= 6'b0;
        end 
        else if(add_cnt_1m)begin 
            if(end_cnt_1m)begin 
                cnt_1m <= 6'b0;
            end
            else begin 
                cnt_1m <= cnt_1m + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_1m = (state_c != IDLE);
    assign end_cnt_1m = add_cnt_1m && cnt_1m == `CNT_1M - 1'b1;

    //clk_1m:1Mhz时钟
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            clk_1m <= 1'b1;
        end 
        else if((cnt_1m == (`CNT_1M >> 1) - 1'b1) || end_cnt_1m)begin 
            clk_1m <= ~clk_1m;
        end 
    end

    //cnt_250k:产生250Khz时钟计数
    always @(posedge clk_1m or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_250k <= 2'b0;
        end 
        else begin 
            cnt_250k <= cnt_250k + 1'b1;
        end 
    end

    //cnt_bit:bit计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_bit <= 4'b0;
        end 
        else if(add_cnt_bit)begin 
            cnt_bit <= cnt_bit + 1'b1;
        end
        else if(state_c == DC)begin
            cnt_bit <= 4'b0;
        end
    end 
    
    assign add_cnt_bit = ((state_c == D_ADD) || (state_c == H_ADD) || (state_c == L_ADD) || (state_c == WR))
     && (cnt_250k == 2'd3) && (cnt_1m == 1'b1);

    //flag_dc:计数进入DC状态次数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag_dc <= 2'b0;
        end 
        else if((cnt_250k == 2'd2) && end_cnt_1m && (state_c == DC))begin 
            flag_dc <= flag_dc + 1'b1;
        end 
    end

    //状态机
    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  : if(idle2start)begin
                        state_n = START;
                    end
                    else begin
                        state_n = state_c;
                    end
            START : if(start2d_add)begin
                        state_n = D_ADD;
                    end
                    else begin
                        state_n = state_c;
                    end
            D_ADD : if(d_add2dc)begin
                        state_n = DC;
                    end
                    else begin
                        state_n = state_c;
                    end
            DC    : if(dc2h_add)begin
                        state_n = H_ADD;
                    end
                    else if(dc2l_add)begin
                        state_n = L_ADD;
                    end
                    else if(dc2wr)begin
                        state_n = WR;
                    end
                    else if(dc2stop)begin
                        state_n = STOP;
                    end
                    else begin
                        state_n = state_c;
                    end
            H_ADD : if(h_add2dc)begin
                        state_n = DC;
                    end
                    else begin
                        state_n = state_c;
                    end
            L_ADD : if(l_add2dc)begin
                        state_n = DC;
                    end
                    else begin
                        state_n = state_c;
                    end
            WR    : if(wr2dc)begin
                        state_n = DC;
                    end
                    else begin
                        state_n = state_c;
                    end
            STOP  : if(stop2idle)begin
                        state_n = IDLE;
                    end
                    else begin
                        state_n = state_c;
                    end
            default: state_n = state_c;
        endcase
    end

    //给定状态跳转条件
    assign    idle2start     =   (state_c == IDLE ) && en ;
    assign    start2d_add    =   (state_c == START) && (cnt_250k == 2'd2) && end_cnt_1m ;
    assign    d_add2dc       =   (state_c == D_ADD) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
    assign    dc2h_add       =   (state_c == DC   ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 1'b0) ;
    assign    h_add2dc       =   (state_c == H_ADD) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
    assign    dc2l_add       =   (state_c == DC   ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 1'b1) ; 
    assign    l_add2dc       =   (state_c == L_ADD) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ; 
    assign    dc2wr          =   (state_c == DC   ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 2'd2) ;
    assign    wr2dc          =   (state_c == WR   ) && (cnt_250k == 2'd2) && end_cnt_1m && (cnt_bit == 4'd8) ;
    assign    dc2stop        =   (state_c == DC   ) && (cnt_250k == 2'd2) && end_cnt_1m && (flag_dc == 2'd3) ;
    assign    stop2idle      =   (state_c == STOP ) && (cnt_250k == 2'd2) && end_cnt_1m ;

    //cmos_scl:时钟总线
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cmos_scl <= 1'b1;
        end 
        else if((state_c == STOP))begin
            if((cnt_250k == 2'd3) && end_cnt_1m)begin
                cmos_scl <= 1'b1;
            end
        end
        else if(state_c != IDLE)begin 
            if((cnt_250k == 1'b1) && end_cnt_1m)begin
                cmos_scl <= 1'b0;
            end
            else if((cnt_250k == 2'd3) && end_cnt_1m)begin
                cmos_scl <= 1'b1;
            end
        end 
        else begin 
            cmos_scl <= 1'b1;
        end 
    end

    //sda:数据总线
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            sda <= 1'b1;
        end 
        else begin 
            case (state_c)
                START:if((cnt_250k == 1'b0) && end_cnt_1m)begin
                    sda <= 1'b0;
                end
                D_ADD:if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
                    sda <= lut_data[32 - cnt_bit];
                end
                DC   :if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
                    sda <= 1'b0;
                end
                H_ADD:if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
                    sda <= lut_data[24 - cnt_bit];
                end
                L_ADD:if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
                    sda <= lut_data[16 - cnt_bit];
                end
                WR   :if((cnt_250k == 2'd3) && (cnt_1m == 2'd2))begin
                    sda <= lut_data[8 - cnt_bit];
                end
                STOP :if((cnt_250k == 1'b0) && end_cnt_1m)begin
                    sda <= 1'b1;
                end
                default: sda <= 1'b1;
            endcase
        end 
    end

    //num:配置寄存器个数计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            num <= 1'b0;
        end 
        else if(dc2stop)begin 
            num <= num + 1'b1;
        end 
    end

    //done:配置完成标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            done <= 1'b0;
        end 
        else if((num == `NUM_CFG) && stop2idle)begin 
            done <= 1'b1;
        end 
    end

    //lut_data:数据
    always@(*)begin
        case(num)			  
            8'd0  : lut_data <= {8'h78,16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠
            8'd1  : lut_data <= {8'h78,16'h3008,8'h02}; //正常工作模式
            8'd2  : lut_data <= {8'h78,16'h3103,8'h02}; //Bit[1]:1 PLL Clock
            //引脚输入/输出控制 FREX/VSYNC/HREF/PCLK/D[9:6]
            8'd3  : lut_data <= {8'h78,8'h30,8'h17,8'hff};
            //引脚输入/输出控制 D[5:0]/GPIO1/GPIO0 
            8'd4  : lut_data <= {8'h78,16'h3018,8'hff};
            8'd5  : lut_data <= {8'h78,16'h3037,8'h13}; //PLL分频控制
            8'd6  : lut_data <= {8'h78,16'h3108,8'h01}; //系统根分频器
            8'd7  : lut_data <= {8'h78,16'h3630,8'h36};
            8'd8  : lut_data <= {8'h78,16'h3631,8'h0e};
            8'd9  : lut_data <= {8'h78,16'h3632,8'he2};
            8'd10 : lut_data <= {8'h78,16'h3633,8'h12};
            8'd11 : lut_data <= {8'h78,16'h3621,8'he0};
            8'd12 : lut_data <= {8'h78,16'h3704,8'ha0};
            8'd13 : lut_data <= {8'h78,16'h3703,8'h5a};
            8'd14 : lut_data <= {8'h78,16'h3715,8'h78};
            8'd15 : lut_data <= {8'h78,16'h3717,8'h01};
            8'd16 : lut_data <= {8'h78,16'h370b,8'h60};
            8'd17 : lut_data <= {8'h78,16'h3705,8'h1a};
            8'd18 : lut_data <= {8'h78,16'h3905,8'h02};
            8'd19 : lut_data <= {8'h78,16'h3906,8'h10};
            8'd20 : lut_data <= {8'h78,16'h3901,8'h0a};
            8'd21 : lut_data <= {8'h78,16'h3731,8'h12};
            8'd22 : lut_data <= {8'h78,16'h3600,8'h08}; //VCM控制,用于自动聚焦
            8'd23 : lut_data <= {8'h78,16'h3601,8'h33}; //VCM控制,用于自动聚焦
            8'd24 : lut_data <= {8'h78,16'h302d,8'h60}; //系统控制
            8'd25 : lut_data <= {8'h78,16'h3620,8'h52};
            8'd26 : lut_data <= {8'h78,16'h371b,8'h20};
            8'd27 : lut_data <= {8'h78,16'h471c,8'h50};
            8'd28 : lut_data <= {8'h78,16'h3a13,8'h43}; //AEC(自动曝光控制)
            8'd29 : lut_data <= {8'h78,16'h3a18,8'h00}; //AEC 增益上限
            8'd30 : lut_data <= {8'h78,16'h3a19,8'hf8}; //AEC 增益上限
            8'd31 : lut_data <= {8'h78,16'h3635,8'h13};
            8'd32 : lut_data <= {8'h78,16'h3636,8'h03};
            8'd33 : lut_data <= {8'h78,16'h3634,8'h40};
            8'd34 : lut_data <= {8'h78,16'h3622,8'h01};
            8'd35 : lut_data <= {8'h78,16'h3c01,8'h34};
            8'd36 : lut_data <= {8'h78,16'h3c04,8'h28};
            8'd37 : lut_data <= {8'h78,16'h3c05,8'h98};
            8'd38 : lut_data <= {8'h78,16'h3c06,8'h00}; //light meter 1 阈值[15:8]
            8'd39 : lut_data <= {8'h78,16'h3c07,8'h08}; //light meter 1 阈值[7:0]
            8'd40 : lut_data <= {8'h78,16'h3c08,8'h00}; //light meter 2 阈值[15:8]
            8'd41 : lut_data <= {8'h78,16'h3c09,8'h1c}; //light meter 2 阈值[7:0]
            8'd42 : lut_data <= {8'h78,16'h3c0a,8'h9c}; //sample number[15:8]
            8'd43 : lut_data <= {8'h78,16'h3c0b,8'h40}; //sample number[7:0]
            8'd44 : lut_data <= {8'h78,16'h3810,8'h00}; //Timing Hoffset[11:8]
            8'd45 : lut_data <= {8'h78,16'h3811,8'h10}; //Timing Hoffset[7:0]
            8'd46 : lut_data <= {8'h78,16'h3812,8'h00}; //Timing Voffset[10:8]
            8'd47 : lut_data <= {8'h78,16'h3708,8'h64};
            8'd48 : lut_data <= {8'h78,16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号
            8'd49 : lut_data <= {8'h78,16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新
            8'd50 : lut_data <= {8'h78,16'h3000,8'h00}; //系统块复位控制
            8'd51 : lut_data <= {8'h78,16'h3004,8'hff}; //时钟使能控制
            8'd52 : lut_data <= {8'h78,16'h4300,8'h61}; //格式控制 RGB565
            8'd53 : lut_data <= {8'h78,16'h501f,8'h01}; //ISP RGB
            8'd54 : lut_data <= {8'h78,16'h440e,8'h00};
            8'd55 : lut_data <= {8'h78,16'h5000,8'ha7}; //ISP控制
            8'd56 : lut_data <= {8'h78,16'h3a0f,8'h30}; //AEC控制;stable range in high
            8'd57 : lut_data <= {8'h78,16'h3a10,8'h28}; //AEC控制;stable range in low
            8'd58 : lut_data <= {8'h78,16'h3a1b,8'h30}; //AEC控制;stable range out high
            8'd59 : lut_data <= {8'h78,16'h3a1e,8'h26}; //AEC控制;stable range out low
            8'd60 : lut_data <= {8'h78,16'h3a11,8'h60}; //AEC控制; fast zone high
            8'd61 : lut_data <= {8'h78,16'h3a1f,8'h14}; //AEC控制; fast zone low
            //LENC(镜头校正)控制 16'h5800~16'h583d
            8'd62 : lut_data <= {8'h78,16'h5800,8'h23}; 
            8'd63 : lut_data <= {8'h78,16'h5801,8'h14};
            8'd64 : lut_data <= {8'h78,16'h5802,8'h0f};
            8'd65 : lut_data <= {8'h78,16'h5803,8'h0f};
            8'd66 : lut_data <= {8'h78,16'h5804,8'h12};
            8'd67 : lut_data <= {8'h78,16'h5805,8'h26};
            8'd68 : lut_data <= {8'h78,16'h5806,8'h0c};
            8'd69 : lut_data <= {8'h78,16'h5807,8'h08};
            8'd70 : lut_data <= {8'h78,16'h5808,8'h05};
            8'd71 : lut_data <= {8'h78,16'h5809,8'h05};
            8'd72 : lut_data <= {8'h78,16'h580a,8'h08};
            8'd73 : lut_data <= {8'h78,16'h580b,8'h0d};
            8'd74 : lut_data <= {8'h78,16'h580c,8'h08};
            8'd75 : lut_data <= {8'h78,16'h580d,8'h03};
            8'd76 : lut_data <= {8'h78,16'h580e,8'h00};
            8'd77 : lut_data <= {8'h78,16'h580f,8'h00};
            8'd78 : lut_data <= {8'h78,16'h5810,8'h03};
            8'd79 : lut_data <= {8'h78,16'h5811,8'h09};
            8'd80 : lut_data <= {8'h78,16'h5812,8'h07};
            8'd81 : lut_data <= {8'h78,16'h5813,8'h03};
            8'd82 : lut_data <= {8'h78,16'h5814,8'h00};
            8'd83 : lut_data <= {8'h78,16'h5815,8'h01};
            8'd84 : lut_data <= {8'h78,16'h5816,8'h03};
            8'd85 : lut_data <= {8'h78,16'h5817,8'h08};
            8'd86 : lut_data <= {8'h78,16'h5818,8'h0d};
            8'd87 : lut_data <= {8'h78,16'h5819,8'h08};
            8'd88 : lut_data <= {8'h78,16'h581a,8'h05};
            8'd89 : lut_data <= {8'h78,16'h581b,8'h06};
            8'd90 : lut_data <= {8'h78,16'h581c,8'h08};
            8'd91 : lut_data <= {8'h78,16'h581d,8'h0e};
            8'd92 : lut_data <= {8'h78,16'h581e,8'h29};
            8'd93 : lut_data <= {8'h78,16'h581f,8'h17};
            8'd94 : lut_data <= {8'h78,16'h5820,8'h11};
            8'd95 : lut_data <= {8'h78,16'h5821,8'h11};
            8'd96 : lut_data <= {8'h78,16'h5822,8'h15};
            8'd97 : lut_data <= {8'h78,16'h5823,8'h28};
            8'd98 : lut_data <= {8'h78,16'h5824,8'h46};
            8'd99 : lut_data <= {8'h78,16'h5825,8'h26};
            8'd100: lut_data <= {8'h78,16'h5826,8'h08};
            8'd101: lut_data <= {8'h78,16'h5827,8'h26};
            8'd102: lut_data <= {8'h78,16'h5828,8'h64};
            8'd103: lut_data <= {8'h78,16'h5829,8'h26};
            8'd104: lut_data <= {8'h78,16'h582a,8'h24};
            8'd105: lut_data <= {8'h78,16'h582b,8'h22};
            8'd106: lut_data <= {8'h78,16'h582c,8'h24};
            8'd107: lut_data <= {8'h78,16'h582d,8'h24};
            8'd108: lut_data <= {8'h78,16'h582e,8'h06};
            8'd109: lut_data <= {8'h78,16'h582f,8'h22};
            8'd110: lut_data <= {8'h78,16'h5830,8'h40};
            8'd111: lut_data <= {8'h78,16'h5831,8'h42};
            8'd112: lut_data <= {8'h78,16'h5832,8'h24};
            8'd113: lut_data <= {8'h78,16'h5833,8'h26};
            8'd114: lut_data <= {8'h78,16'h5834,8'h24};
            8'd115: lut_data <= {8'h78,16'h5835,8'h22};
            8'd116: lut_data <= {8'h78,16'h5836,8'h22};
            8'd117: lut_data <= {8'h78,16'h5837,8'h26};
            8'd118: lut_data <= {8'h78,16'h5838,8'h44};
            8'd119: lut_data <= {8'h78,16'h5839,8'h24};
            8'd120: lut_data <= {8'h78,16'h583a,8'h26};
            8'd121: lut_data <= {8'h78,16'h583b,8'h28};
            8'd122: lut_data <= {8'h78,16'h583c,8'h42};
            8'd123: lut_data <= {8'h78,16'h583d,8'hce};
            //AWB(自动白平衡控制) 16'h5180~16'h519e
            8'd124: lut_data <= {8'h78,16'h5180,8'hff};
            8'd125: lut_data <= {8'h78,16'h5181,8'hf2};
            8'd126: lut_data <= {8'h78,16'h5182,8'h00};
            8'd127: lut_data <= {8'h78,16'h5183,8'h14};
            8'd128: lut_data <= {8'h78,16'h5184,8'h25};
            8'd129: lut_data <= {8'h78,16'h5185,8'h24};
            8'd130: lut_data <= {8'h78,16'h5186,8'h09};
            8'd131: lut_data <= {8'h78,16'h5187,8'h09};
            8'd132: lut_data <= {8'h78,16'h5188,8'h09};
            8'd133: lut_data <= {8'h78,16'h5189,8'h75};
            8'd134: lut_data <= {8'h78,16'h518a,8'h54};
            8'd135: lut_data <= {8'h78,16'h518b,8'he0};
            8'd136: lut_data <= {8'h78,16'h518c,8'hb2};
            8'd137: lut_data <= {8'h78,16'h518d,8'h42};
            8'd138: lut_data <= {8'h78,16'h518e,8'h3d};
            8'd139: lut_data <= {8'h78,16'h518f,8'h56};
            8'd140: lut_data <= {8'h78,16'h5190,8'h46};
            8'd141: lut_data <= {8'h78,16'h5191,8'hf8};
            8'd142: lut_data <= {8'h78,16'h5192,8'h04};
            8'd143: lut_data <= {8'h78,16'h5193,8'h70};
            8'd144: lut_data <= {8'h78,16'h5194,8'hf0};
            8'd145: lut_data <= {8'h78,16'h5195,8'hf0};
            8'd146: lut_data <= {8'h78,16'h5196,8'h03};
            8'd147: lut_data <= {8'h78,16'h5197,8'h01};
            8'd148: lut_data <= {8'h78,16'h5198,8'h04};
            8'd149: lut_data <= {8'h78,16'h5199,8'h12};
            8'd150: lut_data <= {8'h78,16'h519a,8'h04};
            8'd151: lut_data <= {8'h78,16'h519b,8'h00};
            8'd152: lut_data <= {8'h78,16'h519c,8'h06};
            8'd153: lut_data <= {8'h78,16'h519d,8'h82};
            8'd154: lut_data <= {8'h78,16'h519e,8'h38};
            //Gamma(伽马)控制 16'h5480~16'h5490
            8'd155: lut_data <= {8'h78,16'h5480,8'h01}; 
            8'd156: lut_data <= {8'h78,16'h5481,8'h08};
            8'd157: lut_data <= {8'h78,16'h5482,8'h14};
            8'd158: lut_data <= {8'h78,16'h5483,8'h28};
            8'd159: lut_data <= {8'h78,16'h5484,8'h51};
            8'd160: lut_data <= {8'h78,16'h5485,8'h65};
            8'd161: lut_data <= {8'h78,16'h5486,8'h71};
            8'd162: lut_data <= {8'h78,16'h5487,8'h7d};
            8'd163: lut_data <= {8'h78,16'h5488,8'h87};
            8'd164: lut_data <= {8'h78,16'h5489,8'h91};
            8'd165: lut_data <= {8'h78,16'h548a,8'h9a};
            8'd166: lut_data <= {8'h78,16'h548b,8'haa};
            8'd167: lut_data <= {8'h78,16'h548c,8'hb8};
            8'd168: lut_data <= {8'h78,16'h548d,8'hcd};
            8'd169: lut_data <= {8'h78,16'h548e,8'hdd};
            8'd170: lut_data <= {8'h78,16'h548f,8'hea};
            8'd171: lut_data <= {8'h78,16'h5490,8'h1d};
            //CMX(彩色矩阵控制) 16'h5381~16'h538b
            8'd172: lut_data <= {8'h78,16'h5381,8'h1e};
            8'd173: lut_data <= {8'h78,16'h5382,8'h5b};
            8'd174: lut_data <= {8'h78,16'h5383,8'h08};
            8'd175: lut_data <= {8'h78,16'h5384,8'h0a};
            8'd176: lut_data <= {8'h78,16'h5385,8'h7e};
            8'd177: lut_data <= {8'h78,16'h5386,8'h88};
            8'd178: lut_data <= {8'h78,16'h5387,8'h7c};
            8'd179: lut_data <= {8'h78,16'h5388,8'h6c};
            8'd180: lut_data <= {8'h78,16'h5389,8'h10};
            8'd181: lut_data <= {8'h78,16'h538a,8'h01};
            8'd182: lut_data <= {8'h78,16'h538b,8'h98};
            //SDE(特殊数码效果)控制 16'h5580~16'h558b
            8'd183: lut_data <= {8'h78,16'h5580,8'h06};
            8'd184: lut_data <= {8'h78,16'h5583,8'h40};
            8'd185: lut_data <= {8'h78,16'h5584,8'h10};
            8'd186: lut_data <= {8'h78,16'h5589,8'h10};
            8'd187: lut_data <= {8'h78,16'h558a,8'h00};
            8'd188: lut_data <= {8'h78,16'h558b,8'hf8};
            8'd189: lut_data <= {8'h78,16'h501d,8'h40}; //ISP MISC
            //CIP(颜色插值)控制 (16'h5300~16'h530c)
            8'd190: lut_data <= {8'h78,16'h5300,8'h08};
            8'd191: lut_data <= {8'h78,16'h5301,8'h30};
            8'd192: lut_data <= {8'h78,16'h5302,8'h10};
            8'd193: lut_data <= {8'h78,16'h5303,8'h00};
            8'd194: lut_data <= {8'h78,16'h5304,8'h08};
            8'd195: lut_data <= {8'h78,16'h5305,8'h30};
            8'd196: lut_data <= {8'h78,16'h5306,8'h08};
            8'd197: lut_data <= {8'h78,16'h5307,8'h16};
            8'd198: lut_data <= {8'h78,16'h5309,8'h08};
            8'd199: lut_data <= {8'h78,16'h530a,8'h30};
            8'd200: lut_data <= {8'h78,16'h530b,8'h04};
            8'd201: lut_data <= {8'h78,16'h530c,8'h06};
            8'd202: lut_data <= {8'h78,16'h5025,8'h00};
            //系统时钟分频 Bit[7:4]:系统时钟分频 input clock =24Mhz, PCLK = 48Mhz
            8'd203: lut_data <= {8'h78,16'h3035,8'h11}; 
            8'd204: lut_data <= {8'h78,16'h3036,8'h3c}; //PLL倍频
            8'd205: lut_data <= {8'h78,16'h3c07,8'h08};
            //时序控制 16'h3800~16'h3821
            8'd206: lut_data <= {8'h78,16'h3820,8'h46};
            8'd207: lut_data <= {8'h78,16'h3821,8'h01};
            8'd208: lut_data <= {8'h78,16'h3814,8'h31};
            8'd209: lut_data <= {8'h78,16'h3815,8'h31};
            8'd210: lut_data <= {8'h78,16'h3800,8'h00};
            8'd211: lut_data <= {8'h78,16'h3801,8'h00};
            8'd212: lut_data <= {8'h78,16'h3802,8'h00};
            8'd213: lut_data <= {8'h78,16'h3803,8'h04};
            8'd214: lut_data <= {8'h78,16'h3804,8'h0a};
            8'd215: lut_data <= {8'h78,16'h3805,8'h3f};
            8'd216: lut_data <= {8'h78,16'h3806,8'h07};
            8'd217: lut_data <= {8'h78,16'h3807,8'h9b};
            //设置输出像素个数
            //DVP 输出水平像素点数高4位
            8'd218: lut_data <= {8'h78,16'h3808,8'h04};
            //DVP 输出水平像素点数低8位
            8'd219: lut_data <= {8'h78,16'h3809,8'h00};
            //DVP 输出垂直像素点数高3位
            8'd220: lut_data <= {8'h78,16'h380a,8'h03};
            //DVP 输出垂直像素点数低8位
            8'd221: lut_data <= {8'h78,16'h380b,8'h00};
            //水平总像素大小高5位
            8'd222: lut_data <= {8'h78,16'h380c,8'h08};
            //水平总像素大小低8位 
            8'd223: lut_data <= {8'h78,16'h380d,8'hc0};
            //垂直总像素大小高5位 
            8'd224: lut_data <= {8'h78,16'h380e,8'h04};
            //垂直总像素大小低8位     
            8'd225: lut_data <= {8'h78,16'h380f,8'hf8};

            8'd226: lut_data <= {8'h78,16'h3813,8'h06};
            8'd227: lut_data <= {8'h78,16'h3618,8'h00};
            8'd228: lut_data <= {8'h78,16'h3612,8'h29};
            8'd229: lut_data <= {8'h78,16'h3709,8'h52};
            8'd230: lut_data <= {8'h78,16'h370c,8'h03};
            8'd231: lut_data <= {8'h78,16'h3a02,8'h17}; //60Hz max exposure
            8'd232: lut_data <= {8'h78,16'h3a03,8'h10}; //60Hz max exposure
            8'd233: lut_data <= {8'h78,16'h3a14,8'h17}; //50Hz max exposure
            8'd234: lut_data <= {8'h78,16'h3a15,8'h10}; //50Hz max exposure
            8'd235: lut_data <= {8'h78,16'h4004,8'h02}; //BLC(背光) 2 lines
            8'd236: lut_data <= {8'h78,16'h4713,8'h03}; //JPEG mode 3
            8'd237: lut_data <= {8'h78,16'h4407,8'h04}; //量化标度
            8'd238: lut_data <= {8'h78,16'h460c,8'h22};     
            8'd239: lut_data <= {8'h78,16'h4837,8'h22}; //DVP CLK divider
            8'd240: lut_data <= {8'h78,16'h3824,8'h02}; //DVP CLK divider
            8'd241: lut_data <= {8'h78,16'h5001,8'ha3}; //ISP 控制
            8'd242: lut_data <= {8'h78,16'h3b07,8'h0a}; //帧曝光模式  
            //彩条测试使能 
            8'd243: lut_data <= {8'h78,16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示
            //测试闪光灯功能
            8'd244: lut_data <= {8'h78,16'h3016,8'h02};
            8'd245: lut_data <= {8'h78,16'h301c,8'h02};
            8'd246: lut_data <= {8'h78,16'h3019,8'h02}; //打开闪光灯
            8'd247: lut_data <= {8'h78,16'h3019,8'h00}; //关闭闪光灯
            //只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
            default : lut_data <= {8'h78,16'h300a,8'h00}; //器件ID高8位
        endcase
    end
                        
endmodule

3、图像数据拼接模块

将摄像头采集的高八位和低八位图像数据进行拼接

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:图像数据采集模块		
*********************************************************************************/

`include "param.v"

module pixel_sampling( 
    input				clk		      ,//像素采集工作时钟,pclk   
    input				rst_n	      ,//复位信号   
    input       [7:0]   din           ,//摄像头采集像素
    input               vsync         ,//场同步信号
    input               href          ,//行有效信号

    output  reg         dout_valid    ,//输出数据有效标志
    output	reg	[15:0]	dout	       //输出拼接完成的像素   
);								                    
    //中间信号定义		 
    reg		    [7:0]   din_r         ;//像素缓存
    reg         [1:0]   v_r           ;//场同步信号同步打拍,检测上升沿
    reg                 flag          ;//像素拼接标志,高电平拼接,低电平缓存
    reg                 pixel_valid   ;//舍弃10帧完成
    reg		    [3:0]	cnt_frame     ;//像素帧计数
    wire	            add_cnt_frame ;
    wire	            end_cnt_frame ;

    //v_r:场同步信号同步打拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            v_r <= 2'b0;
        end 
        else begin 
            v_r <= {v_r[0], vsync};
        end 
    end

    //cnt_frame:像素帧计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_frame <= 4'b0;
        end 
        else if(add_cnt_frame)begin 
            if(end_cnt_frame)begin 
                cnt_frame <= 4'b0;
            end
            else begin 
                cnt_frame <= cnt_frame + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_frame = (v_r[0] & ~v_r[1]) && (cnt_frame <= `PIXEL);
    assign end_cnt_frame = add_cnt_frame && cnt_frame == `PIXEL + 1'b1;

    //pixel_valid:舍弃10帧完成
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            pixel_valid <= 1'b0;
        end 
        else if(cnt_frame == `PIXEL)begin 
            pixel_valid <= 1'b1;
        end 
    end

    //flag:像素拼接标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag <= 1'b0;
        end 
        else if(href && pixel_valid)begin 
            flag <= ~flag;
        end 
        else begin
            flag <= 1'b0;
        end
    end

    //din_r:像素缓存
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            din_r <= 8'b0;
        end 
        else if(href && pixel_valid)begin 
            din_r <= din;
        end 
    end

    //dout:像素拼接
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout <= 16'b0;
        end 
        else if(href && pixel_valid && flag)begin 
            dout <= {din_r, din};
        end 
    end

    //dout_valid:输出数据有效标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dout_valid <= 1'b0;
        end 
        else if(href && pixel_valid)begin 
            dout_valid <= flag;
        end 
        else begin
            dout_valid <= 1'b0;
        end
    end
                        
endmodule

4、SDRAM操作模块

SDRAM初始化、自刷新、读和写操作使用一个状态机完成

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:SDRAM模块		
*********************************************************************************/

`include    "param.v"

module sdram( 
    input				clk		    ,//100Mhz
    input				rst_n	    ,//复位,低电平
    input				w_req       ,//写请求
    input		        r_req	    ,//读请求
    input		[23:0]	w_addr 	    ,//写地址
    input		[23:0]	r_addr	    ,//读地址
    input		[15:0]	w_data	    ,//写数据

    output      [15:0]  r_data      ,//读数据
    output reg  [12:0]  sdram_addr  ,//地址总线
    output reg  [1:0]   sdram_ba    ,//bank选择
    inout       [15:0]  sdram_dq    ,//数据总线
    output reg  [3:0]   cmd         ,//命令
    output              r_ack       ,//写操作响应
    output              wr_ack      ,//读操作响应
    output reg          init_done   ,//初始化结束
    output              w_done      ,//写操作完成标志
    output              r_done       //读操作完成标志

);								 
    //参数定义
    parameter   IDLE      =   4'd0  ,//初始化
                PRE       =   4'd1  ,//预充电状态
                AREF      =   4'd2  ,//自动刷新状态
                REGIST    =   4'd3  ,//模式寄存器配置
                W_ACT     =   4'd4  ,//写行激活
                R_ACT     =   4'd5  ,//读行激活
                WRITE     =   4'd6  ,//写状态
                READ      =   4'd7  ,//读状态
                BURST     =   4'd8  ;//突发停止

    //状态跳转条件
    wire                idle2pre    ;
    wire                pre2aref    ;
    wire                aref2regist ;
    wire                regist2idle ;
    wire                aref2idle   ;
    wire                idle2w_act  ;
    wire                idle2r_act  ;
    wire                w_act2write ;
    wire                write2burst ;
    wire                r_act2read  ;
    wire                read2burst  ;
    wire                burst2pre   ;
    wire                pre2idle    ;

    //中间信号
    reg         [3:0]   state_c     ;//现态
    reg         [3:0]   state_n     ;//次态
    reg                 waite_done  ;//200us上电等待结束
    reg         [1:0]   atref_time  ;//自动刷新次数		
    reg         [13:0]  cnt_init    ;//上电初始化计数
    wire                add_cnt_init;
    wire                end_cnt_init;
    reg         [9:0]   cnt_aref    ;//自动刷新计数
    wire                add_cnt_aref;
    wire                end_cnt_aref;
    reg         [1:0]   cnt_4clk    ;//四周期时钟计数器
    wire                add_cnt_4clk;
    reg         [2:0]   cnt_7clk    ;//7周期时钟计数器
    wire                add_cnt_7clk;
    reg         [9:0]   cnt_bit     ;//读写计数
    wire                add_cnt_bit ;
    wire                end_cnt_bit ;
    wire                rd_ack      ;//读响应赋值
    reg         [2:0]   r_ack_r     ;//读响应赋值打四拍
    reg                 wr          ;//写操作标志
    reg                 rd          ;//读操作标志
    reg                 aref_req    ;//自动刷新请求
    reg                 ack_r1      ;//写响应打拍
    reg                 ack_r2      ;//读响应打拍
    reg         [15:0]  dq_r        ;//sdram读数据打拍
    reg                 w_ack       ;//写响应打拍
    reg                 w_en        ;//写使能
    reg                 r_en        ;//读使能

    // w_en:写使能
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            w_en <= 1'b0;
        end 
        else if(w_req)begin 
            w_en <= 1'b1;
        end 
        else if(wr && pre2idle)begin 
            w_en <= 1'b0;
        end 
    end

    // r_en:读使能
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            r_en <= 1'b0;
        end 
        else if(r_req)begin 
            r_en <= 1'b1;
        end 
        else if(rd && pre2idle)begin 
            r_en <= 1'b0;
        end 
    end

    // dq_r:sdram读数据打拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            dq_r <= 16'b0;
        end 
        else begin 
            dq_r <= sdram_dq;
        end 
    end

    // cnt_init:上电等待200us计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_init <= 14'b0;
        end 
        else if(add_cnt_init)begin 
            if(end_cnt_init)begin 
                cnt_init <= 14'b0;
            end
            else begin 
                cnt_init <= cnt_init + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_init = cnt_init <= `INIT_TIME;
    assign end_cnt_init = add_cnt_init && cnt_init == `INIT_TIME + 1'b1; 

    // waite_done:上电等待200us结束标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            waite_done <= 1'b0;
        end 
        else if(cnt_init == `INIT_TIME - 1'b1)begin 
            waite_done <= 1'b1;
        end 
        else begin 
            waite_done <= 1'b0;
        end 
    end

    // cnt_aref:自动刷新7us计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_aref <= 10'b0;
        end 
        else if(add_cnt_aref)begin 
            if(end_cnt_aref)begin 
                cnt_aref <= 10'b0;
            end
            else begin 
                cnt_aref <= cnt_aref + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_aref = init_done;
    assign end_cnt_aref = add_cnt_aref && cnt_aref == `AREF_TIME - 1'b1;

    // cnt_4clk:四周期时钟计数器
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_4clk <= 2'b0;
        end 
        else if(add_cnt_4clk)begin 
            cnt_4clk <= cnt_4clk + 1'b1;
        end
    end 

    assign add_cnt_4clk = (state_c == PRE) || (state_c == REGIST) || (state_c == W_ACT)
    || (state_c == R_ACT) || (state_c == BURST);

    // cnt_7clk:七周期时钟计数器
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_7clk <= 3'b0;
        end 
        else if(add_cnt_7clk)begin 
            cnt_7clk <= cnt_7clk + 1'b1;
        end
    end 
    
    assign add_cnt_7clk = (state_c == AREF);

    // atref_time:自动刷新次数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            atref_time <= 2'b0;
        end 
        else if(cnt_7clk == 3'd7)begin 
            atref_time <= atref_time + 1'b1;
        end 
    end

    // init_done:SDRAM初始化结束
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            init_done <= 1'b0;
        end 
        else if(regist2idle)begin 
            init_done <= 1'b1;
        end 
    end

    // cnt_bit:读写计数
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cnt_bit <= 10'b0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 10'b0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = (state_c == WRITE) || (state_c == READ);
    assign end_cnt_bit = add_cnt_bit && (((state_c == WRITE) && 
    (cnt_bit == `BURST_LEN - 1'b1)) || ((state_c == READ) && (cnt_bit == `BURST_LEN - 1'b1)));

    // 写操作标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            wr <= 1'b0;
        end 
        else if(write2burst)begin 
            wr <= 1'b1;
        end 
        else if(pre2idle)begin 
            wr <= 1'b0;
        end 
    end

    // 读操作标志
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rd <= 1'b0;
        end 
        else if(read2burst)begin 
            rd <= 1'b1;
        end 
        else if(pre2idle)begin 
            rd <= 1'b0;
        end 
    end

    // aref_req:自动刷新请求
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            aref_req <= 1'b0;
        end 
        else if(end_cnt_aref)begin 
            aref_req <= 1'b1;
        end 
        else if(aref2idle)begin 
            aref_req <= 1'b0;
        end 
    end

    // 状态转移
    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    :	if(idle2pre)begin
                            state_n = PRE;
                        end
                        else if(idle2w_act)begin
                            state_n = W_ACT;
                        end
                        else if(idle2r_act)begin
                            state_n = R_ACT;
                        end
                        else begin
                            state_n = state_c;
                        end
            PRE     :	if(pre2idle)begin
                            state_n = IDLE;
                        end
                        else if(pre2aref)begin
                            state_n = AREF;
                        end
                        else begin
                            state_n = state_c;
                        end
            AREF    :	if(aref2idle)begin
                            state_n = IDLE;
                        end
                        else if(aref2regist)begin
                            state_n = REGIST;
                        end
                        else begin
                            state_n = state_c;
                        end
            REGIST  :	if(regist2idle)begin
                            state_n = IDLE;
                        end
                        else begin
                            state_n = state_c;
                        end
            W_ACT   :	if(w_act2write)begin
                            state_n = WRITE;
                        end
                        else begin
                            state_n = state_c;
                        end
            R_ACT   :	if(r_act2read)begin
                            state_n = READ;
                        end
                        else begin
                            state_n = state_c;
                        end
            WRITE   :	if(write2burst)begin
                            state_n = BURST;
                        end
                        else begin
                            state_n = state_c;
                        end
            READ    :	if(read2burst)begin
                            state_n = BURST;
                        end
                        else begin
                            state_n = state_c;
                        end
            BURST   :	if(burst2pre)begin
                            state_n = PRE;
                        end
                        else begin
                            state_n = state_c;
                        end
            default:	state_n = state_c;
        endcase
    end
            
    // 状态转移条件赋值
    assign    idle2pre    =   (state_c == IDLE  ) && (waite_done || aref_req) ;//仲裁,进入自动刷新条件
    assign    pre2aref    =   (state_c == PRE   ) && (cnt_4clk == 2'd3) ;
    assign    aref2regist =   (state_c == AREF  ) && (cnt_7clk == 3'd7) && (atref_time == 2'd3) ;
    assign    regist2idle =   (state_c == REGIST) && (cnt_4clk == 2'd3) ;
    assign    aref2idle   =   (state_c == AREF  ) && (cnt_7clk == 3'd7) && (atref_time == 2'd3) && init_done ;
    assign    idle2w_act  =   (state_c == IDLE  ) && !aref_req && w_en && init_done ;//仲裁,进入写操作条件
    assign    idle2r_act  =   (state_c == IDLE  ) && !aref_req && r_en && init_done ;//仲裁,进入读操作条件
    assign    w_act2write =   (state_c == W_ACT ) && (cnt_4clk == 2'd3) ;
    assign    write2burst =   (state_c == WRITE ) && end_cnt_bit ;
    assign    r_act2read  =   (state_c == R_ACT ) && (cnt_4clk == 2'd3) ;
    assign    read2burst  =   (state_c == READ  ) && end_cnt_bit ;
    assign    burst2pre   =   (state_c == BURST ) && (cnt_4clk == 2'd3) ;
    assign    pre2idle    =   (state_c == PRE   ) && (cnt_4clk == 2'd3) && (wr || rd) ;

    always @(*)begin 
        if(!rst_n)begin
            cmd = `CMD_NOOP;
            sdram_addr = 13'b0;
            sdram_ba = 2'b0;
        end 
        else begin 
            case (state_c)
                PRE     :   if(cnt_4clk == 1'b0)begin
                                cmd = `CMD_PRE;
                                sdram_addr = 13'b001_0_00_000_0_000;//选中所有bank进行预充电
                                sdram_ba = 2'b00;
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                AREF    :   if(cnt_7clk == 1'b0)begin
                                cmd = `CMD_AREF;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                REGIST  :   if(cnt_4clk == 1'b0)begin
                                cmd = `CMD_MOD;
                                sdram_addr = 13'b000_0_00_010_0_111;//配置模式寄存器
                                sdram_ba = 2'b0;
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                W_ACT   :   if(cnt_4clk == 1'b0)begin
                                cmd = `CMD_ACT;
                                sdram_addr = w_addr[21:9];
                                sdram_ba = w_addr[23:22];
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                R_ACT   :   if(cnt_4clk == 1'b0)begin
                                cmd = `CMD_ACT;
                                sdram_addr = r_addr[21:9];
                                sdram_ba = r_addr[23:22];
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                WRITE   :   if(cnt_bit == 1'b0)begin
                                cmd = `CMD_WR;
                                sdram_addr = {4'b0000, w_addr[8:0]};
                                sdram_ba = w_addr[23:22];
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                READ    :   if(cnt_bit == 1'b0)begin
                                cmd = `CMD_RD;
                                sdram_addr = {4'b0000, r_addr[8:0]};
                                sdram_ba = r_addr[23:22];
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                BURST   :   if(cnt_4clk == 1'b0)begin
                                cmd = `CMD_BR;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                            else begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
                default :   begin
                                cmd = `CMD_NOOP;
                                sdram_addr = 13'b0;
                                sdram_ba = 2'b0;
                            end
            endcase
        end 
    end

    // wr_ack:写响应  
    assign wr_ack = (state_c == WRITE );

    // rd_ack:读响应     
    assign rd_ack = (state_c == READ ); 

    // r_ack_r:读响应打两拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            r_ack_r <= 3'b0;
        end 
        else begin 
            r_ack_r <= {r_ack_r[1:0], rd_ack};
        end 
    end 

    // r_ack:输出的读响应
    assign  r_ack = r_ack_r[2];

    // w_ack:写响应打第一拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            w_ack <= 1'b0;
        end 
        else begin 
            w_ack <=  wr_ack;
        end 
    end  

    // ack_r1:写响应打第二拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            ack_r1 <= 1'b0;
        end 
        else begin 
            ack_r1 <= w_ack;
        end 
    end

    // ack_r2:输出的读响应打拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            ack_r2 <= 1'b0;
        end 
        else begin 
            ack_r2 <= r_ack;
        end 
    end

    // 读写操作完成标志
    assign w_done = ack_r1 & ~w_ack;
    assign r_done = ack_r2 & ~r_ack;

    // 数据:sdram_dq
    assign sdram_dq = wr_ack?w_data:16'bz;

    // r_data:sdram读出数据
    assign r_data = r_ack?dq_r:16'd0;

endmodule

5、乒乓缓存模块

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:数据缓存模块		
*********************************************************************************/

`include "param.v"

module data_cache( 
    input				  clk		  ,//100MHz工作时钟
    input				  rst_n	      ,//复位信号
    input                 w_done      ,//写完成
    input                 r_done      ,//读完成
    input                 init_done   ,//sdram初始化结束
    input       [15:0]    r_data      ,//sdram数据接收fifo,输入数据
    input                 r_wrclk     ,//sdram数据接收fifo,写时钟
    input                 r_wrreq     ,//sdram数据接收fifo,写请求
    input       [15:0]    t_data      ,//sdram数据发送fifo,输入数据
    input                 t_rdclk     ,//sdram数据发送fifo,读时钟
    input                 t_rdreq     ,//sdram数据发送fifo,读请求   
    input                 w_ack       ,//写响应
    input                 r_ack       ,//读响应

    output      [15:0]	  r_q	      ,//sdram数据接收fifo,输出数据
    output reg  [23:0]    w_addr      ,//sdram写地址
    output reg  [23:0]    r_addr      ,//sdram读地址
    output      [15:0]	  t_q	      ,//sdram数据发送fifo,输出数据
    output reg            sdram_rdreq ,//sdram数据接收fifo,读请求
    output reg            sdram_wrreq  //sdram数据发送fifo,写请求
);					                  
    //中间信号定义		
    wire        [9:0]     r_rdusedw   ;//sdram数据接收fifo,读数据计数
    wire        [9:0]     t_wrusedw   ;//sdram数据发送fifo,写数据计数
    reg                   bank_flag   ;//bank切换标志
    reg                   bank_en     ;//bank切换使能

    // bank_flag、bank_en、w_addr:bank切换标志、bank切换使能和写地址
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            bank_en <= 1'b0;
            bank_flag <= 1'b0;
            w_addr <= 24'b0;
        end 
        else if(w_done)begin 
            if(w_addr[22:0] >= `ADDR_END)begin
                bank_en <= 1'b1;
                bank_flag <= ~bank_flag;
            end
            else begin
                w_addr <= w_addr + `BURST_LEN;
            end
        end 
        else if(bank_en)begin
            bank_en <= 1'b0;
            if(bank_flag)begin
                w_addr <= {1'b1, 23'b0};
            end
            else begin
                w_addr <= {1'b0, 23'b0};
            end
        end
    end

    //r_addr:读地址
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            r_addr <= 24'b0;
        end 
        else if(r_done)begin 
            if(r_addr[22:0] >= `ADDR_END)begin
                if(!bank_flag)begin
                    r_addr <= {1'b1, 23'b0};
                end
                else begin
                    r_addr <= {1'b0, 23'b0};
                end
            end
            else begin
                r_addr <= r_addr + `BURST_LEN;
            end
        end 
    end

    //sdram_rdreq、sdram_wrreq:sdram读写请求
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            sdram_rdreq <= 1'b0;
            sdram_wrreq <= 1'b0;
        end 
        else if(init_done)begin 
            if(r_rdusedw >= `BURST_LEN)begin
                sdram_rdreq <= 1'b0;
                sdram_wrreq <= 1'b1;
            end
            else if(t_wrusedw < `BURST_LEN)begin
                sdram_rdreq <= 1'b1;
                sdram_wrreq <= 1'b0;
            end
            else begin
                sdram_rdreq <= 1'b0;
                sdram_wrreq <= 1'b0;
            end
        end 
        else begin 
            sdram_rdreq <= 1'b0;
            sdram_wrreq <= 1'b0;
        end 
    end

    fifo_r u_fifo_r(
        .aclr    (  ~rst_n  ),
        .data    (r_data    ),
        .rdclk   (clk       ),//sdram数据接收fifo,读时钟
        .rdreq   (w_ack     ),
        .wrclk   (r_wrclk   ),//sdram数据接收fifo,写时钟
        .wrreq   (r_wrreq   ),
        .q       (r_q       ),
        .rdusedw (r_rdusedw )
    );

    fifo_t u_fifo_t(
    	.aclr    (  ~rst_n  ),
        .data    (t_data    ),
        .rdclk   (t_rdclk   ),//sdram数据发送fifo,读时钟
        .rdreq   (t_rdreq   ),
        .wrclk   (clk       ),//sdram数据发送fifo,写时钟
        .wrreq   (r_ack     ),
        .q       (t_q       ),
        .wrusedw (t_wrusedw )
    );
                 
endmodule

6、VGA驱动模块

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:VGA驱动模块1024*768		
*********************************************************************************/

`include "param.v"

module vga( 
    input				clk		    ,//65Mhz
    input				rst_n	    ,//复位信号
    input		[15:0]	din		    ,//vga输入数据

    output              vga_out_hs  ,//行同步
    output		        vga_out_vs	,//场同步
    output		[15:0]	vga_dout	,//rgb像素数据
    output              data_req     //像素数据请求信号
);								                    
    //中间信号定义	
    wire                data_valid  ;//输入像素有效标志	 
    reg		    [10:0]	cnt_h       ;//行同步计数器
    wire	            add_cnt_h   ;
    wire	            end_cnt_h   ;
    reg		    [10:0]	cnt_v       ;//场同步计数器
    wire	            add_cnt_v   ;
    wire	            end_cnt_v   ;

    //cnt_h:行同步计数
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_h <= 11'b0;
        end 
        else if(add_cnt_h)begin 
            if(end_cnt_h)begin 
                cnt_h <= 11'b0;
            end
            else begin 
                cnt_h <= cnt_h + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_h = 1'b1;
    assign end_cnt_h = add_cnt_h && cnt_h == `H_TO - 1'b1;

    //cnt_v:场同步计数
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_v <= 11'b0;
        end 
        else if(add_cnt_v)begin 
            if(end_cnt_v)begin 
                cnt_v <= 11'b0;
            end
            else begin 
                cnt_v <= cnt_v + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_v = end_cnt_h;
    assign end_cnt_v = add_cnt_v && cnt_v == `V_TO - 1'b1;

    //vga_out_hs:行同步
    assign vga_out_hs = (cnt_h <= `H_SYNC - 1'b1) ;

    //vga_out_vs:场同步
    assign vga_out_vs = (cnt_v <= `V_SYNC - 1'b1) ;

    //data_req:像素数据请求信号 
    assign data_req = (`H_SYNC + `H_BP - 2'd2 < cnt_h) && (cnt_h <= `H_SYNC + `H_BP + `H_ACTIVE - 2'd2) &&
     (`V_SYNC + `V_BP < cnt_v) && (cnt_v <= `V_SYNC + `V_BP + `V_ACTIVE);

    //data_valid
    assign data_valid = (`H_SYNC + `H_BP < cnt_h) && (cnt_h <= `H_SYNC + `H_BP + `H_ACTIVE) &&
     (`V_SYNC + `V_BP < cnt_v) && (cnt_v <= `V_SYNC + `V_BP + `V_ACTIVE);
    
    //vga_dout:rgb像素数据
    assign vga_dout = data_valid ? din : 16'b0;
                        
endmodule

7、顶层模块

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:顶层模块		
*********************************************************************************/

module img_pro( 
    input				clk		    ,//系统时钟50Mhz
    input				rst_n	    ,//复位信号,低电平有效
    input   [7:0]       cmos_db     ,//摄像头采集数据
    input               cmos_pclk   ,//摄像头pclk时钟48MHz
    input               cmos_vsync  ,//摄像头场同步信号
    input               cmos_href   ,//摄像头行有效信号

    output              cmos_rst_n  ,//摄像头复位信号
    output              cmos_xclk   ,//摄像头xclk时钟24Mhz
    output              cmos_pwdn   ,//摄像头掉电使能信号
    output		    	cmos_scl	,//摄像头配置时钟信号
    inout		    	cmos_sda	,//摄像头配置数据信号
    output              sdram_clk   ,//sdram工作时钟
    output              sdram_cke   ,//sdram使能
    output  [12:0]      sdram_addr  ,//sdram地址总线
    output  [1:0]       sdram_ba    ,//sdram bank地址
    inout   [15:0]      sdram_dq    ,//sdram数据总线
    output  [1:0]       sdram_dqm   ,//数据掩码
    output  [3:0]       cmd         ,//sdram操作命令
    output              vga_out_hs  ,//vga行同步信号
    output              vga_out_vs  ,//vga场同步信号
    output  [15:0]      vga_dout     //vga输出的RGB数据

);								                 
    //中间信号定义		 
    wire                vga_clk     ;//vga时钟
    wire                clk_100MHz  ;//100MHz工作时钟
    wire                done        ;//摄像头配置完成标志
    wire    [15:0]      pixel_dout  ;//采集的像素数据
    wire                dout_valid  ;//采集数据有效标志
    wire    [15:0]      w_data      ;//sdram写入数据
    wire    [15:0]      r_data      ;//sdram读出数据
    wire    [15:0]      vga_din     ;//vga输入数据
    wire                data_req    ;//vga数据请求
    wire    [23:0]      w_addr      ;//sdram写地址
    wire    [23:0]      r_addr      ;//sdram读地址
    wire                sdram_rdreq ;//sdram数据接收fifo,读请求
    wire                sdram_wrreq ;//sdram数据发送fifo,写请求
    wire                r_ack       ;//读响应
    wire                wr_ack      ;//写响应
    wire                locked_1    ;//pll_1输出稳定标志
    wire                locked_2    ;//pll_2输出稳定标志
    wire                reset       ;//sccb、data_cache、sdram和vga模块复位信号
    wire                init_done   ;//sdram初始化结束
    wire                pixel_rst   ;//pixel_sampling模块复位信号
    wire                w_done      ;//写完成标志
    wire                r_done      ;//读完成标志

    //sdram_cke:sdram使能
    assign sdram_cke = 1'b1;

    //sdram_dqm:数据掩码
    assign sdram_dqm = 2'b00;

    //reset:sccb、data_cache、sdram和vga模块复位信号
    assign reset = locked_1 & locked_2 & rst_n;

    //pixel_rst:pixel_sampling模块复位信号
    assign pixel_rst = done & init_done & reset;

    sccb u_sccb(
    	.clk          (clk           ),
        .rst_n        (reset         ),
        .done         (done          ),
        .cmos_rst_n   (cmos_rst_n    ),
        .cmos_pwdn    (cmos_pwdn     ),
        .cmos_scl     (cmos_scl      ),
        .cmos_sda     (cmos_sda      )
    );
    
    pixel_sampling u_pixel_sampling(
    	.clk          (cmos_pclk     ),//摄像头pclk时钟48MHz
        .rst_n        (pixel_rst     ),//pixel_sampling模块复位信号
        .din          (cmos_db       ),//摄像头采集数据
        .vsync        (cmos_vsync    ),//摄像头场同步信号
        .href         (cmos_href     ),//摄像头行有效信号
        .dout_valid   (dout_valid    ),//输出数据有效标志
        .dout         (pixel_dout    ) //输出拼接完成的像素
    );

    data_cache u_data_cache(
    	.clk          (clk_100MHz    ),
        .rst_n        (reset         ),
        .w_done       (w_done        ),
        .r_done       (r_done        ),
        .init_done    (init_done     ),
        .r_data       (pixel_dout    ),
        .r_wrclk      (cmos_pclk     ),
        .r_wrreq      (dout_valid    ),
        .t_data       (r_data        ),
        .t_rdclk      (vga_clk       ),
        .t_rdreq      (data_req      ),
        .w_ack        (wr_ack        ),
        .r_ack        (r_ack         ),
        .r_q          (w_data        ),
        .w_addr       (w_addr        ),
        .r_addr       (r_addr        ),
        .t_q          (vga_din       ),
        .sdram_rdreq  (sdram_rdreq   ),//sdram数据接收fifo,读请求
        .sdram_wrreq  (sdram_wrreq   ) //sdram数据发送fifo,写请求
    );
    
    sdram u_sdram(
    	.clk          (clk_100MHz    ),
        .rst_n        (reset         ),
        .w_req        (sdram_wrreq   ),
        .r_req        (sdram_rdreq   ),
        .w_addr       (w_addr        ),
        .r_addr       (r_addr        ),
        .w_data       (w_data        ),
        .r_data       (r_data        ),
        .sdram_addr   (sdram_addr    ),
        .sdram_ba     (sdram_ba      ),
        .sdram_dq     (sdram_dq      ),
        .cmd          (cmd           ),
        .init_done    (init_done     ),
        .r_ack        (r_ack         ),
        .wr_ack       (wr_ack        ),
        .w_done       (w_done        ),//写操作完成标志
        .r_done       (r_done        ) //读操作完成标志
    );

    vga u_vga(
    	.clk          (vga_clk       ),
        .rst_n        (reset         ),
        .din          (vga_din       ),
        .vga_out_hs   (vga_out_hs    ),
        .vga_out_vs   (vga_out_vs    ),
        .vga_dout     (vga_dout      ),
        .data_req     (data_req      )
    );
     
    pll_1	pll_1_inst (
        .areset       (~rst_n        ),
        .inclk0       (clk           ),//50MHz
        .c0           (vga_clk       ),//65MHz
        .c1           (clk_100MHz    ),//100MHz
        .c2           (sdram_clk     ),//100MHz,-75deg
        .locked       (locked_1      )
	);

    pll_2	pll_2_inst (
        .areset       (~rst_n        ),
        .inclk0       (clk           ),//50MHz系统时钟
        .c0           (cmos_xclk     ),//24Mhz
        .locked       (locked_2      )
	);
                        
endmodule

8、参数定义

仿真时可以修改参数节省仿真时间

/**************************************功能介绍***********************************
Copyright:			
Date     :			
Author   :厉长川			
Version  :2022.10.10 v1			
Description:参数定义		
*********************************************************************************/

//sccb参数定义
`define     CNT_1M      6'd50                                   //1M时钟计数
`define     CNT_MS      21'd1315000                             //26.3ms时间计数
`define     PWDN        18'd255000                              //5.1ms计时
`define     RST_CMOS    19'd310000                              //1.1ms复位等待时间
`define     NUM_CFG     8'd248                                  //寄存器配置个数
		
//pixel_sampling参数定义		
`define     PIXEL       4'd10                                   //舍弃帧数

//vga接口参数		
// 1024*768  65Mhz
`define     H_ACTIVE    11'd1024                                //行有效 
`define     H_FP        11'd24                                  //行前沿   
`define     H_SYNC      11'd136                                 //行同步
`define     H_BP        11'd160                                 //行后沿  
`define     H_TO        11'd1344                                //行周期
`define     V_ACTIVE    11'd768                                 //场有效  
`define     V_FP        11'd3                                   //场前沿    
`define     V_SYNC      11'd6                                   //场同步 
`define     V_BP        11'd29                                  //场后沿  
`define     V_TO        11'd806                                 //场周期 
	
//sdram模块参数		
`define     CMD_NOOP    4'b0111                                 //空指令
`define     CMD_ACT     4'b0011                                 //行激活指令
`define     CMD_RD      4'b0101                                 //读指令
`define     CMD_WR      4'b0100                                 //写指令
`define     CMD_BR      4'b0110                                 //突发终止指令
`define     CMD_PRE	    4'b0010                                 //预充电指令
`define     CMD_AREF	4'b0001                                 //自动刷新指令
`define     CMD_MOD     4'b0000                                 //模式寄存器配置命令
`define     INIT_TIME   14'd10000                               //上电等待时间,10000个时钟周期(100us)
`define     AREF_TIME   10'd700                                 //自动刷新间隔时间,700个时钟周期(7us)
`define     BURST_LEN   10'd512                                 //读写突发长度
`define     ADDR_END    `H_ACTIVE * `V_ACTIVE - `BURST_LEN      //读写地址末地址

9、最终效果

①:分析综合

在这里插入图片描述

②:上板验证

在这里插入图片描述
在这里插入图片描述

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值