发 帖  
原厂入驻New
[经验]

【每周FPGA案例】OV7670摄像头显示

2020-11-5 15:19:14  335 FPGA OV7670 图像传感器
分享
5
第1节 OV7670摄像头显示
--作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处

1.1 总体设计1.1.1 概述
OV7670是一种图像传感器,图像传感器,体积小,工作电压低,提供单片VGA摄像头影像处理器的所有功能。通过SCCB总线控制,可以输入整帧、子采样、取窗口等方式的各种分辨率8位影像数据。该产品VGA图像最高达到30/秒。用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、饱和度、色度等都可以通过SCCB接口编程。OmmiVision图像传感器应用独有的传感器技术,通过减少或消除光学电子缺陷如固定图案噪声、托尾、浮散等,提高图像质量,得到清晰的稳定的彩色图像。

1.1.2 设计目标
本工程使用ov7670摄像头、SDRAMVGA显示器、按键等,实现摄像头显示的功能,具体要求如下:
1、  通过SCCB接口对摄像头中的寄存器进行配置。
2、  通过按键/矩阵键盘控制是否对摄像头进行配置。
3、  对摄像头输出的图像数据进行采集之后,通过SDRAM进行缓存
4、  通过VGA显示器显示摄像头采集到的图像。

1.1.3 系统结构框图
系统结构框图如下图一所示:

图一
1.1.4模块功能
    按键检测模块实现功能
1、  将外来异步信号打两拍处理,将异步信号同步化。
2、  实现20ms按键消抖功能。
3、  实现矩阵键盘或者普通案件的检测功能,并输出有效按键信号。
  
   锁相环PLL_1
1、  将摄像头输出的像素时钟转化为同频同相的25M时钟。


   锁相环
PLL_2

1、产生摄像头需要的25M时钟xlclk
2、  产生SDRAM工作需要的100M时钟。


  配置模块模块实现功能
1、  根据按键信息,将配置表中的寄存器信息读出。
2、  根据SCCB接口模块的指示,将配置信息输出到SCCB模块。

  SCCB接口模块实现功能
1、  根据上游模块的读写使能命令、地址以及数据,产生对应的SCCB时序,从而将数据写入摄像头指定的寄存器中,或者从指定的摄像头寄存器中读取数据。


  图像采集模块实现功能
1、  根据摄像头输出格式,对像素数据进行接收。
2、  对摄像头输出数据进行串并转换。


  存储控制模块实现功能
1、  通过乒乓操作的方法,控制SDRAM的读写以及Bank地址,以保证图像数据连续显示到显示器上。

  SDRAM接口模块实现功能
1、  接收上游模块发送的读/写请求、Bank地址、行地址和写数据,产生SDRAM的控制时序。

  VGA接口模块实现功能
1、  产生VGA时序,在有效显示区域,将图像数据送入显示器进行显示。

1.1.5顶层信号
  
信号名
  
位宽
定义
clk
I
1
系统工作时钟 50M
rst_n
I
1
系统复位信号,低电平有效
Key_in
I
4
4位按键信号,开发板按键为矩阵键盘时,不需要该信号
Key_col
I
4
4位矩阵键盘列信号,默认高电平,开发板按键为普通按键时,不需要该信号
Key_row
O
4
4位矩阵键盘行信号,默认低电平,开发板按键为普通按键时,不需要该信号
dq
I/O
16
SDRAM数据总线,既能作为数据输出,也能作为数据输入。
cke
O
1
SDRAM时钟使能信号,决定是否启用clk输入,为高电平时,时钟有效。
cs
O
1
SDRAM片选信号,决定设备内是否启用命令输入,当cs为低时启用,当cs为高时禁用命令输入。
ras
O
1
行地址选通信号,低电平有效
cas
O
1
列地址选通信号,低电平有效
we
O
1
写使能信号,低电平有效
dqm
O
2
数据掩码,控制I/O的高低字节,低电平有效。例如:2’b10,表示数据高字节无效,低字节有效。
sd_addr
O
13
SDRAM地址信号。
sd_bank
O
2
Bank地址选择信号,通过该信号决定哪个Bank正处于激活、读、写、预充电等命令期间。
sd_clk
O
1
SDRAM输入时钟,除cke外,SDRAM的所有输入与该引脚的上升沿同步获得。
Pclk
I
1
摄像头输出像素时钟
vsync
I
1
图像场同步信号
href
I
1
图像行同步信号
din
I
8
摄像头输出的数据
Xclk
O
1
摄像头驱动时钟,25M
Pwdn
O
1
摄像头待机状态指示信号,该信号为1表示,摄像头处于待机状态,不工作,该信号为0,摄像头处于唤醒状态。
Sio_c
O
1
SCCB时钟
Sio_d
I/O
1
SCCB数据线,既能作为输入也能作为输出
Vga_hys
O
1
VGA行同步信号
Vga_vys
O
1
VGA场同步信号
Vga_rgb
O
16
VGA图像数据

1.1.6参考代码
下面是使用工程的顶层代码:
  1. module mdyOV7670Cameradisplay_top(
  2.     clk         ,
  3.     rst_n       ,
  4.     key_in      ,
  5.     pclk        ,
  6.     vsync       ,
  7.     href        ,
  8.     din         ,
  9.     xclk        ,
  10.     pwdn        ,
  11.     sio_c       ,
  12.     sio_d       ,
  13.     vga_hys     ,
  14.     vga_vys     ,
  15.     vga_rgb     ,
  16.     cke         ,
  17.     cs          ,
  18.     ras         ,
  19.     cas         ,
  20.     we          ,
  21.     dqm         ,
  22.     sd_addr     ,
  23.     sd_bank     ,
  24.     sd_clk      ,
  25.     dq
  26. );

  27.     input               clk           ;
  28.     input               rst_n         ;
  29.     input               pclk          ;
  30.     input  [3:0]        key_in        ;
  31.     input               vsync         ;
  32.     input               href          ;
  33.     input  [7:0]        din           ;

  34.     output              xclk          ;
  35.     output              pwdn          ;
  36.     output              vga_hys       ;
  37.     output              vga_vys       ;
  38.     output [15:0]       vga_rgb       ;
  39.     output              sio_c         ;
  40.     output              cs            ;
  41.     output              ras           ;
  42.     output              cas           ;
  43.     output              we            ;
  44.     output [1 :0]       dqm           ;
  45.     output [12:0]       sd_addr       ;
  46.     output [1 :0]       sd_bank       ;
  47.     output              sd_clk        ;
  48.         output              cke           ;   
  49.         
  50.   
  51.          
  52.     inout  [15:0]       dq            ;
  53.         inout               sio_d         ;
  54.     wire   [15:0]       dq_in         ;
  55.     wire   [15:0]       dq_out        ;
  56.     wire                dq_out_en     ;
  57.     wire                en_sio_d_w    ;
  58.     wire                sio_d_w       ;
  59.     wire                sio_d_r       ;
  60.     wire                xclk_         ;
  61.     wire                clk_100m      ;
  62.     wire                locked        ;
  63.     wire   [3:0]        key_num       ;
  64.     wire                en_coms       ;
  65.     wire   [7:0]        value_gray    ;
  66.     wire                rdy           ;
  67.     wire                wen           ;
  68.     wire                ren           ;
  69.     wire   [7:0]        wdata         ;
  70.     wire                capture_en    ;
  71.     wire   [7:0]        rdata         ;
  72.     wire                rdata_vld     ;
  73.     wire   [15:0]       cmos_dout     ;
  74.     wire                cmos_dout_vld ;
  75.     wire                cmos_dout_sop ;
  76.     wire                cmos_dout_eop ;
  77.     wire   [15:0]       rd_addr       ;
  78.     wire                rd_en         ;
  79.     wire   [15:0]       vga_data      ;
  80.     wire                rd_end        ;
  81.     wire                wr_end        ;
  82.     wire                rd_addr_sel   ;
  83.     wire   [3:0]        key_vld       ;
  84.     wire                display_area  ;
  85.         wire   [7:0]        sub_addr      ;         
  86.     wire                cke           ;
  87.     wire                cs            ;
  88.     wire                ras           ;
  89.     wire                cas           ;
  90.     wire                we            ;
  91.     wire   [1 :0]       dqm           ;
  92.     wire   [12:0]       sd_addr       ;
  93.     wire   [1 :0]       sd_bank       ;
  94.     wire                sd_clk        ;
  95.     wire   [15:0]       sd_rdata      ;
  96.     wire                sd_rdata_vld  ;
  97.     wire   [15:0]       fifo2sd_wdata ;
  98.     wire                wr_ack        ;
  99.     wire                rd_ack        ;
  100.     wire                wr_req        ;
  101.     wire                rd_req        ;
  102.         wire   [1 :0]       bank          ;
  103.         wire   [12:0]       addr          ;

  104.     assign  dq_in = dq;
  105.     assign  dq    = dq_out_en?dq_out:16'hzzzz;


  106.     assign sio_d = en_sio_d_w ? sio_d_w : 1'dz;
  107.     assign sio_d_r = sio_d;

  108.    

  109.         
  110.         
  111.     pll_sd  pll_sd_inst2 (
  112.             .inclk0       (clk           ),
  113.             .c0           (xclk          ),
  114.             .c1           (clk_100m      )        
  115.         );


  116.     cmos_pll  u_cmos_pll(

  117.         .inclk0       (pclk         ),
  118.         .c0           (clk_25M       )
  119.         );

  120.    

  121.     key_module#(.KEY_W(4)) u_key_module(
  122.         .clk          (xclk         ),
  123.         .rst_n        (rst_n        ),
  124.         .key_in       (key_in       ),
  125.         .key_vld      (key_vld      )   
  126.     );

  127.     ov7670_config u4(
  128.         .clk          (xclk         ),
  129.         .rst_n        (rst_n        ),
  130.         .config_en    (key_vld[1]   ),
  131.         .rdy          (rdy          ),
  132.         .rdata        (rdata        ),
  133.         .rdata_vld    (rdata_vld    ),
  134.         .wdata        (wdata        ),
  135.         .addr         (sub_addr     ),
  136.         .wr_en        (wen          ),
  137.         .rd_en        (ren          ),
  138.         .cmos_en      (en_capture   ),
  139.         .pwdn         (pwdn         )      
  140.     );

  141.     sccb u5(
  142.         .clk          (xclk         ),
  143.         .rst_n        (rst_n        ),
  144.         .ren          (ren          ),
  145.         .wen          (wen          ),
  146.         .sub_addr     (sub_addr     ),
  147.         .rdata        (rdata        ),
  148.         .rdata_vld    (rdata_vld    ),
  149.         .wdata        (wdata        ),
  150.         .rdy          (rdy          ),
  151.         .sio_c        (sio_c        ),
  152.         .sio_d_r      (sio_d_r      ),
  153.         .en_sio_d_w   (en_sio_d_w   ),
  154.         .sio_d_w      (sio_d_w      )
  155.     );

  156.     cmos_capture u6(
  157.         .clk          (clk_25M       ),
  158.         .rst_n        (rst_n        ),
  159.         .en_capture   (en_capture   ),
  160.         .vsync        (vsync        ),
  161.         .href         (href         ),
  162.         .din          (din          ),
  163.         .dout         (cmos_dout    ),
  164.         .dout_vld     (cmos_dout_vld),
  165.         .dout_sop     (cmos_dout_sop),
  166.         .dout_eop     (cmos_dout_eop)
  167.     );

  168.         
  169.     vga_config u11(
  170.         .clk          (clk_25M      ),
  171.         .clk_in       (clk_100m     ),
  172.         .rst_n        (rst_n        ),
  173.         .din          (cmos_dout    ),
  174.         .din_vld      (cmos_dout_vld),
  175.         .din_sop      (cmos_dout_sop),
  176.         .din_eop      (cmos_dout_eop),
  177.             .dout         (vga_data     ),
  178.             .wr_req       (wr_req       ),
  179.             .rd_req       (rd_req       ),
  180.                 .wr_ack       (wr_ack       ),
  181.         .rd_ack       (rd_ack       ),
  182.         .wdata                  (fifo2sd_wdata),         
  183.                 .sd_rdata     (sd_rdata     ),
  184.             .sd_rdata_vld (sd_rdata_vld ),
  185.                 .display_area (display_area ),
  186.         .bank         (bank         ),
  187.                 .addr         (addr         )
  188.     );
  189.          
  190.          
  191.          sdram_intf u20   (           
  192.         .clk          (clk_100m     ),
  193.         .rst_n        (rst_n        ),
  194.         .wr_req       (wr_req       ),
  195.         .rd_req       (rd_req       ),  
  196.         .dq_in        (dq_in        ),
  197.         .dq_out       (dq_out       ),
  198.         .dq_out_en    (dq_out_en    ),
  199.         .wr_ack       (wr_ack       ),
  200.         .rd_ack       (rd_ack       ),
  201.         .rdata        (sd_rdata     ),
  202.         .rdata_vld    (sd_rdata_vld ),
  203.         .cke          (cke          ),
  204.         .cs           (cs           ),
  205.         .ras          (ras          ),
  206.         .cas          (cas          ),
  207.         .we           (we           ),
  208.         .dqm          (dqm          ),
  209.         .sd_addr      (sd_addr      ),
  210.         .sd_bank      (sd_bank      ),
  211.         .sd_clk       (sd_clk       ),
  212.                 .wdata        (fifo2sd_wdata),
  213.                 .bank         (bank         ),
  214.                 .addr         (addr         )
  215.                   
  216.          );
  217.          
  218.          

  219.     vga_driver u12(
  220.         .clk         (clk_25M       ),
  221.         .rst_n       (rst_n         ),
  222.         .din         (vga_data      ),
  223.         .vga_hys     (vga_hys       ),
  224.         .vga_vys     (vga_vys       ),
  225.         .vga_rgb     (vga_rgb       ),
  226.             .display_area(display_area  )
  227.     );
  228.         


  229. endmodule
复制代码



1.2 按键检测模块设计1.2.1接口信号
下面为使用矩阵键盘时的接口信号:

  
信号
  
接口方向
定义
clk
输入
rst_n
输入
低电平复位信号
key_col
输入
矩阵键盘列输入信号
Key_row
输出
矩阵键盘行输出信号
Key_en
输出
按键按下位置指示信号
下面是使用普通按键时的接口信号:
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
Key_in
输入
按键输入信号
Key_vld
输出
按键按下指示信号

1.2.2 设计思路
在前面的案例中已经有按键检测的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
【每周FPGA案例】至简设计系列_按键控制数字时钟

1.2.3 参考代码
1.  //矩阵键盘
  1. always  @(posedge clk or negedge rst_n)begin
  2.     if(rst_n==1'b0)begin
  3.         key_col_ff0 <= 4'b1111;
  4.         key_col_ff1 <= 4'b1111;
  5.     end
  6.     else begin
  7.         key_col_ff0 <= key_col    ;
  8.         key_col_ff1 <= key_col_ff0;
  9.     end
  10. end


  11. always @(posedge clk or negedge rst_n) begin
  12.     if (rst_n==0) begin
  13.         shake_cnt <= 0;
  14.     end
  15.     else if(add_shake_cnt) begin
  16.         if(end_shake_cnt)
  17.             shake_cnt <= 0;
  18.         else
  19.             shake_cnt <= shake_cnt+1 ;
  20.    end
  21. end
  22. assign add_shake_cnt = key_col_ff1!=4'hf;
  23. assign end_shake_cnt = add_shake_cnt  && shake_cnt == tiME_20MS-1 ;


  24. always  @(posedge clk or negedge rst_n)begin
  25.     if(rst_n==1'b0)begin
  26.         state_c <= CHK_COL;
  27.     end
  28.     else begin
  29.         state_c <= state_n;
  30.     end
  31. end

  32. always  @(*)begin
  33.     case(state_c)
  34.         CHK_COL: begin
  35.                      if(col2row_start )begin
  36.                          state_n = CHK_ROW;
  37.                      end
  38.                      else begin
  39.                          state_n = CHK_COL;
  40.                      end
  41.                  end
  42.         CHK_ROW: begin
  43.                      if(row2del_start)begin
  44.                          state_n = DELAY;
  45.                      end
  46.                      else begin
  47.                          state_n = CHK_ROW;
  48.                      end
  49.                  end
  50.         DELAY :  begin
  51.                      if(del2wait_start)begin
  52.                          state_n = WAIT_END;
  53.                      end
  54.                      else begin
  55.                          state_n = DELAY;
  56.                      end
  57.                  end
  58.         WAIT_END: begin
  59.                      if(wait2col_start)begin
  60.                          state_n = CHK_COL;
  61.                      end
  62.                      else begin
  63.                          state_n = WAIT_END;
  64.                      end
  65.                   end
  66.        default: state_n = CHK_COL;
  67.     endcase
  68. end
  69. assign col2row_start = state_c==CHK_COL  && end_shake_cnt;
  70. assign row2del_start = state_c==CHK_ROW  && row_index==3 && end_row_cnt;
  71. assign del2wait_start= state_c==DELAY    && end_row_cnt;
  72. assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;

  73. always  @(posedge clk or negedge rst_n)begin
  74.     if(rst_n==1'b0)begin
  75.         key_row <= 4'b0;
  76.     end
  77.     else if(state_c==CHK_ROW)begin
  78.         key_row <= ~(1'b1 << row_index);
  79.     end
  80.     else begin
  81.         key_row <= 4'b0;
  82.     end
  83. end





  84. always @(posedge clk or negedge rst_n) begin
  85.     if (rst_n==0) begin
  86.         row_index <= 0;
  87.     end
  88.     else if(add_row_index) begin
  89.         if(end_row_index)
  90.             row_index <= 0;
  91.         else
  92.             row_index <= row_index+1 ;
  93.    end
  94.    else if(state_c!=CHK_ROW)begin
  95.        row_index <= 0;
  96.    end
  97. end
  98. assign add_row_index = state_c==CHK_ROW && end_row_cnt;
  99. assign end_row_index = add_row_index  && row_index == 4-1 ;


  100. always @(posedge clk or negedge rst_n) begin
  101.     if (rst_n==0) begin
  102.         row_cnt <= 0;
  103.     end
  104.     else if(add_row_cnt) begin
  105.         if(end_row_cnt)
  106.             row_cnt <= 0;
  107.         else
  108.             row_cnt <= row_cnt+1 ;
  109.    end
  110. end
  111. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
  112. assign end_row_cnt = add_row_cnt  && row_cnt == 16-1 ;



  113. always  @(posedge clk or negedge rst_n)begin
  114.     if(rst_n==1'b0)begin
  115.         key_col_get <= 0;
  116.     end
  117.     else if(state_c==CHK_COL && end_shake_cnt ) begin
  118.         if(key_col_ff1==4'b1110)
  119.             key_col_get <= 0;
  120.         else if(key_col_ff1==4'b1101)
  121.             key_col_get <= 1;
  122.         else if(key_col_ff1==4'b1011)
  123.             key_col_get <= 2;
  124.         else
  125.             key_col_get <= 3;
  126.     end
  127. end


  128. always  @(posedge clk or negedge rst_n)begin
  129.     if(rst_n==1'b0)begin
  130.         key_out <= 0;
  131.     end
  132.     else if(state_c==CHK_ROW && end_row_cnt)begin
  133.         key_out <= {row_index,key_col_get};
  134.     end
  135.     else begin
  136.         key_out <= 0;
  137.     end
  138. end

  139. always  @(posedge clk or negedge rst_n)begin
  140.     if(rst_n==1'b0)begin
  141.         key_vld <= 1'b0;
  142.     end
  143.     else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin
  144.         key_vld <= 1'b1;
  145.     end
  146.     else begin
  147.         key_vld <= 1'b0;
  148.     end
  149. end


  150. always  @(*)begin
  151.     if(rst_n==1'b0)begin
  152.         key_en = 0;
  153.     end
  154.     else if(key_vld && key_out==0)begin
  155.         key_en = 4'b0001;
  156.     end
  157.     else if(key_vld && key_out==1)begin
  158.         key_en = 4'b0010;
  159.     end
  160.     else if(key_vld && key_out==2)begin
  161.         key_en = 4'b0100;
  162.     end
  163.     else begin
  164.         key_en = 0;
  165.     end
  166. end


  167. endmodule
复制代码



1.3 锁相环1.3.1 接口信号
PLL_1pll_sd
  
信号名
  
I/O
位宽
定义
Inclk0
I
1
输入时钟50M
C0
O
1
输出时钟25M
C1
O
1
输出时钟100M
PLL_2cmos_pll
  
信号名
  
I/O
位宽
定义
Inclk0
I
1
输入时钟25M
C0
O
1
输出时钟25M

1.3.2 设计思路
此模块是使用Quartus生成的PLL IP核,相关的生成步骤、功能原理等可以看明德扬论坛中关于PLL的介绍。
IP核设计(PLL

1.3.3 时钟网络
本工程共有5个时钟,硬件上的晶振提供的50M时钟。由于摄像头的驱动时钟为25M,因此需要由锁相环产生一个25M的时钟xclk,摄像头输出图像数据的同时,会输出一个像素时钟pclk,该时钟是和图像数据对齐的,由于摄像头属于外设,是临时插在开发板上的,因此,插得不牢、晃动、碰撞等,都有可能造成摄像头输出的时钟pclk的不稳定,因此这里将此时钟经过锁相环,产生一个同频同相的稳定时钟clk_25M,作为之后图像处理模块的工作时钟。而SDRAM的工作时钟是100M,因此需要通过锁相环产生100M的时钟。通过架构图中模块的颜色来进行判别,按键检测模块、配置模块和SCCB模块都是采用的xclk图像采集模块、存储控制模块和VGA接口模块都是采用的clk_25MSDRAM接口模块的工作时钟为100M

1.4 配置模块设计
1.4.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
工作时钟 100M
rst_n
I
1
系统复位信号,低电平有效
Config_en
I
1
开始配置指示脉冲
rdy
I
1
下游模块准备好指示信号
rdata
I
8
从摄像头中读取的数据
rdata_vld
I
1
读取数据有效指示信号
Wdata
O
8
将要写到摄像头中的数据
Wr_en
O
2
写使能
Addr
O
8
摄像头寄存器地址信号
rd_en
O
1
读使能
Cmos_en
O
1
配置完成指示信号
Pwdn
O
1
摄像头待机状态指示信号

1.4.2配置表
需要配置的寄存器信息都在配置表文件中保存,通过寄存器计数器reg_cnt进行读取,18Bit位宽的信号add_wdata为保存配置信息的信号,其中add_wdata[17]表示读属性,当其为1时,表示该寄存器可读;daa_wdata[16]表示写属性,当其为1时,表示该寄存器可写;add_wdata[17:8]表示寄存器地址;add_wdata[7:0]表示要往寄存器中写入的值。

寄存器地址表示的寄存器以及数据每一位代表的意思,可以参考ov7670_中文版数据手册

OV7670摄像头模块资料及参考学习资料

举例说明:add_wdata={2’b11,16’h1e31},其中2’b11表示该寄存器可读可写,寄存器地址为8’h1e,查数据手册可知,寄存器名为MVFP,表示水平镜像/竖直翻转使能。数据的位[7:8]作为保留位,没有作用;位[5]表示水平镜像使能,0为正常,1为镜像;位[4]表示竖直翻转使能,0为正常,1为翻转;位[3]保留;位[2]表示消除黑太阳使能;位[10]保留。写数据为8’h31,表示开启水平镜像和竖直翻转。
注意,该文件不可综合,不要添加到编译软件中综合,否则会报错,只需要将此文件放到工程目录下即可。

1.4.3参考代码
  1. parameter      REG_NUM =       164;
  2. always@(*) begin
  3.             case(reg_cnt)
  4.                     0   : add_wdata = {2'b11,16'h1204};        
  5.                 1   : add_wdata = {2'b11,16'h40d0};        
  6.                 2   : add_wdata = {2'b11,16'h3a04};   
  7.                 3   : add_wdata = {2'b11,16'h3dc8};        
  8.                 4   : add_wdata = {2'b11,16'h1e31};        
  9.                 5   : add_wdata = {2'b11,16'h6b00};        
  10.                 6   : add_wdata = {2'b11,16'h32b6};        
  11.                 7   : add_wdata = {2'b11,16'h1713};        
  12.                 8   : add_wdata = {2'b11,16'h1801};        
  13.                 9   : add_wdata = {2'b11,16'h1902};        
  14.                 10  : add_wdata = {2'b11,16'h1a7a};        
  15.                 11  : add_wdata = {2'b11,16'h030a};        
  16.                 12  : add_wdata = {2'b11,16'h0c00};        
  17.                 13  : add_wdata = {2'b11,16'h3e10};        
  18.                 14  : add_wdata = {2'b11,16'h7000};        
  19.                 15  : add_wdata = {2'b11,16'h7100};        
  20.                 16  : add_wdata = {2'b11,16'h7211};                        
  21.                 17  : add_wdata = {2'b11,16'h7300};         
  22.                 18  : add_wdata = {2'b11,16'ha202};        
  23.                 19  : add_wdata = {2'b11,16'h1180};        
  24.                 20  : add_wdata = {2'b11,16'h7a20};
  25.                 21  : add_wdata = {2'b11,16'h7b1c};
  26.                 22  : add_wdata = {2'b11,16'h7c28};
  27.                 23  : add_wdata = {2'b11,16'h7d3c};
  28.                 24  : add_wdata = {2'b11,16'h7e55};
  29.                 25  : add_wdata = {2'b11,16'h7f68};
  30.                 26  : add_wdata = {2'b11,16'h8076};
  31.                 27  : add_wdata = {2'b11,16'h8180};
  32.                 28  : add_wdata = {2'b11,16'h8288};
  33.                 29  : add_wdata = {2'b11,16'h838f};
  34.                 30  : add_wdata = {2'b11,16'h8496};
  35.                 31  : add_wdata = {2'b11,16'h85a3};
  36.                 32  : add_wdata = {2'b11,16'h86af};
  37.                 33  : add_wdata = {2'b11,16'h87c4};
  38.                 34  : add_wdata = {2'b11,16'h88d7};
  39.                 35  : add_wdata = {2'b11,16'h89e8};
  40.                 36  : add_wdata = {2'b11,16'h13e0};
  41.                 37  : add_wdata = {2'b11,16'h0010};
  42.                 38  : add_wdata = {2'b11,16'h1000};
  43.                 39  : add_wdata = {2'b11,16'h0d00};
  44.                 40  : add_wdata = {2'b11,16'h1428};
  45.                 41  : add_wdata = {2'b11,16'ha505};
  46.                 42  : add_wdata = {2'b11,16'hab07};
  47.                 43  : add_wdata = {2'b11,16'h2475};
  48.                 44  : add_wdata = {2'b11,16'h2563};
  49.                 45  : add_wdata = {2'b11,16'h26a5};
  50.                 46  : add_wdata = {2'b11,16'h9f78};
  51.                 47  : add_wdata = {2'b11,16'ha068};
  52.                 48  : add_wdata = {2'b11,16'ha103};
  53.                 49  : add_wdata = {2'b11,16'ha6df};
  54.                 50  : add_wdata = {2'b11,16'ha7df};
  55.                 51  : add_wdata = {2'b11,16'ha8f0};
  56.                 52  : add_wdata = {2'b11,16'ha990};
  57.                 53  : add_wdata = {2'b11,16'haa94};
  58.                 54  : add_wdata = {2'b11,16'h13ef};  
  59.                 55  : add_wdata = {2'b11,16'h0e61};
  60.                 56  : add_wdata = {2'b11,16'h0f4b};
  61.                 57  : add_wdata = {2'b11,16'h1602};
  62.                 58  : add_wdata = {2'b11,16'h2102};
  63.                 59  : add_wdata = {2'b11,16'h2291};
  64.                 60  : add_wdata = {2'b11,16'h2907};
  65.                 61  : add_wdata = {2'b11,16'h330b};
  66.                 62  : add_wdata = {2'b11,16'h350b};
  67.                 63  : add_wdata = {2'b11,16'h371d};
  68.                 64  : add_wdata = {2'b11,16'h3871};
  69.                 65  : add_wdata = {2'b11,16'h392a};
  70.                 66  : add_wdata = {2'b11,16'h3c78};
  71.                 67  : add_wdata = {2'b11,16'h4d40};
  72.                 68  : add_wdata = {2'b11,16'h4e20};
  73.                 69  : add_wdata = {2'b11,16'h6900};
  74.                
  75.                 70  : add_wdata = {2'b11,16'h7419};
  76.                 71  : add_wdata = {2'b11,16'h8d4f};
  77.                 72  : add_wdata = {2'b11,16'h8e00};
  78.                 73  : add_wdata = {2'b11,16'h8f00};
  79.                 74  : add_wdata = {2'b11,16'h9000};
  80.                 75  : add_wdata = {2'b11,16'h9100};
  81.                 76  : add_wdata = {2'b11,16'h9200};
  82.                 77  : add_wdata = {2'b11,16'h9600};
  83.                 78  : add_wdata = {2'b11,16'h9a80};
  84.                 79  : add_wdata = {2'b11,16'hb084};
  85.                 80  : add_wdata = {2'b11,16'hb10c};
  86.                 81  : add_wdata = {2'b11,16'hb20e};
  87.                 82  : add_wdata = {2'b11,16'hb382};
  88.                 83  : add_wdata = {2'b11,16'hb80a};

  89.                 84  : add_wdata = {2'b11,16'h4314};
  90.                 85  : add_wdata = {2'b11,16'h44f0};
  91.                 86  : add_wdata = {2'b11,16'h4534};
  92.                 87  : add_wdata = {2'b11,16'h4658};
  93.                 88  : add_wdata = {2'b11,16'h4728};
  94.                 89  : add_wdata = {2'b11,16'h483a};
  95.                 90  : add_wdata = {2'b11,16'h5988};
  96.                 91  : add_wdata = {2'b11,16'h5a88};
  97.                 92  : add_wdata = {2'b11,16'h5b44};
  98.                 93  : add_wdata = {2'b11,16'h5c67};
  99.                 94  : add_wdata = {2'b11,16'h5d49};
  100.                 95  : add_wdata = {2'b11,16'h5e0e};
  101.                 96  : add_wdata = {2'b11,16'h6404};
  102.                 97  : add_wdata = {2'b11,16'h6520};
  103.                 98  : add_wdata = {2'b11,16'h6605};
  104.                 99  : add_wdata = {2'b11,16'h9404};
  105.                 100 : add_wdata = {2'b11,16'h9508};
  106.                 101 : add_wdata = {2'b11,16'h6c0a};
  107.                 102 : add_wdata = {2'b11,16'h6d55};
  108.                 103 : add_wdata = {2'b11,16'h6e11};
  109.                 104 : add_wdata = {2'b11,16'h6f9f};
  110.                 105 : add_wdata = {2'b11,16'h6a40};
  111.                 106 : add_wdata = {2'b11,16'h0140};
  112.                 107 : add_wdata = {2'b11,16'h0240};
  113.                 108 : add_wdata = {2'b11,16'h13e7};
  114.                 109 : add_wdata = {2'b11,16'h1500};
  115.                
  116.                 110 : add_wdata = {2'b11,16'h4f80};
  117.                 111 : add_wdata = {2'b11,16'h5080};
  118.                 112 : add_wdata = {2'b11,16'h5100};
  119.                 113 : add_wdata = {2'b11,16'h5222};
  120.                 114 : add_wdata = {2'b11,16'h535e};
  121.                 115 : add_wdata = {2'b11,16'h5480};
  122.                 116 : add_wdata = {2'b11,16'h589e};
  123.                
  124.                 117 : add_wdata = {2'b11,16'h4108};
  125.                 118 : add_wdata = {2'b11,16'h3f00};
  126.                 119 : add_wdata = {2'b11,16'h7505};
  127.                 120 : add_wdata = {2'b11,16'h76e1};
  128.                 121 : add_wdata = {2'b11,16'h4c00};
  129.                 122 : add_wdata = {2'b11,16'h7701};
  130.                
  131.                 123 : add_wdata = {2'b11,16'h4b09};
  132.                 124 : add_wdata = {2'b11,16'hc9F0};
  133.                 125 : add_wdata = {2'b11,16'h4138};
  134.                 126 : add_wdata = {2'b11,16'h5640};
  135.                
  136.                
  137.                 127 : add_wdata = {2'b11,16'h3411};
  138.                 128 : add_wdata = {2'b11,16'h3b02};
  139.                 129 : add_wdata = {2'b11,16'ha489};
  140.                 130 : add_wdata = {2'b11,16'h9600};
  141.                 131 : add_wdata = {2'b11,16'h9730};
  142.                 132 : add_wdata = {2'b11,16'h9820};
  143.                 133 : add_wdata = {2'b11,16'h9930};
  144.                 134 : add_wdata = {2'b11,16'h9a84};
  145.                 135 : add_wdata = {2'b11,16'h9b29};
  146.                 136 : add_wdata = {2'b11,16'h9c03};
  147.                 137 : add_wdata = {2'b11,16'h9d4c};
  148.                 138 : add_wdata = {2'b11,16'h9e3f};
  149.                 139 : add_wdata = {2'b11,16'h7804};
  150.                
  151.                
  152.                  140 :add_wdata =  {2'b11,16'h7901};
  153.                  141 :add_wdata =  {2'b11,16'hc8f0};
  154.                  142 :add_wdata =  {2'b11,16'h790f};
  155.                  143 :add_wdata =  {2'b11,16'hc800};
  156.                  144 :add_wdata =  {2'b11,16'h7910};
  157.                  145 :add_wdata =  {2'b11,16'hc87e};
  158.                  146 :add_wdata =  {2'b11,16'h790a};
  159.                  147 :add_wdata =  {2'b11,16'hc880};
  160.                  148 :add_wdata =  {2'b11,16'h790b};
  161.                  149 :add_wdata =  {2'b11,16'hc801};
  162.                  150 :add_wdata =  {2'b11,16'h790c};
  163.                  151 :add_wdata =  {2'b11,16'hc80f};
  164.                  152 :add_wdata =  {2'b11,16'h790d};
  165.                  153 :add_wdata =  {2'b11,16'hc820};
  166.                  154 :add_wdata =  {2'b11,16'h7909};
  167.                  155 :add_wdata =  {2'b11,16'hc880};
  168.                  156 :add_wdata =  {2'b11,16'h7902};
  169.                  157 :add_wdata =  {2'b11,16'hc8c0};
  170.                  158 :add_wdata =  {2'b11,16'h7903};
  171.                  159 :add_wdata =  {2'b11,16'hc840};
  172.                  160 :add_wdata =  {2'b11,16'h7905};
  173.                  161 :add_wdata =  {2'b11,16'hc830};
  174.                  162 :add_wdata =  {2'b11,16'h7926};
  175.                  
  176.                  163 : add_wdata = {2'b11,16'h0903};
  177.                  164 : add_wdata = {2'b11,16'h3b42};
  178.                
  179.             default : add_wdata = 0;
  180.             endcase
  181.         end
复制代码



1.4.4配置模块设计思路
该模块主要功能是将配置表中的数据读出,并对读出的数据进行解析,识别读写属性,根据读写属性,产生读写使能;识别寄存器地址和写数据,并将其分离。
由于配置表文件不可综合,因此无法通过例化获取,可以通过以下语句进行调用:

该模块产生的读使能、写使能、寄存器地址和写数据,都会送给下游SCCB接口模块,而SCCB接口属于单总线接口。不能同时读写,因此,该模块在对配置表进行读取的时候,需要读取两次,先进行写,然后在进行读,该操作由计数器控制,如下图所示:

读写计数器rw_cnt:该计数器表示读写阶段,数第一个表示处于写数据阶段,数第二个表示处于读数据阶段。加一条件为(flag && rdy),表示按下配置开始按键,并且下游模块准备好的时候就加一;结束条件为数两个,只需要区分读阶段和写阶段,因此数两个即可,数完就清零。
待配置的寄存器共有164个,因此需要一个寄存器计数器来对它进行计数,如下图所示:

寄存器计数器reg_cnt:该计数器主要对需要配置的寄存器个数进行计数。加一条件问end_rw_cnt,由于一个寄存器需要读写两个阶段,所以为读写计数器的结束条件;结束条件为数164个,需要配置的寄存器共164个,数完就清零。
下面介绍一下该模块的其他信号设计思路:

配置指示信号flag:该信号初始状态为0,表示还没有开始配置;当接收到开始配置使能信号config_en的时候,变为1,表示开始进入配置状态;当配置完成之后,也就是寄存器计数器数完了,便退出配置状态,因此该信号从高变低的条件为end_reg_cnt
配置完成指示信号cmos_en:当该信号为高电平时,表示摄像头的寄存器已经经过最少一次配置了。初始状态为0,表示没有配置过;变1的条件为end_reg_cnt,也就是寄存器全部配置完一次之后,便置为高电平。

寄存器写数据wdata:初始状态为0,之后,直接取从配置表读出的add_wdata的低8位。

寄存器地址addr:初始状态位0,之后,直接取add_wdata的第8到第15位即可。

写使能wr_en:一个寄存器需要读和写两个阶段,先进行写,然后在读,add_wdata的第16位表示写属性。所以该信号从01的条件为(add_rw_cnt && rw_cnt==0 && add_wdata[16])

读使能rd_en:写之后,在进行读,add_wdata的第17位指示读属性,因此读使能拉高的条件位(add_rw_cnt && rw_cnt==1 && add_wdata[17])

1.4.5参考代码
  1. assign              pwdn = 0;


  2.     `include "ov7670_para.v"
  3.                
  4.                
  5.     always  @(posedge clk or negedge rst_n)begin
  6.         if(rst_n==1'b0)begin
  7.             reg_cnt <= 0;
  8.         end
  9.         else if(add_reg_cnt)begin
  10.             if(end_reg_cnt)
  11.                 reg_cnt <= 0;
  12.             else
  13.                 reg_cnt <= reg_cnt + 1;
  14.         end
  15.     end

  16.     assign add_reg_cnt = end_rw_cnt;   
  17.     assign end_reg_cnt = add_reg_cnt && reg_cnt==REG_NUM-1;

  18.     always  @(posedge clk or negedge rst_n)begin
  19.         if(rst_n==1'b0)begin
  20.             rw_cnt <= 0;
  21.         end
  22.         else if(add_rw_cnt) begin
  23.             if(end_rw_cnt)
  24.                 rw_cnt <= 0;
  25.             else
  26.                 rw_cnt <= rw_cnt + 1;
  27.         end
  28.     end

  29.     assign  add_rw_cnt = flag && rdy;
  30.     assign  end_rw_cnt = add_rw_cnt && rw_cnt==RW_NUM-1;


  31.     always  @(posedge clk or negedge rst_n)begin
  32.         if(rst_n==1'b0)begin
  33.             flag <= 1'b0;
  34.         end
  35.         else if(config_en)begin
  36.             flag <= 1'b1;
  37.         end
  38.         else if(end_reg_cnt)begin
  39.             flag <= 1'b0;
  40.         end
  41.     end

  42.     always  @(posedge clk or negedge rst_n)begin
  43.         if(rst_n==1'b0)begin
  44.             cmos_en <= 1'b0;
  45.         end
  46.         else if(end_reg_cnt)begin
  47.             cmos_en <= 1'b1;
  48.         end
  49.     end


  50.     always  @(posedge clk or negedge rst_n)begin
  51.         if(rst_n==1'b0)begin
  52.             wdata <= 8'b0;
  53.         end
  54.         else begin
  55.             wdata <= add_wdata[7:0];
  56.         end
  57.     end

  58.     always  @(posedge clk or negedge rst_n)begin
  59.         if(rst_n==1'b0)begin
  60.             addr <= 8'b0;
  61.         end
  62.         else begin
  63.             addr <= add_wdata[15:8];
  64.         end
  65.     end


  66.     always  @(posedge clk or negedge rst_n)begin
  67.         if(rst_n==1'b0)begin
  68.             wr_en <= 1'b0;
  69.         end
  70.         else if(add_rw_cnt && rw_cnt==0 && add_wdata[16])begin
  71.             wr_en <= 1'b1;
  72.         end
  73.         else begin
  74.             wr_en <= 1'b0;
  75.         end
  76.     end


  77.     always  @(posedge clk or negedge rst_n)begin
  78.         if(rst_n==1'b0)begin
  79.             rd_en <= 1'b0;
  80.         end
  81.         else if(add_rw_cnt && rw_cnt==1 && add_wdata[17])begin
  82.             rd_en <= 1'b1;
  83.         end
  84.         else begin
  85.             rd_en <= 1'b0;
  86.         end
  87.     end
复制代码




1.5 SCCB接口模块设计


1.5.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
工作时钟 25M
rst_n
I
1
系统复位信号,低电平有效
wren
I
1
写使能
rden
I
1
读使能
Sub_addr
I
8
寄存器地址
rdata
O
8
读寄存器数据
rdata_vld
O
1
读寄存器数据有效指示信号
Wdata
I
8
写寄存器数据
rdy
O
1
准备好接收指示信号
Sio_c
O
1
SCCB接口时钟信号
Sio_d_r
I
1
SCCB接口读数据线
en_sio_d_w
O
1
三态门使能信号
Sio_d_w
O
1
SCCB接口写数据线

1.5.2设计思路
本工程使用的SCCB接口协议,只有两根线,时钟线sio_c和数据线sio_d,因此如果要使用这个接口,我们需要时钟线的频率要求、读数据的时序和写数据的时序。知道了需求,那么在查看数据手册的时候便容易很多。
下图为三线传输开始的标志,本工程中使用的是两线传输,因此可以将下图中的SCCB_E信号忽略,然后可以看出,在sio_c为高的时候,sio_d拉低,表示数据传输的开始,但是在图中看不出sio_d拉低到sio_c拉低的间隔时间是多少。

下图是三线传输停止的标志,本工程使用的是两线传输,因此还是将SCCB_E信号忽略即可,由此得出,在sio_c为高的时候,将sio_d拉高,表示传输的结束,但是下图仍然无法看出从sio_c拉高到sio_d拉高间隔的时间

下图为OV7670数据手册中关于SCCB接口时序的描述,开始条件保持时间为tHD:STA开始条件建立时间为tSU:STO

下表为ov7670摄像头数据手册中关于SCCB时序的一些时间参数,查表得到,tHD:STAtSU:STO最小值为600ns,因此对于这两个参数们可以根据我们使用的方便进行设置。而SCCB的时钟频率,最大值为400K,也可以根据我们的方便进行设置,只要不超过400K就可以了。


经过上面的时序图以及表格的参考,我们基本上可以将sio_c这个信号设计出来了,下面我们在关注sio_d,该信号我们主要关注读数据时序和写数据时序,下图为3相写传输周期,也就是写时序共有三段组成,第一段ID Address为读写属性,第二段Sub-address为寄存器地址,第三段Write Date为写数据,每段都是由9bit数据组成,数据从高到低排列,最低位x为不关心为,下面再使用的时候统一设置为1即可。

下图为读时序,读的时序比较特别,由两相写传输周期和两相读传输周期组成,首先执行写传输周期,该周期会发送读写属性与寄存器地址,目的是为了先识别要读取数据的寄存器,之后执行读传输周期,该周期会发送读写属性,之后开始接收返回的数据即可,8bit的数据接收完成之后,FPGA需要驱动NA位为1,之后读周期结束。



下面开始本模块的设计,首先确定sio_c的频率,这里我将它设置为120个系统时钟周期1sio_c的时钟周期,时钟频率约等于208K。为了方便计算,将开始条件保持时间和开始条件建立时间都设置为1sio_c周期。每个周期发送1bit数据,因此还需要一个计数器来计数每阶段共有多少bit的数据。而读时序的话,由两段组成,因此还需要一个计数器来数写数据时序和读数据时序需要的段数,最终的到下面计数器的架构图:

下面开始对本模块各个信号进行设计:
Sio_c周期计数器count_sck:该计数器表示sio_c一个周期需要的时钟周期个数。加一条件为(flag_r || flag_w),表示接收到读使能或者写使能之后便开始计数;结束条件为数120个,跟工程设置的SCCB时钟频率为208Khz,一个周期约为4800ns,因此需要数120个,数完就清零。
位计数器count_bit:该计数器表示每个阶段需要传输的数据位数。加一条件为end_count_sck,每传输一位需要一个sio_c的周期;结束条件为数bit_num个,阶段不同,传输的数据数也不同,写数据阶段bit_num=30,读数据阶段bit_num=21

阶段计数器count_duan:该计数器表示传输数据所需要的阶段。加一条件为end_count_bit,每个阶段的数据传输完成之后,表示当前阶段结束;结束条件为数duan_num个,写数据阶段,duan_num=1,读数据阶段duan_num=2

SCCB时钟信号sio_c:初始状态为高电平,由于频率为208K,因此高电平和低电平各持续60个时钟周期,又由于开始条件保持时间和开始条件建立时间的存在,sio_c在每个阶段的第一个数据和最后一个数据部分不能变为低电平,所以变低的条件为(count_bit>=0 && count_bit < (bit_num-2) &&add_count_sck && count_sck == SIO_C-1);从低变高的条件为(count_bit>=1 && count_bit < bit_num && add_count_sck&& count_sck == SIO_C/2-1)

读状态指示信号flag_r:初始状态为0,表示不处于读状态,当接收到读使能ren有效的时候,变为高电平,表示进入读状态;当段计数器count_duan数完之后,该信号变为低电平,表示读阶段结束。

写状态指示信号flag_w:初始状态为0,表示不处于写状态,当接收到写使能wen有效的时候,变为高电平,表示进入写状态;当段计数器count_duan数完之后,该信号变为低电平,表示写阶段结束。

要输出的数据out_data:读数据阶段out_data= {1'h0,rd_com,1'h1,addr_ff,1'h1,1'h0,1'h1,9'h0},其中从左往右依次是起始位0、读写属性rd_com1bitx、寄存器地址、1bitx1个间隔为0、结束位19bit0。间隔位0的存在是由于数据传输结束是一个拉高的操作,因此需要先将数据线拉低,在发送结束位。后面9bit0仅仅是为了将out_data的位宽占满,如果不补的话,会在高位自动补零,这样就不对了。读写属性rd_com会因为读写的不同而变化,写的时候为8’h42,读的时候为 8’ h43;写数据阶段out_data={1'h0,8'h42,1'h1,addr_ff,1'h1,wdata_ff,1'h1,1'h0,1'h1},其中从左往右依次是起始位0、写属性8’h421bitx、寄存器地址、1bitx、写数据wdata_ff1个间隔为0、结束位1

三态门使能en_sio_d_w:当该信号为0,此时只允许从机往FPGA发送数据。当该信号为1时,只允许FPGA往从机发送数据。因此该信号初始状态为0,当需要进行写数据的时候,将该信号拉高即可,拉高的条件为(ren || wen)(flag_r &&count_duan==1 && count_bit==18 && add_count_sck &&count_sck==1-1),这两个条件分别是在接收到读使能或者写使能的时候,都需要先往从机里面写入数据,再读数据的第二阶段,将数据读出之后,还要发送间隔位、停止位等。再读数据得时候,将三态门使能信号拉低,也就是在读的第二阶段,所以拉低的条件为(flag_r && count_duan==1 && count_bit==10 &&add_count_sck && count_sck==1-1)

写数据线sio_d_w:在写数据区域,将out_data的值按照顺序赋值即可,注意在赋值的时候,要在SCCB时钟一个时钟周期开始的时候进行,因此条件为(count_bit>= 0 && count_bit < bit_num && add_count_sck &&count_sck == SIO_C/4-1)

读数据rdata:读出数据的时间为读时序的第二阶段,注意在取值的时候,需要SCCB时钟的高电平的中间时刻进行(flag_r&& count_duan==1 && count_bit>=10 && count_bit <18 && add_count_sck && count_sck==SIO_C/4*3-1)

读数据有效指示信号rdata_vld:初始状态为0,表示此时读数据信号无效。当读数据阶段全部完成之后,读数据有效,将该信号拉高一个时钟周期,因此条件为(flag_r && end_count_duan)

准备好接收指示信号rdy:该信号为0时,表示当前模块处于发送或者接收状态,不能接收上游模块发送的数据。该信号为1时,表示当前看模块处于空闲状态,可以接受上游模块发送的数据。因此该信号为1的条件是(wen || ren || flag_r|| flag_w)。注意rdy信号需要使用组合逻辑产生。

1.5.3参考代码

  1.    always  @(posedge clk or negedge rst_n)begin
  2.         if(rst_n==1'b0)begin
  3.             count_sck <= 0;
  4.         end
  5.         else if(add_count_sck)begin
  6.             if(end_count_sck)begin
  7.                 count_sck <= 0;
  8.             end
  9.             else begin
  10.                 count_sck <= count_sck + 1;
  11.             end
  12.         end
  13.     end

  14.     assign add_count_sck = flag_r || flag_w;
  15.     assign end_count_sck = add_count_sck && count_sck == SIO_C-1;

  16.     always  @(posedge clk or negedge rst_n)begin
  17.         if(rst_n==1'b0)begin
  18.             count_bit <= 0;
  19.         end
  20.         else if(add_count_bit)begin
  21.             if(end_count_bit)begin
  22.                 count_bit <= 0;
  23.             end
  24.             else begin
  25.                 count_bit <= count_bit + 1;
  26.             end
  27.         end
  28.     end

  29.     assign add_count_bit = end_count_sck;
  30.     assign end_count_bit = add_count_bit && count_bit == bit_num+2-1;

  31.     always  @(posedge clk or negedge rst_n)begin
  32.         if(rst_n==1'b0)begin
  33.             count_duan <= 0;
  34.         end
  35.         else if(add_count_duan)begin
  36.             if(end_count_duan)begin
  37.                 count_duan <= 0;
  38.             end
  39.             else begin
  40.                 count_duan <= count_duan + 1;
  41.             end
  42.         end
  43.     end

  44.     assign add_count_duan = end_count_bit;
  45.     assign end_count_duan = add_count_duan && count_duan == duan_num-1;

  46.     always  @(posedge clk or negedge rst_n)begin
  47.         if(rst_n==1'b0)begin
  48.             flag_r <= 0;
  49.         end
  50.         else if(ren)begin
  51.             flag_r <= 1;
  52.         end
  53.         else if(end_count_duan)begin
  54.             flag_r <= 0;
  55.         end
  56.     end

  57.     always  @(posedge clk or negedge rst_n)begin
  58.         if(rst_n==1'b0)begin
  59.             flag_w <= 0;
  60.         end
  61.         else if(wen)begin
  62.             flag_w <= 1;
  63.         end
  64.         else if(end_count_duan)begin
  65.             flag_w <= 0;
  66.         end
  67.     end

  68.     always  @(*)begin
  69.         if(flag_r)begin
  70.             bit_num = 21;
  71.             duan_num = 2;
  72.         end
  73.         else if(flag_w)begin
  74.             bit_num = 30;
  75.             duan_num = 1;
  76.         end
  77.         else begin
  78.             bit_num = 1;
  79.             duan_num = 1;
  80.         end
  81.     end

  82.     always  @(posedge clk or negedge rst_n)begin
  83.         if(rst_n==1'b0)begin
  84.             sio_c <= 1;
  85.         end
  86.         else if(sio_c_h2l)begin
  87.             sio_c <= 0;
  88.         end
  89.         else if(sio_c_l2h)begin
  90.             sio_c <= 1;
  91.         end
  92.     end

  93.     assign sio_c_h2l = count_bit >= 0 && count_bit < (bit_num-2) && add_count_sck && count_sck == SIO_C-1;
  94.     assign sio_c_l2h = count_bit >= 1 && count_bit < bit_num && add_count_sck && count_sck == SIO_C/2-1;


  95.    always  @(posedge clk or negedge rst_n)begin
  96.        if(rst_n==1'b0)begin
  97.            wdata_ff  <= 0;
  98.            addr_ff   <= 0;
  99.        end
  100.        else if(wen ||ren) begin
  101.            wdata_ff  <= wdata    ;
  102.            addr_ff   <= sub_addr ;
  103.        end
  104.    end


  105.     always @ (*)begin
  106.         if(flag_r)begin
  107.             out_data = {1'h0,rd_com,1'h1,addr_ff,1'h1,1'h0,1'h1,9'h0};
  108.         end
  109.         else if(flag_w)begin
  110.             out_data = {1'h0,8'h42,1'h1,addr_ff,1'h1,wdata_ff,1'h1,1'h0,1'h1};
  111.         end
  112.         else begin
  113.             out_data = 0;
  114.         end
  115.     end

  116.     assign rd_com = (flag_r && count_duan == 0)? 8'h42 : 8'h43;

  117.     always  @(posedge clk or negedge rst_n)begin
  118.         if(rst_n==1'b0)begin
  119.             en_sio_d_w <= 0;
  120.         end
  121.         else if(ren || wen)begin
  122.             en_sio_d_w <= 1;
  123.         end
  124.         else if(end_count_duan)begin
  125.             en_sio_d_w <= 0;
  126.         end
  127.         else if(en_sio_d_w_h2l)begin
  128.             en_sio_d_w <= 0;
  129.         end
  130.         else if(en_sio_d_w_l2h)begin
  131.             en_sio_d_w <= 1;
  132.         end
  133.     end

  134.     assign en_sio_d_w_h2l = flag_r && count_duan == 1 && count_bit == 10 && add_count_sck && count_sck == 1-1;
  135.     assign en_sio_d_w_l2h = flag_r && count_duan == 1 && count_bit == 18 && add_count_sck && count_sck == 1-1;

  136.     always  @(posedge clk or negedge rst_n)begin
  137.         if(rst_n==1'b0)begin
  138.             sio_d_w <= 1;
  139.         end
  140.         else if(out_data_time)begin
  141.             sio_d_w <= out_data[30-count_bit-1];
  142.         end
  143.     end

  144.     assign out_data_time = count_bit >= 0 && count_bit < bit_num && add_count_sck && count_sck == SIO_C/4-1;

  145.     always  @(posedge clk or negedge rst_n)begin
  146.         if(rst_n==1'b0)begin
  147.             rdata <= 0;
  148.         end
  149.         else if(rdata_time)begin
  150.             rdata[17-count_bit] <= sio_d_r;
  151.         end
  152.     end

  153.     assign rdata_time = flag_r && count_duan==1 && count_bit>=10 && count_bit<18 && add_count_sck && count_sck==SIO_C/4*3-1;

  154.     always  @(posedge clk or negedge rst_n)begin
  155.         if(rst_n==1'b0)begin
  156.             rdata_vld <= 0;
  157.         end
  158.         else if(flag_r && end_count_duan)begin
  159.             rdata_vld <= 1;
  160.         end
  161.         else begin
  162.             rdata_vld <= 0;
  163.         end
  164.     end

  165.     always  @(*)begin
  166.         if(ren || wen || flag_r || flag_w)begin
  167.             rdy = 0;
  168.         end
  169.         else begin
  170.             rdy = 1;
  171.         end
  172.     end

  173. endmodule
复制代码



1.6 图像采集模块设计1.6.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
工作时钟 25M
rst_n
I
1
系统复位信号,低电平有效
en_capture
I
1
摄像头配置完成指示信号,当其为1时,表示摄像头完成了配置。
vsync
I
1
摄像头帧同步信号,该信号拉高几个时钟周期,表示一帧图像传输的开始。
href
I
I
摄像头行同步信号,该信号为高电平时,摄像头输出的数据有效。
din
I
8
摄像头输出数据
dout
O
16
输出图像数据
dout_vld
O
1
输出图像数据有效指示信号
dout_sop
O
1
输出图像第一个有效数据指示信号
dout_eop
O
1
输出图像最后一个有效数据指示信号

1.6.2设计思路
下面时序图是从OV7670摄像头数据手册中得到的,可以看出来摄像头输出的数据跟随像素时钟时钟,一个时钟周期输出一个字节的数据,而且数据只在行同步信号href为高时有效,我们可以将行同步信号href当作是摄像头输出数据有效指示信号来使用。

从下面时序图中可以看出,帧同步信号拉高三个时钟周期,表示将要开始一帧图像的传输,在vsync拉高之后,到图像开始传输,还有一段时间,这段时间不用管是多少,可以看行同步信号href是否为高就可以了。

我们配置摄像头输出格式为RGB565,因此一个像素是由两个字节组成,而输出数据接口的位宽是一个字节,因此摄像头两个时钟周期才能输出一个完整的图像数据,由下图可以看出先输出的是高8位数据,然后再输出低8位数据。

从上面几个时序图中,已经知道了摄像头输出图像的时刻以及方式,便可以开始对数据进行接收,这里采用行、场两个计数的架构,如下图所示:

行计数器cnt_x:该计数器用来表示摄像头输出图像一行数据的个数。加一条件为din_vld,表示输入的数据有效,便开始计数。结束条件为数1280个,一行有640个像素数据,每个像素都是两个字节,摄像头每次输出一个字节,因此共输出1280个。
列计数器cnt_y:该计数器用来计数一帧图像的行数。加一条件为end_cnt_x,表示每数完1行就加一。结束条件为数480个,一帧图像就是480行,数完就清零。

下面再看一下该模块其他信号的设计:

采集状态指示信号flag_capture:初始状态为0,表示不处于采集状态,当检测到帧同步信号的上升沿和配置完成指示信号有效的时候,便进入采集状态,因此该信号变为1的条件为(flag_capture==0&& vsync_12h && en_capture)。当一帧图像传输完成之后,就退出采集状态,因此该信号变0的条件为end_cnt_y

输入数据有效指示信号din_vld:当进入到采集状态,并且检测到行同步信号为1的时候,输入数据就是有效的,该信号由组合逻辑产生,即flag_capture&& href

输出图像数据dout:初始状态为0,当输入数据有效时,按照数据的排列顺序,进行串并转换。

输出图像数据有效指示信号dout_vld:一个像素数据为2个字节,摄像头每个时钟输出一个字节,因此是输出两次有效一次。初始状态为0,表示数据无效,当行计数器cnt_x的第0位为1的时候,该信号变为1,表示输出图像数据有效。

第一个有效数据指示信号dout_sop:初始状态为0。当行计数器cnt_x等于1,并且列计数器等于0的时候,表示输出第一个有效数据,该信号拉高,其他时候为0

最后一个有效数据指示信号dout_eop:当行计数器cnt_x等于1280-1,并且列计数器等于480-1的时候,表示输出最后一个有效数据,该信号为高,其他时候为低电平。

1.6.3参考代码
  1.    always @ (posedge clk or negedge rst_n)begin
  2.         if(!rst_n)begin
  3.             cnt_x <= 0;
  4.         end
  5.         else if(add_cnt_x)begin
  6.             if(end_cnt_x)begin
  7.                 cnt_x <= 0;
  8.             end
  9.             else begin
  10.                 cnt_x <= cnt_x + 1;
  11.             end
  12.         end
  13.     end

  14.     assign add_cnt_x = flag_capture && din_vld;
  15.     assign end_cnt_x = add_cnt_x && cnt_x == COL*2-1;

  16.     always @ (posedge clk or negedge rst_n)begin
  17.         if(!rst_n)begin
  18.             cnt_y <= 0;
  19.         end
  20.         else if(add_cnt_y)begin
  21.             if(end_cnt_y)begin
  22.                 cnt_y <= 0;
  23.             end
  24.             else begin
  25.                 cnt_y <= cnt_y + 1;
  26.             end
  27.         end
  28.     end

  29.     assign add_cnt_y = end_cnt_x;
  30.     assign end_cnt_y = add_cnt_y && cnt_y == ROW-1;

  31.     always @ (posedge clk or negedge rst_n)begin
  32.         if(!rst_n)begin
  33.             flag_capture <= 0;
  34.         end
  35.         else if(flag_capture == 0 && vsync_l2h && en_capture)begin
  36.             flag_capture <= 1;
  37.         end
  38.         else if(end_cnt_y)begin
  39.             flag_capture <= 0;
  40.         end
  41.     end

  42.     assign vsync_l2h = vsync_ff0 == 0 && vsync == 1;

  43.     always @ (posedge clk or negedge rst_n)begin
  44.         if(!rst_n)begin
  45.             vsync_ff0 <= 0;
  46.         end
  47.         else begin
  48.             vsync_ff0 <= vsync;
  49.         end
  50.     end

  51.     always @ (posedge clk or negedge rst_n)begin
  52.         if(!rst_n)begin
  53.             dout <= 0;
  54.         end
  55.         else if(din_vld)begin
  56.             dout <= {dout[7:0],din};
  57.         end
  58.     end

  59.     assign din_vld = flag_capture && href;

  60.     always @ (posedge clk or negedge rst_n)begin
  61.         if(!rst_n)begin
  62.             dout_vld <= 0;
  63.         end
  64.         else if(flag_dout_vld)begin
  65.             dout_vld <= 1;
  66.         end
  67.         else begin
  68.             dout_vld <= 0;
  69.         end
  70.     end

  71.     assign flag_dout_vld = add_cnt_x && cnt_x[0] == 1;

  72.     always @ (posedge clk or negedge rst_n)begin
  73.         if(!rst_n)begin
  74.             dout_sop <= 0;
  75.         end
  76.         else if(flag_dout_vld && cnt_x[10:1] == 0 && cnt_y == 0)begin
  77.             dout_sop <= 1;
  78.         end
  79.         else begin
  80.             dout_sop <= 0;
  81.         end
  82.     end

  83.     always @ (posedge clk or negedge rst_n)begin
  84.         if(!rst_n)begin
  85.             dout_eop <= 0;
  86.         end
  87.         else if(flag_dout_vld && cnt_x[10:1] == COL-1 && cnt_y == ROW-1)begin
  88.             dout_eop <= 1;
  89.         end
  90.         else begin
  91.             dout_eop <= 0;
  92.         end
  93.     end
复制代码



1.7 存储控制模块设计
1.7.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
工作时钟 25M
rst_n
I
1
系统复位信号,低电平有效
clk_in
I
1
SDRAM时钟,100M
din
I
16
输入图像数据
din_vld
I
1
输入图像数据有效指示信号
din_sop
I
1
第一个有效图像数据指示信号
din_eop
I
1
最后一个有效图像数据指示信号
dout
O
16
输出图像数据
wr_req
O
1
写SDRAM请求信号
rd_req
O
1
读SDRAM请求信号
wdata
O
16
写SDRAM数据
wr_ack
I
1
写SDRAM响应
rd_ack
I
1
读SDRAM响应
sd_rdata
I
16
读SDRAM数据
sd_rdata_vld
I
1
读SDRAM数据有效指示信号
display_area
I
1
显示器显示区域指示信号
bank
O
2
SDRAM的Bank地址
addr
O
13
SDRAM的地址
1.7.2设计思路
本工程的主体时钟为25M,显示器的分辨率为640*480,相当于一个时钟周期显示一个像素。而摄像头的工作时钟也是25M,但是数据位宽为8bit,相当于两个时钟输出一个像素,如果直接将采集模块接收到的数据送给显示器显示,那么一个像素点会被显示两次,这肯定是不对的,因此需要先将摄像头输出的数据缓存起来,当存够一帧图像之后,再送给显示器进行显示,显示器本该1秒显示60帧图像的,降低到了30帧,但是显示的图像是正确而且连续的。

所以本模块的作用就是存储控制,再SDRAM中划分两个区域,通过乒乓操作的方式进行存储的控制,该操作的主要的难点在于摄像头是不断输出数据的,而显示器也需要不断的进行显示才可以,两边都不能等待,但是SDRAM的读写都是使用的同一组数据总线,这也就造成了它注定无法进行同时读写。但是对于本工程来说,由于工程主体时钟25MSDRAM工作时钟100M相比有较大差距,因此可以使用一种“伪同时读写”的操作,达到“同时读写”的效果。此方法的介绍请在明德扬论坛中搜索“需要同时读写SDRAM的解决办法”。

本模块内部包含两个FIFOfifo原理以及使用,可以在明德扬论坛搜索学习),fifo_16bwritefifo_16bread,前者为写FIFO,后者为读FIFO,两个FIFO的作用主要是跨时钟域处理和数据缓存。下面介绍一下该模块中各个信号的设计。

FIFO写使能wfifo_wrreq:输入图像数据从第一个有效数据开始一直到最后一个有效数据期间,写使能信号一直为高电平,即(din_vld && (din_sop || get_data_flag))

数据写入指示信号get_data_flag:初始状态为0,表示没有数据需要写入;拉高的条件为(din_vld&& din_sop),表示接收到第一个有效数据时,便开始写入;有高变低的条件为(din_vld && din_eop),表示当检测到最后一个有效数据之后,将该信号拉低,停止写入。
FIFO数据读出指示信号rd_wfifo_flag:初始状态为0,表示不需要读出数据;变高的条件为wfifo_rdusedw>=512,表示当写FIFO内部数据量达到512个的时候,便准备将数据读出
由于一帧图像的像素点共640*480=307200个,而SDRAM一行有512个存储空间,要将图像存储进SDRAM中,共需要600行。因此提出两个计数器的架构:


写数据行计数器cnt_rd_wfifo:该信号表示SDRAM中每一行中要写入的数据量。加一条件为(rd_wfifo_flag&& ((cnt_rd_wfifo==0 && wr_ack) || (cnt_rd_wfifo!=0)),表示在写数据准备读出状态下,如果检测到写响应信号有效,便开始计数;结束条件为数512个,SDRAM每行内存容量为512个数据,数完就清零。

SDRAM列地址计数器cnt_waddr:该计数器用于计数SDRAM的列地址。加一条件为end_cnt_rd_wfifo,表示每行写完就加一;结束条件为数600个,总需要600行。

FIFO数据写入指示信号wr_RFifo_flag:初始状态为0,表示不需要写入数据;变高的条件为rfifo_rdusedw<550,表示当读FIFO内部数据量小于550个的时候,便请求写入数据。

读使能rd_req:再读FIFO不处于请求数据写入状态的时候,检测到读FIFO内部的数据量少于550个的时候,读使能信号有效,直到接收到读响应。

写使能wr_req:在写FIFO不处于请求输出的状态的时候,检测到写FIFO内部的数据量至少有512个的时候,写使能信号有效,直到接收到写响应。

FIFO读使能wfifo_rdreq:当写FIFO内部不为空,并且行计数器开始计数的时候,该信号有效。

读数据行地址计数器cnt_wr_rfifo:该计数器表示读SDRAM时候的行地址;加一条件为sd_rdata_vld,表示SDRAM读出数据有效就加一;结束条件为数512个。

读数据列地址计数器cnt_raddr:该计数器表示读SDRAM时候的列地址;加一条件为end_cnt_wr_rfifo,表示读出一行数据,就加一;结束条件为数600个。

Bank地址waddr_bank:初始状态为2’b0,当一帧图像的数据全部写进去之后,写Bank地址取反,变为2’b11

Bank地址raddr_bank:初始状态为2’b11,当一帧图像全部读出来之后,并且与写Bank地址相等,则取反,变为2’b00

Bank切换计数器cnt_bank:由于读写Bank的切换需要时间,因此需要此计数器来决定间隔的时间,当开始切换Bank的时候开始计数,经过调试,共间隔8个时钟周期。

FIFO的读使能:当进入到有效显示区域的时候,便将FIFO内部数据读出。

FIFO的写使能:当SDRAM输出有效数据的时候,写使能便拉高,将数据写入。

1.7.3参考代码
  1. always  @(*)begin
  2.             if(rd_wfifo_flag)
  3.                 bank=waddr_bank;
  4.             else
  5.                 bank=raddr_bank;
  6.     end
  7.          always  @(*)begin
  8.             if(rd_wfifo_flag)
  9.                 addr=waddr;
  10.             else
  11.                 addr=raddr;
  12.     end
  13.         
  14.          
  15. fifo_16bwrite        fifo_16bwrite_inst (
  16.         .aclr        ( ~rst_n        ),
  17.         .data        (din             ),
  18.         .rdclk       (clk_in          ),
  19.         .rdreq       (wfifo_rdreq     ),
  20.         .wrclk       (clk             ),
  21.         .wrreq       (wfifo_wrreq     ),
  22.         .q           (wdata           ),
  23.         .rdempty     (wfifo_rdempty   ),
  24.         .rdusedw     (wfifo_rdusedw   )

  25.         );
  26. fifo_16bread        fifo_16bread_inst (
  27.     .aclr        ( ~rst_n         ) ,
  28.         .data        (sd_rdata        ),
  29.         .rdclk       (clk             ),
  30.         .rdreq       (display_area    ),
  31.         .wrclk       (clk_in          ),
  32.         .wrreq       (sd_rdata_vld    ),
  33.         .q           (dout            ),
  34.         .rdempty     (rfifo_rdempty   ),
  35.         .rdusedw     (rfifo_rdusedw   )

  36.         );
  37.         
  38.         assign wfifo_wrreq= din_vld &&(din_sop || get_data_flag);
  39.         assign waddr  = cnt_waddr ;
  40.         assign wbank  = waddr_bank ;
  41.    

  42.           always @ (posedge clk or negedge rst_n)begin
  43.         if(!rst_n)begin
  44.             get_data_flag <= 0;
  45.         end
  46.         else if(din_vld && din_sop)begin
  47.             get_data_flag <= 1;
  48.                                 end
  49.         else if(din_vld && din_eop) begin
  50.             get_data_flag <= 0;
  51.         end
  52.     end

  53.          
  54. assign rd_wfifo_start = rd_wfifo_flag==0&&wfifo_rdusedw>=512;

  55.           always @ (posedge clk_in or negedge rst_n)begin
  56.         if(!rst_n)begin
  57.             rd_wfifo_flag <= 0;
  58.         end
  59.         else if(wfifo_rdusedw>=512)begin
  60.             rd_wfifo_flag <= 1;
  61.                                 end
  62.         else if(end_cnt_rd_wfifo) begin
  63.             rd_wfifo_flag <= 0;
  64.         end
  65.     end


  66.           always @ (posedge clk_in or negedge rst_n)begin
  67.         if(!rst_n)begin
  68.             wr_req <= 0;
  69.         end
  70.         else if(rd_wfifo_flag==0&&wfifo_rdusedw>=512)begin
  71.            wr_req <= 1 ;
  72.                                 end
  73.         else if(wr_ack) begin
  74.              wr_req <= 0;
  75.         end
  76.     end
  77.   

  78.     always @(posedge clk_in or negedge rst_n)begin
  79.         if(!rst_n)begin
  80.             cnt_rd_wfifo <= 0;
  81.         end
  82.         else if(add_cnt_rd_wfifo)begin
  83.             if(end_cnt_rd_wfifo)
  84.                 cnt_rd_wfifo <= 0;
  85.             else
  86.                 cnt_rd_wfifo <= cnt_rd_wfifo + 1;
  87.         end
  88.     end
  89.          
  90.     assign add_cnt_rd_wfifo = rd_wfifo_flag&&((cnt_rd_wfifo==0&&wr_ack)||(cnt_rd_wfifo!=0));      
  91.     assign end_cnt_rd_wfifo = add_cnt_rd_wfifo && cnt_rd_wfifo== 512-1;   


  92.     always  @(*)begin
  93.             if(add_cnt_rd_wfifo&&wfifo_rdempty==0)
  94.                 wfifo_rdreq=1;
  95.             else
  96.                 wfifo_rdreq=0;
  97.     end


  98.     always @(posedge clk_in or negedge rst_n)begin
  99.         if(!rst_n)begin
  100.             cnt_waddr <= 0;
  101.         end
  102.         else if(add_cnt_waddr)begin
  103.             if(end_cnt_waddr)
  104.                 cnt_waddr <= 0;
  105.             else
  106.                 cnt_waddr <= cnt_waddr + 1;
  107.         end
  108.     end

  109.     assign add_cnt_waddr = end_cnt_rd_wfifo;      
  110.     assign end_cnt_waddr = add_cnt_waddr && cnt_waddr==600-1 ;   


  111.         assign  raddr=  cnt_raddr;
  112.         assign  rbank=  raddr_bank;
  113.         
  114. assign wr_rfifo_star= wr_rfifo_flag==0&&rfifo_rdusedw<550;

  115. always  @(posedge clk_in or negedge rst_n)begin
  116.     if(rst_n==1'b0)begin
  117.         wr_rfifo_flag <= 0;
  118.     end
  119.     else if(wr_rfifo_star)begin
  120.           wr_rfifo_flag <= 1;
  121.     end
  122.     else if(end_cnt_wr_rfifo)begin
  123.          wr_rfifo_flag <= 0;
  124.     end
  125. end

  126. always  @(posedge clk_in or negedge rst_n)begin
  127.     if(rst_n==1'b0)begin
  128.         rd_req <= 0;
  129.     end
  130.     else if(wr_rfifo_star)begin
  131.         rd_req <= 1;
  132.     end
  133.     else if(rd_ack)begin
  134.          rd_req <= 0;
  135.     end
  136. end

  137.   always @(posedge clk_in or negedge rst_n)begin
  138.         if(!rst_n)begin
  139.             cnt_wr_rfifo <= 0;
  140.         end
  141.         else if(add_cnt_wr_rfifo)begin
  142.             if(end_cnt_wr_rfifo)
  143.                 cnt_wr_rfifo <= 0;
  144.             else
  145.                 cnt_wr_rfifo <= cnt_wr_rfifo + 1;
  146.         end
  147.     end

  148.     assign add_cnt_wr_rfifo = sd_rdata_vld ;      
  149.     assign end_cnt_wr_rfifo = add_cnt_wr_rfifo && cnt_wr_rfifo== 512-1;   


  150.    
  151.     always @(posedge clk_in or negedge rst_n)begin
  152.         if(!rst_n)begin
  153.             cnt_raddr <= 0;
  154.         end
  155.         else if(add_cnt_raddr)begin
  156.             if(end_cnt_raddr)
  157.                 cnt_raddr <= 0;
  158.             else
  159.                 cnt_raddr <= cnt_raddr + 1;
  160.         end
  161.     end

  162.     assign add_cnt_raddr = end_cnt_wr_rfifo;      
  163.     assign end_cnt_raddr = add_cnt_raddr && cnt_raddr==600-1 ;

  164.          

  165.     always  @(posedge clk_in or negedge rst_n)begin
  166.         if(rst_n==1'b0)begin
  167.             waddr_bank <= 2'b00;
  168.         end
  169.         else if(end_cnt_waddr)begin
  170.             waddr_bank <= ~waddr_bank;
  171.         end
  172.     end

  173. assign change_bank = end_cnt_raddr&&raddr_bank==waddr_bank;

  174. always  @(posedge clk_in or negedge rst_n)begin
  175.     if(rst_n==1'b0)begin
  176.         raddr_bank <= 2'b11;
  177.     end
  178.     else if(end_cnt_raddr&&raddr_bank==waddr_bank)begin
  179.           raddr_bank <= ~raddr_bank;
  180.     end

  181. end


  182. always  @(posedge clk_in or negedge rst_n)begin
  183.     if(rst_n==1'b0)begin
  184.         change_bank_instr <= 0 ;
  185.     end
  186.     else if(change_bank)begin
  187.         change_bank_instr<=1;
  188.     end
  189.     else if(end_cnt_bank)begin
  190.          change_bank_instr <= 0 ;
  191.     end
  192. end



  193. always @(posedge clk_in or negedge rst_n)begin
  194.     if(!rst_n)begin
  195.         cnt_bank <= 0;
  196.     end
  197.     else if(add_cnt_bank)begin
  198.         if(end_cnt_bank)
  199.             cnt_bank <= 0;
  200.         else
  201.             cnt_bank <= cnt_bank + 1;
  202.     end
  203. end

  204. assign add_cnt_bank = change_bank_instr;      
  205. assign end_cnt_bank = add_cnt_bank && cnt_bank==8-1 ;   
复制代码


1.8 SDRAM接口模块设计
本模块的设计跟上一个案例《SDRAM读写控制器》基本一样,这里就不再做介绍,想要了解的可以去明德扬论坛搜素相关文章即可。
【每周FPGA案例】SDRAM读写控制器

1.9 VGA接口模块设计
本模块的设计可以参考明德扬书籍《FPGA至简设计原理与应用》中关于VGA部分的案例,也可以在明德扬论坛中搜索书籍名称。
【FPGA至简设计原理与应用】书籍连载17 第三篇FPGA至简设计项目 第八章VGA显示颜色

1.10 效果和总结
该工程无论在哪一个板子上都是使用的显示器观察现象,因此我们只看一个就可以了


感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:

基于FPGA的密码锁设计
波形相位频率可调DDS信号发生器
基于FPGA的曼彻斯特编码解码设计
基于FPGA的出租车计费系统
数电基础与Verilog设计
基于FPGA的频率、电压测量
基于FPGA的汉明码编码解码设计
关于锁存器问题的讨论
阻塞赋值与非阻塞赋值
参数例化时自动计算位宽的解决办法

1.11 公司简介
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801
开发板——千兆网ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。

[设计教程下载]


[设计代码下载]
mdyOV7670CameraDisplay.zip (37.68 KB, 下载次数: 5)

johnson 2020-11-12 21:23:42
潘老师的力作,必须顶一个!
回复

举报

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发经验
关闭

站长推荐 上一条 /9 下一条

快速回复 返回顶部 返回列表