此项目一共分为摄像头配置模块,图像采集模块,异步FIFO控制模块,SDRAM控制模块,SDRAM端口模块,VGA显示模块。
摄像头配置模块
直接采用IIC接口对摄像头进行配置:模块分化:IIC端口模块,IIC控制模块,和LUT查找表模块;配置图像像素输出为1280*720
摄像头配置参数
//涉嫌头参数配置-LUT模块module lut_da(input clk ,input rst_n,input redy , //反馈信号,1可以发数据,0 不能读写output reg data_en , output [24:0] data_out ,output config_done //1代表配置完成);parameter RW_CTRL=1'b0; //1代表写模式+读模式 ;0代表只写localparam WAIT_TIME =1000_000,//上电等待时间MAX =253-2 ,WAIT =3'b001,WRITE =3'b010,IDLE =3'b100;reg [19:0]cnt ;wireadd_cnt ;wireend_cnt ;reg [24:0] lut_data;reg cnt_flag;reg [2:0] state;reg [2:0] state_next;wire wait_write;wire write_idle;always@(posedge clk or negedge rst_n) beginif(!rst_n) beginstate<=WAIT;endelse beginstate<=state_next;endendalways @(*)begin case(state)WAIT :beginif(wait_write) beginstate_next=WRITE;endelse beginstate_next=WAIT;endendWRITE:beginif(write_idle) beginstate_next=IDLE;endelse beginstate_next=WRITE;endendIDLE :beginstate_next=IDLE;enddefault:state=WAIT;endcaseendassign wait_write=state==WAIT && end_cnt;assign write_idle=state==WRITE&& end_cnt;always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt <= 0;end else if(add_cnt)begin if(end_cnt)begin cnt <= 0;endelse begin cnt <= cnt + 1'b1;end endend assign add_cnt = state==WAIT|| (state==WRITE&&cnt_flag);assign end_cnt = add_cnt && cnt==( (state==WAIT) ? (WAIT_TIME-1 ): (MAX+1) ) ;//data_enalways @(posedge clk or negedge rst_n)begin if(!rst_n)begindata_en <= 0;end else if( data_en==1 && state==WRITE ) begin data_en <= 0; endelse if( state==WRITE && redy==1 )begin //redy==1模块空闲中,可以读写操作data_en<=1;endendassign config_done= state==IDLE;assign data_out= (state==WRITE) ? lut_data : 1'b0;always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_flag <= 0;end else if(data_en)begin cnt_flag<=1'b1;end else begin cnt_flag<=0;end end//lut_data always@(*)begincase( cnt ) //2个字节地址,一个字节数据//15fps VGA YUV output// 24MHz input clock, 24MHz PCLK0 : lut_data= {RW_CTRL,24'h3103_11}; // system clock from pad, bit[1]1 : lut_data= {RW_CTRL,24'h3008_82}; // software reset, bit[7]2 : lut_data= {RW_CTRL,24'h3008_42}; // software power down, bit[6]3 : lut_data= {RW_CTRL,24'h3103_03}; // system clock from PLL, bit[1]4 : lut_data= {RW_CTRL,24'h3017_ff}; // FREX, Vsync, HREF, PCLK, D[9:6] output enable5 : lut_data= {RW_CTRL,24'h3018_ff}; // D[5:0], GPIO[1:0] output enable6 : lut_data= {RW_CTRL,24'h3034_1a}; // MIPI 10-bit7 : lut_data= {RW_CTRL,24'h3037_13}; // PLL root divider, bit[4], PLL pre-divider, bit[3:0]8 : lut_data= {RW_CTRL,24'h3108_01}; // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2]9 : lut_data= {RW_CTRL,24'h3630_36};// SCLK root divider, bit[1:0]10 : lut_data= {RW_CTRL,24'h3631_0e};11 : lut_data= {RW_CTRL,24'h3632_e2};12 : lut_data= {RW_CTRL,24'h3633_12};13 : lut_data= {RW_CTRL,24'h3621_e0};14 : lut_data= {RW_CTRL,24'h3704_a0};15 : lut_data= {RW_CTRL,24'h3703_5a};16 : lut_data= {RW_CTRL,24'h3715_78};17 : lut_data= {RW_CTRL,24'h3717_01};18 : lut_data= {RW_CTRL,24'h370b_60};19 : lut_data= {RW_CTRL,24'h3705_1a};20 : lut_data= {RW_CTRL,24'h3905_02};21 : lut_data= {RW_CTRL,24'h3906_10};22 : lut_data= {RW_CTRL,24'h3901_0a};23 : lut_data= {RW_CTRL,24'h3731_12};24 : lut_data= {RW_CTRL,24'h3600_08}; // VCM control25 : lut_data= {RW_CTRL,24'h3601_33}; // VCM control26 : lut_data= {RW_CTRL,24'h302d_60}; // system control27 : lut_data= {RW_CTRL,24'h3620_52};28 : lut_data= {RW_CTRL,24'h371b_20};29 : lut_data= {RW_CTRL,24'h471c_50};30 : lut_data= {RW_CTRL,24'h3a13_43}; // pre-gain = 1.047x31 : lut_data= {RW_CTRL,24'h3a18_00}; // gain ceiling32 : lut_data= {RW_CTRL,24'h3a19_f8}; // gain ceiling = 15.5x33 : lut_data= {RW_CTRL,24'h3635_13};34 : lut_data= {RW_CTRL,24'h3636_03};35 : lut_data= {RW_CTRL,24'h3634_40};36 : lut_data= {RW_CTRL,24'h3622_01};// 50/60Hz detection 50/60Hz 灯光条纹过滤37 : lut_data= {RW_CTRL,24'h3c01_34}; // Band auto, bit[7]38 : lut_data= {RW_CTRL,24'h3c04_28}; // threshold low sum39 : lut_data= {RW_CTRL,24'h3c05_98}; // threshold high sum40 : lut_data= {RW_CTRL,24'h3c06_00}; // light meter 1 threshold[15:8]41 : lut_data= {RW_CTRL,24'h3c07_08}; // light meter 1 threshold[7:0]42 : lut_data= {RW_CTRL,24'h3c08_00}; // light meter 2 threshold[15:8]43 : lut_data= {RW_CTRL,24'h3c09_1c}; // light meter 2 threshold[7:0]44 : lut_data= {RW_CTRL,24'h3c0a_9c}; // sample number[15:8]45 : lut_data= {RW_CTRL,24'h3c0b_40}; // sample number[7:0]46 : lut_data= {RW_CTRL,24'h3810_00}; // Timing Hoffset[11:8]47 : lut_data= {RW_CTRL,24'h3811_10}; // Timing Hoffset[7:0]48 : lut_data= {RW_CTRL,24'h3812_00}; // Timing Voffset[10:8]49 : lut_data= {RW_CTRL,24'h3708_64};50 : lut_data= {RW_CTRL,24'h4001_02}; // BLC start from line 251 : lut_data= {RW_CTRL,24'h4005_1a}; // BLC always update52 : lut_data= {RW_CTRL,24'h3000_00}; // enable blocks53 : lut_data= {RW_CTRL,24'h3004_ff}; // enable clocks54 : lut_data= {RW_CTRL,24'h300e_58}; // MIPI power down, DVP enable55 : lut_data= {RW_CTRL,24'h302e_00};56 : lut_data= {RW_CTRL,24'h4300_61}; // RGB,57 : lut_data= {RW_CTRL,24'h501f_01}; // ISP RGB58 : lut_data= {RW_CTRL,24'h440e_00};59 : lut_data= {RW_CTRL,24'h5000_a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on// AEC target 自动曝光控制60 : lut_data= {RW_CTRL,24'h3a0f_30}; // stable range in high61 : lut_data= {RW_CTRL,24'h3a10_28}; // stable range in low62 : lut_data= {RW_CTRL,24'h3a1b_30}; // stable range out high63 : lut_data= {RW_CTRL,24'h3a1e_26}; // stable range out low64 : lut_data= {RW_CTRL,24'h3a11_60}; // fast zone high65 : lut_data= {RW_CTRL,24'h3a1f_14}; // fast zone low// Lens correction for ? 镜头补偿66 : lut_data= {RW_CTRL,24'h5800_23};67 : lut_data= {RW_CTRL,24'h5801_14};68 : lut_data= {RW_CTRL,24'h5802_0f};69 : lut_data= {RW_CTRL,24'h5803_0f};70 : lut_data= {RW_CTRL,24'h5804_12};71 : lut_data= {RW_CTRL,24'h5805_26};72 : lut_data= {RW_CTRL,24'h5806_0c};73 : lut_data= {RW_CTRL,24'h5807_08};74 : lut_data= {RW_CTRL,24'h5808_05};75 : lut_data= {RW_CTRL,24'h5809_05};76 : lut_data= {RW_CTRL,24'h580a_08};77 : lut_data= {RW_CTRL,24'h580b_0d};78 : lut_data= {RW_CTRL,24'h580c_08};79 : lut_data= {RW_CTRL,24'h580d_03};80 : lut_data= {RW_CTRL,24'h580e_00};81 : lut_data= {RW_CTRL,24'h580f_00};82 : lut_data= {RW_CTRL,24'h5810_03};83 : lut_data= {RW_CTRL,24'h5811_09};84 : lut_data= {RW_CTRL,24'h5812_07};85 : lut_data= {RW_CTRL,24'h5813_03};86 : lut_data= {RW_CTRL,24'h5814_00};87 : lut_data= {RW_CTRL,24'h5815_01};88 : lut_data= {RW_CTRL,24'h5816_03};89 : lut_data= {RW_CTRL,24'h5817_08};90 : lut_data= {RW_CTRL,24'h5818_0d};91 : lut_data= {RW_CTRL,24'h5819_08};92 : lut_data= {RW_CTRL,24'h581a_05};93 : lut_data= {RW_CTRL,24'h581b_06};94 : lut_data= {RW_CTRL,24'h581c_08};95 : lut_data= {RW_CTRL,24'h581d_0e};96 : lut_data= {RW_CTRL,24'h581e_29};97 : lut_data= {RW_CTRL,24'h581f_17};98 : lut_data= {RW_CTRL,24'h5820_11};99 : lut_data= {RW_CTRL,24'h5821_11};100: lut_data= {RW_CTRL,24'h5822_15};101: lut_data= {RW_CTRL,24'h5823_28};102: lut_data= {RW_CTRL,24'h5824_46};103: lut_data= {RW_CTRL,24'h5825_26};104: lut_data= {RW_CTRL,24'h5826_08};105: lut_data= {RW_CTRL,24'h5827_26};106: lut_data= {RW_CTRL,24'h5828_64};107: lut_data= {RW_CTRL,24'h5829_26};108: lut_data= {RW_CTRL,24'h582a_24};109: lut_data= {RW_CTRL,24'h582b_22};110: lut_data= {RW_CTRL,24'h582c_24};111: lut_data= {RW_CTRL,24'h582d_24};112: lut_data= {RW_CTRL,24'h582e_06};113: lut_data= {RW_CTRL,24'h582f_22};114: lut_data= {RW_CTRL,24'h5830_40};115: lut_data= {RW_CTRL,24'h5831_42};116: lut_data= {RW_CTRL,24'h5832_24};117: lut_data= {RW_CTRL,24'h5833_26};118: lut_data= {RW_CTRL,24'h5834_24};119: lut_data= {RW_CTRL,24'h5835_22};120: lut_data= {RW_CTRL,24'h5836_22};121: lut_data= {RW_CTRL,24'h5837_26};122: lut_data= {RW_CTRL,24'h5838_44};123: lut_data= {RW_CTRL,24'h5839_24};124: lut_data= {RW_CTRL,24'h583a_26};125: lut_data= {RW_CTRL,24'h583b_28};126: lut_data= {RW_CTRL,24'h583c_42};127: lut_data= {RW_CTRL,24'h583d_ce}; // lenc BR offset// AWB 自动白平衡128: lut_data= {RW_CTRL,24'h5180_ff}; // AWB B block129: lut_data= {RW_CTRL,24'h5181_f2}; // AWB control130: lut_data= {RW_CTRL,24'h5182_00}; // [7:4] max local counter, [3:0] max fast counter131: lut_data= {RW_CTRL,24'h5183_14}; // AWB advanced132: lut_data= {RW_CTRL,24'h5184_25};133: lut_data= {RW_CTRL,24'h5185_24};134: lut_data= {RW_CTRL,24'h5186_09};135: lut_data= {RW_CTRL,24'h5187_09};136: lut_data= {RW_CTRL,24'h5188_09};137: lut_data= {RW_CTRL,24'h5189_75};138: lut_data= {RW_CTRL,24'h518a_54};139: lut_data= {RW_CTRL,24'h518b_e0};140: lut_data= {RW_CTRL,24'h518c_b2};141: lut_data= {RW_CTRL,24'h518d_42};142: lut_data= {RW_CTRL,24'h518e_3d};143: lut_data= {RW_CTRL,24'h518f_56};144: lut_data= {RW_CTRL,24'h5190_46};145: lut_data= {RW_CTRL,24'h5191_f8}; // AWB top limit146: lut_data= {RW_CTRL,24'h5192_04}; // AWB bottom limit147: lut_data= {RW_CTRL,24'h5193_70}; // red limit148: lut_data= {RW_CTRL,24'h5194_f0}; // green limit149: lut_data= {RW_CTRL,24'h5195_f0}; // blue limit150: lut_data= {RW_CTRL,24'h5196_03}; // AWB control151: lut_data= {RW_CTRL,24'h5197_01}; // local limit152: lut_data= {RW_CTRL,24'h5198_04};153: lut_data= {RW_CTRL,24'h5199_12};154: lut_data= {RW_CTRL,24'h519a_04};155: lut_data= {RW_CTRL,24'h519b_00};156: lut_data= {RW_CTRL,24'h519c_06};157: lut_data= {RW_CTRL,24'h519d_82};158: lut_data= {RW_CTRL,24'h519e_38}; // AWB control// Gamma 伽玛曲线159: lut_data= {RW_CTRL,24'h5480_01}; // Gamma bias plus on, bit[0]160: lut_data= {RW_CTRL,24'h5481_08};161: lut_data= {RW_CTRL,24'h5482_14};162: lut_data= {RW_CTRL,24'h5483_28};163: lut_data= {RW_CTRL,24'h5484_51};164: lut_data= {RW_CTRL,24'h5485_65};165: lut_data= {RW_CTRL,24'h5486_71};166: lut_data= {RW_CTRL,24'h5487_7d};167: lut_data= {RW_CTRL,24'h5488_87};168: lut_data= {RW_CTRL,24'h5489_91};169: lut_data= {RW_CTRL,24'h548a_9a};170: lut_data= {RW_CTRL,24'h548b_aa};171: lut_data= {RW_CTRL,24'h548c_b8};172: lut_data= {RW_CTRL,24'h548d_cd};173: lut_data= {RW_CTRL,24'h548e_dd};174: lut_data= {RW_CTRL,24'h548f_ea};175: lut_data= {RW_CTRL,24'h5490_1d};// color matrix 色彩矩阵176: lut_data= {RW_CTRL,24'h5381_1e}; // CMX1 for Y177: lut_data= {RW_CTRL,24'h5382_5b}; // CMX2 for Y178: lut_data= {RW_CTRL,24'h5383_08}; // CMX3 for Y179: lut_data= {RW_CTRL,24'h5384_0a}; // CMX4 for U180: lut_data= {RW_CTRL,24'h5385_7e}; // CMX5 for U181: lut_data= {RW_CTRL,24'h5386_88}; // CMX6 for U182: lut_data= {RW_CTRL,24'h5387_7c}; // CMX7 for V183: lut_data= {RW_CTRL,24'h5388_6c}; // CMX8 for V184: lut_data= {RW_CTRL,24'h5389_10}; // CMX9 for V185: lut_data= {RW_CTRL,24'h538a_01}; // sign[9]186: lut_data= {RW_CTRL,24'h538b_98}; // sign[8:1]// UV adjust UV 色彩饱和度调整187: lut_data= {RW_CTRL,24'h5580_06}; // saturation on, bit[1]188: lut_data= {RW_CTRL,24'h5583_40};189: lut_data= {RW_CTRL,24'h5584_10};190: lut_data= {RW_CTRL,24'h5589_10};191: lut_data= {RW_CTRL,24'h558a_00};192: lut_data= {RW_CTRL,24'h558b_f8};193: lut_data= {RW_CTRL,24'h501d_40}; // enable manual offset of contrast// CIP 锐化和降噪194: lut_data= {RW_CTRL,24'h5300_08}; // CIP sharpen MT threshold 1195: lut_data= {RW_CTRL,24'h5301_30}; // CIP sharpen MT threshold 2196: lut_data= {RW_CTRL,24'h5302_10}; // CIP sharpen MT offset 1197: lut_data= {RW_CTRL,24'h5303_00}; // CIP sharpen MT offset 2198: lut_data= {RW_CTRL,24'h5304_08}; // CIP DNS threshold 1199: lut_data= {RW_CTRL,24'h5305_30}; // CIP DNS threshold 2200: lut_data= {RW_CTRL,24'h5306_08}; // CIP DNS offset 1201: lut_data= {RW_CTRL,24'h5307_16}; // CIP DNS offset 2202: lut_data= {RW_CTRL,24'h5309_08}; // CIP sharpen TH threshold 1203: lut_data= {RW_CTRL,24'h530a_30}; // CIP sharpen TH threshold 2204: lut_data= {RW_CTRL,24'h530b_04}; // CIP sharpen TH offset 1205: lut_data= {RW_CTRL,24'h530c_06}; // CIP sharpen TH offset 2206: lut_data= {RW_CTRL,24'h5025_00};207: lut_data= {RW_CTRL,24'h3008_02}; // wake up from standby, bit[6]// input clock 24Mhz, PCLK 84Mhz208: lut_data= {RW_CTRL,24'h3035_21}; // PLL209: lut_data= {RW_CTRL,24'h3036_69}; // PLL210: lut_data= {RW_CTRL,24'h3c07_07}; // lightmeter 1 threshold[7:0]211: lut_data= {RW_CTRL,24'h3820_47}; // flip212: lut_data= {RW_CTRL,24'h3821_01}; // no mirror213: lut_data= {RW_CTRL,24'h3814_31}; // timing X inc214: lut_data= {RW_CTRL,24'h3815_31}; // timing Y inc215: lut_data= {RW_CTRL,24'h3800_00}; // HS216: lut_data= {RW_CTRL,24'h3801_00}; // HS217: lut_data= {RW_CTRL,24'h3802_00}; // VS218: lut_data= {RW_CTRL,24'h3803_fa}; // VS219: lut_data= {RW_CTRL,24'h3804_0a}; // HW : lut_data= HE}220: lut_data= {RW_CTRL,24'h3805_3f}; // HW : lut_data= HE}221: lut_data= {RW_CTRL,24'h3806_06}; // VH : lut_data= VE}222: lut_data= {RW_CTRL,24'h3807_a9}; // VH : lut_data= VE}223: lut_data= {RW_CTRL,24'h3808_05}; // DVPHO 1280224: lut_data= {RW_CTRL,24'h3809_00}; // DVPHO225: lut_data= {RW_CTRL,24'h380a_02}; // DVPVO 76: lut_data= {RW_CTRL,24'h380b_d0}; // DVPVO227: lut_data= {RW_CTRL,24'h380c_07}; // HTS228: lut_data= {RW_CTRL,24'h380d_64}; // HTS229: lut_data= {RW_CTRL,24'h380e_02}; // VTS230: lut_data= {RW_CTRL,24'h380f_e4}; // VTS231: lut_data= {RW_CTRL,24'h3813_04}; // timing V offset232: lut_data= {RW_CTRL,24'h3618_00};233: lut_data= {RW_CTRL,24'h3612_29};234: lut_data= {RW_CTRL,24'h3709_52};235: lut_data= {RW_CTRL,24'h370c_03};236: lut_data= {RW_CTRL,24'h3a02_02}; // 60Hz max exposure237: lut_data= {RW_CTRL,24'h3a03_e0}; // 60Hz max exposure238: lut_data= {RW_CTRL,24'h3a14_02}; // 50Hz max exposure239: lut_data= {RW_CTRL,24'h3a15_e0}; // 50Hz max exposure240: lut_data= {RW_CTRL,24'h4004_02}; // BLC line number241: lut_data= {RW_CTRL,24'h3002_1c}; // reset JFIFO, SFIFO, JPG242: lut_data= {RW_CTRL,24'h3006_c3}; // disable clock of JPEG2x, JPEG243: lut_data= {RW_CTRL,24'h4713_03}; // JPEG mode 3244: lut_data= {RW_CTRL,24'h4407_04}; // Quantization scale245: lut_data= {RW_CTRL,24'h460b_37};246: lut_data= {RW_CTRL,24'h460c_20};247: lut_data= {RW_CTRL,24'h4837_16}; // MIPI global timing248: lut_data= {RW_CTRL,24'h3824_04}; // PCLK manual divider249: lut_data= {RW_CTRL,24'h5001_83}; // SDE on, CMX on, AWB on250: lut_data= {RW_CTRL,24'h3503_00}; // AEC/AGC on 251: lut_data= {RW_CTRL,24'h4740_20}; // VS 1//252: lut_data= {RW_CTRL,24'h503d_80}; // color bar 选择彩条输出//253: lut_data= {RW_CTRL,24'h4741_00}; //default: lut_data=0;endcaseendendmodule
IIC端口模块
//IIC端口模块`include "param.v"module i2c_itfc(input clk ,input rst_n ,input [4:0] cmd ,input [7:0] wr_din,input sda_in,output reg sda_en,output reg sda_out,output reg scl ,output reg [7:0]rd_out, output reg rd_out_vld ,output reg done0 ,output reg ack );//高位先发msbparameter PAN=150,DOW_TIME=75,WR_TIME =15, //发送数据不管RD_TIME =90;//采集数据 localparam IDLE= 7'b000_0001,START= 7'b000_0010,WRITE= 7'b000_0100,ACTACK = 7'b000_1000,READ= 7'b001_0000,SENDACK = 7'b010_0000,STOP= 7'b100_0000;reg [6:0] state;reg [6:0] state_next;wireidle_start ;wirestart_write ;wirewrite_actack ;wireactack_start ;wireactack_write ;wireactack_read ;wireread_sendack ;wiresendack_read ;wiresendack_stop ;wirestop_idle ;wireactack_stop ;reg [15:0] max;reg [15:0] cnt_bit; wire add_cnt_bit;wire end_cnt_bit;reg [3:0] cnt_byte; wire add_cnt_byte;wire end_cnt_byte;reg [7:0] data_flag ; //接收数据暂存always@(posedge clk or negedge rst_n) beginif(!rst_n) beginstate<=IDLE;endelse beginstate<=state_next;endendalways@(*) begincase(state)IDLE : beginif(idle_start) beginstate_next=START;endelse beginstate_next<=state;endendSTART: beginif(start_write) beginstate_next=WRITE;endelse beginstate_next<=state;endendWRITE: beginif(write_actack) beginstate_next=ACTACK;endelse beginstate_next<=state;endendACTACK: beginif(actack_start) beginstate_next=START;endelse if(actack_write) beginstate_next=WRITE;endelse if(actack_read) beginstate_next=READ;endelse if(actack_stop) beginstate_next=STOP;endelse beginstate_next<=state;endendREAD : beginif(read_sendack) beginstate_next=SENDACK;endelse beginstate_next<=state;endendSENDACK: beginif(sendack_stop) beginstate_next=STOP; endelse if(sendack_read) beginstate_next=READ;endelse beginstate_next<=state;endendSTOP : beginif(stop_idle) beginstate_next=IDLE;endelse beginstate_next<=state;endenddefault:state_next<=IDLE;endcaseendassign idle_start =state==IDLE && (cmd==`CMD_EN) ; //开始工作 assign start_write =state==START && (end_cnt_byte) ; assign write_actack=state==WRITE && (end_cnt_byte) ; assign actack_start=state==ACTACK && (cmd==`CMD_RDST &&end_cnt_byte ) ; //读数据的时候发起始位1次assign actack_write=state==ACTACK && (cmd==`CMD_WR &&end_cnt_byte ) ; //写数据 assign actack_read =state==ACTACK && (cmd==`CMD_RD &&end_cnt_byte ) ; //接收数据assign actack_stop =state==ACTACK && ((cmd==`CMD_NO &&end_cnt_byte)||ack==1) ; //写完数据接发停止位 或者ack无应答assign read_sendack=state==READ && ( end_cnt_byte) ; assign sendack_read=state==SENDACK&& ( end_cnt_byte) ; assign sendack_stop=state==SENDACK&& (cmd==`CMD_NO &&end_cnt_byte ) ; //发停止位assign stop_idle =state==STOP && (end_cnt_byte) ; //一个字节周期always@(posedge clk or negedge rst_n) beginif(!rst_n) begincnt_bit<=0;endelse if(end_cnt_bit) begincnt_bit<=0;endelse if(add_cnt_bit) begincnt_bit<=cnt_bit+1'b1;endendassign add_cnt_bit=state!=IDLE;assign end_cnt_bit=add_cnt_bit && cnt_bit==PAN-1;//cnt_byte多少个字节周期always@(posedge clk or negedge rst_n) beginif(!rst_n) begincnt_byte<=0;endelse if(end_cnt_byte) begincnt_byte<=0;endelse if(add_cnt_byte) begincnt_byte<=cnt_byte+1'b1;endendassign add_cnt_byte=state!=IDLE && end_cnt_bit;assign end_cnt_byte=add_cnt_byte && cnt_byte==max-1; //cnt_byte==MAX-1之后等下个周期add_cnt_byte到了才为零///maxalways@( * ) beginif(!rst_n) beginmax=0;endelse begincase(state)START : max=1;WRITE : max=8;ACTACK : max=1;READ : max=8;SENDACK: max=1;STOP : max=1;default:max=0;endcase endend//sclalways@(posedge clk or negedge rst_n) beginif(!rst_n) beginscl<=1;endelse if( cnt_bit==DOW_TIME-1 ) begin //scl<=1;endelse if( start_write|| ( (state==WRITE||state==READ||state==ACTACK||state==SENDACK)&&cnt_bit==PAN-1&&actack_stop==0 ) ) beginscl<=0; //数据传输状态scl先低电平再高电平 end else if(state==START ||state==STOP) begin //起始状态,stop状态高电平scl<=1;endend//ackalways@(posedge clk or negedge rst_n) beginif(!rst_n) beginack<=0;endelse if(state==ACTACK && cnt_bit==RD_TIME) begin ack<=sda_in;//发数据时,反馈ack是否应答endelse if( stop_idle ) begin//一次数据发送结束,反馈一个信号ack<=1;endelse beginack<=0;endend //sda_en always@(posedge clk or negedge rst_n) beginif(!rst_n) beginsda_en<=1;endelse if(actack_read||write_actack||sendack_read) begin //接收应答信号 和 读数据时候 为低电平sda_en<=0;endelse if( read_sendack ||actack_start||actack_write||actack_stop) beginsda_en<=1;endend//sda_out always@(posedge clk or negedge rst_n) beginif(!rst_n) beginsda_out<=1; //空闲为1endelse if(state==START&& cnt_bit==DOW_TIME ) begin //起始状态,sda由高拉低sda_out<=0;endelse if(state==WRITE && cnt_bit==WR_TIME ) begin //写数据状态,scl低电平写数据sda_out<=wr_din[7-cnt_byte]; //高字节先发endelse if(read_sendack&&cmd==`CMD_NO) begin //发送应答信号状态, NO_ACKsda_out<=1;endelse if( read_sendack ) begin //发送应答信号状态,ACKsda_out<=0;endelse if( actack_stop|| sendack_stop ) begin //停止状态先拉低sda_out<=0;endelse if( state==STOP && cnt_bit==DOW_TIME ) begin //停止状态又由低拉高sda_out<=1;endend//sda_in data_flagalways@(posedge clk or negedge rst_n) beginif(!rst_n) begindata_flag<=0;endelse if(state==READ && cnt_bit==RD_TIME-1 ) begin data_flag[8-cnt_byte]<=sda_in;endend//donealways@(posedge clk or negedge rst_n) beginif(!rst_n) begindone0<=0;endelse if( ((state==ACTACK|| state==READ )&&cnt_bit==PAN-4 && cnt_byte==max-1)||stop_idle) begin //done信号提前3个周期给出去,或者状态结束(反馈到控制模块)done0<=1;endelse begindone0<=0;endend//rd_out rd_out_vldalways@(posedge clk or negedge rst_n) beginif(!rst_n) beginrd_out<=0;rd_out_vld<=0;endelse if( state==READ &&cnt_bit==PAN-1 ) begin rd_out<=data_flag;rd_out_vld<=1;endelse beginrd_out_vld<=0;endendendmodule
IIC控制模块
//IIC控制模块`include "param.v"module sio_drive(inputclk,inputrst_n ,output regredy , //写数据进来要求。1代表空闲中,0代表工作中inputdata_en, //lut数据传输开始使能input[24:0] data_l , //lut数据模块output[7:0] dout , //读出的数据output dout_vld, //读出有效数据output reg no_ack , //无应答//端口模块output reg [4:0] cmd,output reg [7:0] data_i2c, //传给端口模块数据input[7:0] i2c_din, //端口模块传回数据inputi2c_din_vld ,inputdone , //上次数据传输完成标志 inputack //端口模块 );//parameter WR_NUM=3'd4, //读操作一共周期数RD_NUM=3'd5, //写操作一共周期数WR_ID =8'h78, //摄像头写IDRD_ID =8'h79; //摄像头读IDreg [2:0] cnt_byte ;wire add_cnt_byte ;wire end_cnt_byte ;reg [2:0]MAX ;wire rw_ctrl ;reg [23:0]data_lut ; //数据暂存reg wr_rd ;reg [1:0]per ; assign rw_ctrl = data_en ? data_l[24]:1'b0; //是写+读 或者只写always @(posedge clk or negedge rst_n)begin if(!rst_n)begindata_lut<=0;end else if(data_en)begin data_lut<=data_l[23:0] ;end end//wr_rd peralways@(posedge clk or negedge rst_n) beginif(!rst_n) beginper<=0;wr_rd<=0; //0代表读,1代表写endelse if( rw_ctrl==1 && data_en ) begin //写模式+读模式per<=2; wr_rd<=1;endelse if(rw_ctrl==0 && data_en) begin //只写模式per<=1; wr_rd<=1;endelse if(cnt_byte==MAX && ack==1) begin //ack只有一个周期per<=per-1'b1;wr_rd<=0; //读数据endend//redyalways@(* ) beginif( per==0 ) begin //per==1 代表空闲状态redy=1'b1 ;endelsebeginredy=0 ;endend//no_ackalways@(posedge clk or negedge rst_n) beginif(!rst_n) beginno_ack<=0;endelse if(cnt_byte>0&&cnt_byte<MAX) beginno_ack<=ack; endelsebeginno_ack<=0; endend//cnt_bytealways@(posedge clk or negedge rst_n) beginif(!rst_n) begincnt_byte<=0;endelse if(end_cnt_byte && ack==1) begincnt_byte<=0;endelse if(end_cnt_byte ) begincnt_byte<=cnt_byte;endelse if(add_cnt_byte) begincnt_byte<=cnt_byte+1'b1;endendassign add_cnt_byte= done==1 ; //提前一个周期给反馈信号assign end_cnt_byte=(add_cnt_byte && cnt_byte==MAX)||no_ack ; //最后一个数据发完再接收一个done//MAXalways@(posedge clk or negedge rst_n) beginif(!rst_n) beginMAX<=0;endelse if(wr_rd==1 && cnt_byte==0) beginMAX<=WR_NUM ;endelse if(wr_rd==0 && cnt_byte==0 ) beginMAX<=RD_NUM ;endend//输出给端口模块数据data_i2calways@(posedge clk or negedge rst_n) beginif(!rst_n) begindata_i2c<=0;cmd<=0;endelse if( wr_rd==1 ) begin //writeif( (per==1 ||per==2)&&cnt_byte==0 ) begin //写模式cmd<=`CMD_EN; //端口模块开始工作data_i2c<=WR_ID;endelse case(cnt_byte)1 :begin data_i2c<=data_lut[23:16];cmd<=`CMD_WR;end //地址2 :begin data_i2c<=data_lut[15:8] ;cmd<=`CMD_WR;end //数据3 :begin data_i2c<=data_lut[7:0] ;cmd<=`CMD_WR;end 4 :begincmd<=`CMD_NO; end default:data_i2c<=0;endcaseendelse if( wr_rd==0 ) begin //readif( (per==1 ||per==2)&&cnt_byte==0 ) begincmd<=`CMD_EN; //端口模块开始工作data_i2c<=WR_ID;endelse case(cnt_byte)1 :begin data_i2c<=data_lut[23:16];cmd<=`CMD_WR;end2 :begin data_i2c<=data_lut[15:8];cmd<=`CMD_WR;end3 :begin data_i2c<=RD_ID;cmd<=`CMD_RDST;end4 :begin cmd<=`CMD_RD;end5 :begincmd<=`CMD_NO;enddefault:begin data_i2c<=0;cmd<=0;endendcaseendendassign dout =i2c_din ;assign dout_vld=i2c_din_vld;endmodule
图像采集模块
//图像采集模块`include "param.v"module ov5640_capture(input clk ,input rst_n ,input href ,//行同步input vsync ,//帧同步input config_done, //配置模块完成input [7:0] din ,//输出output reg[15:0] dout , //信号与数据和数据有效 在时钟周期内是同步的,output reg dout_vld , //信号与数据和数据有效 在时钟周期内是同步的,output reg sop , //信号与数据和数据有效 在时钟周期内是同步的,包头output reg eop , //信号与数据和数据有效 在时钟周期内是同步的,包尾output reg [23:0] cnt_dout_vld );reg [11:0] cnt_col; //行计数器 2560wire add_cnt_col;wire end_cnt_col;reg [9:0] cnt_row; //场计数器 720wire add_cnt_row;wire end_cnt_row;reg vsync_r; //一帧的起点reg href_flag;reg dout_lvd_en;//帧起点同步always@(posedge clk or negedge rst_n) beginif(!rst_n) beginvsync_r<=0;endelse if( vsync && config_done) begin //摄像头配置完成vsync_r<=1'b1 ;endelse if(dout_vld) begin vsync_r<=0;endendalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginhref_flag <= 0;end else if(dout_lvd_en) beginhref_flag<=0; endelse if( href )begin href_flag<=1'b1;end else begin href_flag<=0; end end//行计数always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_col <= 0;end else if(add_cnt_col)begin if(end_cnt_col)begin cnt_col <= 0;endelse begin cnt_col <= cnt_col + 1'b1;end endend assign add_cnt_col = href_flag ; assign end_cnt_col = add_cnt_col && cnt_col ==`COL_MAX-1 ;//1280//场计数always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_row <= 0;end else if(add_cnt_row)begin if(end_cnt_row)begin cnt_row <= 0;endelse begin cnt_row <= cnt_row + 1'b1;end endend assign add_cnt_row = end_cnt_col;assign end_cnt_row = add_cnt_row && cnt_row ==`ROW_MAX-1; //720always @(posedge clk or negedge rst_n)begin if(!rst_n) begindout<=0;endelse begin dout[15-cnt_col[0]*8 -:8] <= din;end end//dout_vld 数据晚一个周期always @(posedge clk or negedge rst_n)begin if(!rst_n) begindout_vld<=0;endelse if(add_cnt_col && cnt_col[0]==1)begin dout_vld<=1'b1;end else begin dout_vld<=0;end end//sop always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsop <= 0;end else if( add_cnt_col && cnt_col[0]==1&& vsync_r )begin sop<=1'b1;end else begin sop<=0; end endalways @(*)begin if(end_cnt_row)begin eop=1'b1; end else begin eop=1'b0; end end//可修改always @(posedge clk or negedge rst_n)begin if(!rst_n)begindout_lvd_en <= 0;end else if(eop)begin dout_lvd_en<=1'b1;endelse if( vsync )begin dout_lvd_en<=1'b0;end end//仿真测试用always@(posedge clk or negedge rst_n) beginif(!rst_n) begincnt_dout_vld<=0;endelse if(eop) begincnt_dout_vld<=0;endelse if( add_cnt_col && dout_vld==1'b1) begincnt_dout_vld<=cnt_dout_vld+1'b1;endendendmodule
异步FIFO控制模块
此模块采用FIFO18位宽:数据+sop+eop判断
//异步FIFO处理跨时域问题,判断是否丢帧问题`include "param.v"//wrfifo 18位//rdfifo 16位module fifo_ctrl (inputclk , //100minputrst_n ,inputclk_in, //84minputclk_out , //75m//采集模块数据input [15:0]din_pixel ,inputdin_pixel_vld ,inputsop ,inputeop , //vga模块inputreq_vga,output reg[15:0] data_vga ,output reg data_vga_vld,//wrfifo 读出给sdram数据output [11:0] wrfifo_rdusedw ,output [17:0] wrfifo_q , inputwrfifo_rdreq,//rdfifo sdram写进数据input[15:0] rdfifo_data,inputrdfifo_wrreq, output reg sop_vld, //一帧数据丢失,不能读wrfifo数据output [10:0] rdfifo_wrusedw ,output reg[23:0]cnt_num ,output reg[23:0]cnt_vld);//wrfiforeg [17:0] wrfifo_data ;//reg eop_vld ;reg fail;reg wrfifo_wrreq ;wirewrfifo_wrfull ; //rdfifowirerdfifo_rdempty;wire [15:0] rdfifo_q ; wirerdfifo_rdreq ; reg rdreq_flag;wire [11:0] wrfifo_wusedw ;reg sop_flag ;wire [17:0]wrfifo_qout ;//wrFIFO输出wrfifou_wrfifo(.aclr (~rst_n ) ,.data (wrfifo_data ) ,.rdclk(clk) ,.rdreq(rdreq_flag ) ,.wrclk(clk_in ) ,.wrreq(wrfifo_wrreq ) ,.q (wrfifo_qout ) ,.rdusedw (wrfifo_rdusedw) ,.wrfull (wrfifo_wrfull) ,.wrusedw (wrfifo_wusedw ));//*****************************************WRFIFO*******************************************//always @(posedge clk_in or negedge rst_n)begin if(!rst_n)beginwrfifo_data <= 0;end else begin wrfifo_data<={sop,eop,din_pixel} ;end end//sop_vld为1代表这一帧数据丢失always @(posedge clk or negedge rst_n)begin //写时钟下if(!rst_n)begin sop_vld<=1'b0;end else if( wrfifo_rdusedw>4050 )begin sop_vld <= 1'b1;end else if( wrfifo_qout[17] /* sop */) begin //写数据时钟sop_vld<=1'b0;endendalways @( * )begin if(sop_vld )begin rdreq_flag=1'b1;end else beginrdreq_flag = wrfifo_rdreq;endendalways @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_num <= 0;end else if(wrfifo_qout[16]) begincnt_num<=0;endelse if(sop_vld==0 &&rdreq_flag )begin cnt_num<=cnt_num+1'b1;endendassign wrfifo_q=(sop_vld==0)?wrfifo_qout:0;always @(posedge clk_in or negedge rst_n)begin if(!rst_n)beginsop_flag <= 0;end else if(sop==1'b1)begin sop_flag<=1'b1;end else if(eop)begin sop_flag <= 0;end endalways @(posedge clk_in or negedge rst_n)begin if(!rst_n)beginwrfifo_wrreq <= 0;end else if(wrfifo_wrfull==1'b0)begin wrfifo_wrreq<=(sop_flag&& din_pixel_vld )||sop ;end end//tb测试用always @(posedge clk_in or negedge rst_n)begin if(!rst_n)begincnt_vld <= 0;end else if(sop_flag==0) begincnt_vld<=0;endelse if(wrfifo_wrreq)begin cnt_vld<=cnt_vld+1'b1 ;end end//************************rdfifo****************************************//rdfifo u_rdfifo(.aclr(~rst_n ) ,.data (rdfifo_data ) ,.rdclk(clk_out ) ,.rdreq(rdfifo_rdreq ) ,.wrclk(clk) , .wrreq(rdfifo_wrreq ) ,.q (rdfifo_q ) ,.rdempty (rdfifo_rdempty) ,.wrusedw (rdfifo_wrusedw) );//data_vga //data_vga_vld always@(posedge clk_out or negedge rst_n) beginif(!rst_n) begindata_vga<=0;data_vga_vld<=0;endelse begindata_vga<=rdfifo_q;data_vga_vld<=rdfifo_rdreq;endendassign rdfifo_rdreq= rdfifo_rdempty==1'b0 ? req_vga : 1'b0 ;endmodule
SDRAM控制模块
SDRAM控制模块`include "param.v"module sdram_ctrl (input clk, input rst_n , //wrfifo input [1:0] wrfifo_seop , input [11:0] wrfifo_rdusedw,input sop_vld ,//rdfifo input [10:0] rdfifo_wrusedw , output reg[15:0] dout_fifo, output reg dout_fifo_vld, //接收端口模块数据 input [2:0] done,input [15:0] din_ctrl , input din_ctrl_vld , //传给端口模块 output reg[23:0] addr_iftc,output reg wr_en_out, output reg rd_en_out,output regsop ,outputregeop);//状态参数localparam IDLE =5'b0000_1,ITFCEN=5'b0001_0,ADDR =5'b0010_0,WRITE =5'b0100_0,READ =5'b1000_0;reg [4:0] state;reg [4:0] state_next;wire idle_itfcen;wire itfcen_addr;wire idle_addr ;wire addr_write;wire addr_read ;wire write_idle;wire read_idle ;//信号参数wire [15:0]din;reg wr_en ;reg rd_en ;reg [21:0] addr_wr; wireadd_addr_wr;wireend_addr_wr;reg [21:0] addr_rd; wireadd_addr_rd;wireend_addr_rd;regflag ; reg [1:0] wr_bank ;reg [1:0] rd_bank ;regseop ;regbank_turn ;//bank翻转记录 //打拍regsop_flag;wirenedge;wirepedge;regsop_vld_r0;regsop_vld_r1;//********************************sop+打拍***************************************8/always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsop<=0;eop<=0;endelse if(state==WRITE||write_idle) begineop<=wrfifo_seop[0];sop<=0;endelse if(addr_write ) beginsop<=wrfifo_seop[1];eop<=0;endelse beginsop<=0;eop<=0; endend//**************************状态机**********************************************/always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate <= IDLE;end else begin state <= state_next;end end always @(*)begin case(state)IDLE :beginif(idle_itfcen) beginstate_next=ITFCEN;endelse beginstate_next=state;endendITFCEN :beginif(itfcen_addr) beginstate_next=ADDR;endelse beginstate_next=state;endendADDR: beginif(addr_write) beginstate_next=WRITE;endelse if(addr_read) beginstate_next=READ;endelse beginstate_next=state;endendWRITE:beginif(write_idle) beginstate_next=IDLE;endelse beginstate_next=state;endendREAD :beginif(read_idle) beginstate_next=IDLE;endelse beginstate_next=state;endenddefault:state_next=IDLE;endcaseendassign idle_itfcen =state==IDLE && (wr_en ||rd_en ); //en必须只有一个周期assign itfcen_addr =state==ITFCEN &&(rd_en_out||wr_en_out) ;//仲裁后,判断读写assign addr_write=state==ADDR && (flag==1'b1 ); //提示下一周期给数据assign addr_read =state==ADDR && (flag==1'b0 );//读,给一次地址就行assign write_idle=state==WRITE && (done[2]==1'b0 );//提示下一周期停止给数据assign read_idle =state==READ && (done[2]==1'b0 );//done[2]代表一次突发完成//****************************地址*****************************************/always @(posedge clk or negedge rst_n)begin if(!rst_n)beginaddr_wr <=1'b0;end else if(add_addr_wr)begin if(end_addr_wr)begin addr_wr <=1'b0;endelse begin addr_wr <= addr_wr + `BUSRT_MAX;end endend assign add_addr_wr = write_idle; //突发完后再加一次assign end_addr_wr = add_addr_wr && addr_wr ==`FPS-`BUSRT_MAX ; //一帧像素-突发长度1800-1 always @(posedge clk or negedge rst_n)begin if(!rst_n)beginaddr_rd <=1'b0;end else if(add_addr_rd)begin if(end_addr_rd)begin addr_rd <=1'b0;endelse begin addr_rd <= addr_rd + `BUSRT_MAX;end endend assign add_addr_rd = read_idle;assign end_addr_rd = add_addr_rd && addr_rd == `FPS-`BUSRT_MAX ;//*******************************************seop***************************************/////always @(posedge clk or negedge rst_n)begin if(!rst_n)beginseop <= 1'b1;end else if( end_addr_wr )begin seop <=1'b0; end else if(bank_turn==0 ) begin //seop突发写标志,1才能写。0 不能写seop <= 1'b1;endend// rd_bank, wr_bank, bank_turn状态 写模式翻转记录always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_bank <= 2'b00;rd_bank <=2'b11;bank_turn<=1'b0; //状态翻转记录 0代表已经翻转,可以写。1代表还未翻转等待翻转中end else if( seop==0 && end_addr_rd)begin wr_bank <=~wr_bank;rd_bank <=~rd_bank;bank_turn<=1'b0; end else if( seop==1'b1 && wr_en ) beginbank_turn<=1'b1; //翻转后endend//bank_flag 第一个bank写完标志//*****************************突发信号******************************/always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsop_vld_r0 <=1'b0;sop_vld_r1 <=1'b0;end else begin //sop_vld_r0 <=sop_vld;sop_vld_r1 <=sop_vld_r0;endendassign pedge=sop_vld_r0&& ~sop_vld_r1;assign nedge=~sop_vld_r0&& sop_vld_r1;always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsop_flag <=1'b0;end else if(pedge) beginsop_flag<=1'b1;endelse if(nedge) beginsop_flag<=1'b0;endendalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_en <=1'b0;end else if(idle_itfcen) beginwr_en<=1'b0;endelse if( (wrfifo_rdusedw >=`BUSRT_MAX-1) && seop==1'b1 && state==IDLE && sop_flag==0 )begin wr_en<=1'b1; //代表一次突发写 end end//rd_en//代表一次突发读always @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_en <=1'b0;end else if(idle_itfcen) begin rd_en <=1'b0;endelse if(/*rd_flag && */rdfifo_wrusedw<`FIFO_L && state==IDLE )begin //读数据,rdfifo数据量小于300,且端口模块空闲中rd_en<=1'b1;end else if(rdfifo_wrusedw>`FIFO_H) begin //读数据,rdfifo数据量大于1600,rd_en<=1'b0;end end//读写仲裁标志always @(posedge clk or negedge rst_n)begin if(!rst_n)beginflag <=1'b0;end else if(wr_en&&rd_en &&state==IDLE) begin // 同时接到读请求、写请求. flag<=~flag;endelse if(wr_en)begin //突发一次写,flag<=1'b1;end else if(rd_en)begin //突发一次读 flag<=1'b0;end end//wr_en_out 传给端口模块的读写使能信号//rd_en_outalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_en_out <=0 ; rd_en_out <=0 ;end else if( (wr_en_out||rd_en_out)&&state==ITFCEN) beginwr_en_out <=0 ; rd_en_out <=0 ;endelse if( state==ITFCEN)begin wr_en_out <=flag ; rd_en_out <=~flag ;end else beginwr_en_out <=0 ; rd_en_out <=0 ;endend //**************************输出*********************************************///addr_iftcalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginaddr_iftc <= 0;end else if(flag==1'b0)beginaddr_iftc <= {rd_bank,addr_rd};end else begin addr_iftc <= {wr_bank,addr_wr};end end //输出 时序always @(posedge clk or negedge rst_n)begin if(!rst_n)begindout_fifo <= 0;dout_fifo_vld<=0;end else begin dout_fifo <= din_ctrl;dout_fifo_vld<=din_ctrl_vld;end end endmodule
SDRAM端口模块
SDRAM端口模块`include "param.v"module sdram_itfc(inputclk ,inputrst_n ,inputwr_en , //写使能 .同时地址数据有效inputrd_en , //读使能input [23:0] addr , //12个地址加2个bank地址input [15:0]dout_sdram ,output reg dout_sdram_req , output sclk ,output reg cke ,output cs_n , //除时钟外,使能型号output ras_n , //output cas_n , //output we_n , //output reg[1:0]ba_addr, //bank地址output reg[12:0] sdram_addr , //输进sdram地址//DQM maskoutput reg[1:0]dqm_out,inout [15:0]dq , //接控制模块 output reg[2:0]done , //反馈信号。地址反馈 及一个数据写完了反馈output reg[15:0] data_ctrl ,output regdata_ctrl_vld );localparam WAIT =8'b0000_0001, //上电等待20usPRECH=8'b0000_0010,//预充电AREF =8'b0000_0100,//刷新MRS =8'b0000_1000,//模式设置IDLE =8'b0001_0000,//ACTI =8'b0010_0000,//bank+row激活 bank,block都是内部划分块名 WRITE=8'b0100_0000,//bank+列激活+写READ =8'b1000_0000;//bank+列激活+读reg [3:0]cmd;reg [7:0]state;reg [7:0]state_next;wire wait_prech ;wire prech_aref ;wire prech_idle ;wire aref_mrs ;wire aref_idle ;wire mrs_idle ;wire idle_aref ;wire idle_acti ;wire acti_write ;wire acti_read ;wire write_prech;wire read_prech ;reg [15:0] cnt0;wire add_cnt0;wire end_cnt0;reg [9:0]cnt_ref;reg ref_flag;reg [15:0]max; //每个状态cnt0计数最大值reg flag; //PRECH标志信号reg wrrd_flag;reg data_vld_r0; //CL为2的条件下reg data_vld_r1;reg data_vld_r2; //列延时为3的情况下选wire [1:0]bank;wire [12:0]row ;wire [8:0]col ;reg work;wire [15:0] dq_in ;reg [15:0] dq_out;reg dq_en ;assign dq_in=dq; assign dq=dq_en?dq_out:16'bz;assign bank=addr[23:22];assign row =addr[21:9] ;assign col =addr[8:0] ;always@(posedge clk or negedge rst_n ) beginif(!rst_n) beginstate<=WAIT;endelse beginstate<=state_next;endendalways@(*) begincase(state)WAIT :beginif(wait_prech) beginstate_next=PRECH;endelse beginstate_next=state;endend PRECH :beginif( prech_aref) beginstate_next=AREF;endelse if(prech_idle) beginstate_next=IDLE;endelse beginstate_next=state;endend AREF :beginif(aref_mrs) beginstate_next=MRS;endelse if(aref_idle) beginstate_next=IDLE;endelse beginstate_next=state;endend MRS :beginif(mrs_idle) beginstate_next=IDLE;endelse beginstate_next=state;endend IDLE :beginif(idle_aref) begin//优先state_next=AREF;endelse if(idle_acti) beginstate_next=ACTI;endelse beginstate_next=state;endend ACTI :beginif(acti_write) beginstate_next=WRITE;endelse if(acti_read) beginstate_next=READ;endelse beginstate_next=state;endend WRITE :beginif(write_prech) beginstate_next=PRECH;endelse beginstate_next=state;endend READ :beginif(read_prech) beginstate_next=PRECH;endelse beginstate_next=state;endend default:state_next=IDLE;endcaseendassign wait_prech = state== WAIT && ( end_cnt0 );assign prech_aref = state== PRECH && ( flag==0 && end_cnt0 );assign prech_idle = state== PRECH && ( flag==1 && end_cnt0 );assign aref_mrs = state== AREF && ( flag==0 && end_cnt0);assign aref_idle = state== AREF && ( flag==1 && end_cnt0);assign mrs_idle = state== MRS && ( end_cnt0 ); assign idle_aref = state== IDLE && ( ref_flag==1);assign idle_acti = state== IDLE && ( work &&ref_flag==0 ); //ref_flag==1,done就不能发出信号,en就不能为1assign acti_write = state== ACTI && ( wrrd_flag==0 && end_cnt0 ); assign acti_read = state== ACTI && ( wrrd_flag==1 && end_cnt0 );assign write_prech = state== WRITE && ( end_cnt0 ); //数据写完后还要延迟2个clkassign read_prech = state== READ && ( end_cnt0 ); //数据读完都可以立即发下一命令//cnt_maxalways@( * ) begincase(state)WAIT : max =`TIME_WAIT ; //20usPRECH : max =`TIME_TRP ; //预充电20nsAREF : max =`TIME_TRC ; //刷新70nsMRS : max =`TIME_TMRS ; //模式寄存器2clkIDLE : max = 0; //ACTI : max =`TIME_TRCD ; //激活行20ns//2WRITE : max =(`BUSRT_MAX+2) ; //突发长度完+2clk恢复周期 READ : max = `BUSRT_MAX; //突发长度+0clk default:max =0;endcaseendalways@(posedge clk or negedge rst_n) beginif(!rst_n) begincnt0<=0;endelse if(end_cnt0) begincnt0<=0;endelse if(add_cnt0) begincnt0<=cnt0+1'b1;endendassign add_cnt0=state!=IDLE ;assign end_cnt0=add_cnt0 && cnt0==max-1;//flag //预充电下一状态标志//开机到idle后才开始计数always@(posedge clk or negedge rst_n) beginif(!rst_n) beginflag<=0;endelse if(mrs_idle) beginflag<=1; //第一次模式寄存器标志endend//cnt_ref; //定时刷新标志//ref_flag;always@(posedge clk or negedge rst_n) beginif(!rst_n) begincnt_ref<=0;endelse if(cnt_ref==`TIME_REF-1) begin //780周期cnt_ref<=1;endelse if( flag ) begincnt_ref<=cnt_ref+1'b1;endend//刷新标志always@(posedge clk or negedge rst_n) beginif(!rst_n) beginref_flag<=0;endelse if(cnt_ref==`TIME_REF-1) begin //780周期ref_flag<=1;endelse if(state==AREF) beginref_flag<=0;endend//wrrd_flag //读或者写标志always@(posedge clk or negedge rst_n) beginif(!rst_n) beginwrrd_flag<=0;endelse if(wr_en) begin //读写标志信号wrrd_flag<=0;endelse if(rd_en) beginwrrd_flag<=1;endendalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginwork <= 0;endelse if(state==ACTI)begin work <= 0;end else if( wr_en||rd_en )begin work<=1'b1;end end//命令cmdassigncs_n = cmd[3] ;assignras_n= cmd[2] ;assigncas_n= cmd[1] ;assignwe_n = cmd[0] ;always@(posedge clk or negedge rst_n) beginif(!rst_n) begin cmd<=4'b1111;endelse if(wait_prech) begin //预充电 0010 同时外加 地址a10 为1cmd<=`CMD_PREC; //3endelse if(prech_aref) begin //刷新0001cmd<=`CMD_AREF; //7endelse if(aref_mrs) begin //模式寄存器0000cmd<=`CMD_MRS; //2endelse if(idle_aref) begin//刷新0001cmd<=`CMD_AREF; //7endelse if(idle_acti) begin//激活行0011cmd<=`CMD_ACTI; //3endelse if(acti_write) begin //写 0100cmd<=`CMD_WRITE; endelse if(acti_read) begin//读 0101cmd<=`CMD_RDAD; endelse if(read_prech||write_prech) begin //预充电0010cmd<=`CMD_PREC; //同时外加 地址a10 为1endelse begin//空命令cmd<=`CMD_NOP; endend// sclk//ckealways@(posedge clk or negedge rst_n) beginif(!rst_n) begincke<=0;endelse begincke<=1;endendassign sclk=~clk;//~sclk延迟半个周期 //ba_addr //sdram_addr always@(posedge clk or negedge rst_n) beginif(!rst_n) begin ba_addr <= 2'b00;sdram_addr<= 13'b0;endelse if(wait_prech||write_prech||read_prech) begin//预充电模式sdram_addr<=13'd1024; //a10为1,所有预充电所有,不需要bank地址endelse if(aref_mrs) beginba_addr <= 2'b00;sdram_addr<={ 3'b000 ,`OP , 2'b00 , `CAS_LTY , `BT , `BL}; //模式寄存器endelse if(idle_acti) begin //激活ba_addr <= bank;//banksdram_addr<=row; //行地址endelse if(acti_write||acti_read) beginba_addr <= bank;//banksdram_addr<= col; //列地址endend//done反馈信号always@(*) beginif(state!=IDLE||ref_flag ||work==1'b1 ) begin //780周期计数done=3'b100 ; //正在工作中 ,不能给读写命令endelse begindone=3'b000;endend//写数据//dout_sdram_reqalways@( posedge clk or negedge rst_n ) beginif(!rst_n) begindout_sdram_req<=0;endelse if(state==WRITE && cnt0==max-4) begindout_sdram_req<=0; //提示下下个周期停止给写数据,数据时序输出,打一拍endelse if(state== ACTI&&wrrd_flag==0 && add_cnt0 && cnt0==max-2) begin dout_sdram_req<=1'b1; //提示下下个周期给写数据+列地址,数据时序输出,打一拍endendalways@( posedge clk or negedge rst_n ) beginif(!rst_n) begindq_out<=0;endelse begin dq_out<=dout_sdram; endend //读数据always@(posedge clk or negedge rst_n) beginif(!rst_n) begindata_vld_r0<=0;endelse if(read_prech) begindata_vld_r0<=0;endelse if(acti_read ) begin data_vld_r0<=1; //突发写 ,经过CL列延迟个周期后,出数据endend//组合输出always@(*) beginif(!rst_n) begindata_ctrl=0;endelse begindata_ctrl=dq_in;endendalways@(posedge clk or negedge rst_n) begin if(!rst_n) begindata_ctrl_vld <=0;data_vld_r1<=0;endelse begindata_vld_r1<=data_vld_r0;//data_vld_r2<=data_vld_r1; //列延时为3的情况下选data_ctrl_vld<=data_vld_r1;endend// dq_en /只在写的状态下拉高always@(posedge clk or negedge rst_n) beginif(!rst_n) begindq_en<=0;endelse if(acti_write) begin dq_en<=1;endelse if(state==WRITE&&cnt0==max-3 ) begin dq_en<=0;endendalways@(posedge clk or negedge rst_n) beginif(!rst_n) begindqm_out<=0;endelse if(state==WRITE&&cnt0==max-3 ) begin//写后要拉高dqm_outdqm_out<=2'b11;endelse if(state!=WRITE) begindqm_out<=0;endendendmodule
VGA显示模块
VGA显示模块`include "param.v"module vga_itfc(input clk,input rst_n ,input [15:0] din,input din_vld , //数据有效output regreq,//请求数据信号output reg [15:0]dout_rgb,output reghsync ,output regvhync );reg [11:0] cnt_h; //行计数器wireadd_cnt_h;wireend_cnt_h;reg [9:0]cnt_v; //场计数器wireadd_cnt_v;wireend_cnt_v;reg h_vld ; //行有效数据reg v_vld ; //场有效数据reg rdreq ;wirewrreq ;wireempty ;wirefull ;wire [15:0] q ;wire [9:0] usedw ;// cnt_h always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_h <= 0;end else if(add_cnt_h)begin if(end_cnt_h)begin cnt_h <= 0;endelse begin cnt_h <= cnt_h + 1'b1;end endend assign add_cnt_h = 1'b1; //上电即开始assign end_cnt_h = add_cnt_h && cnt_h ==`H_TP-1 ; //1650always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_v <= 0;end else if(add_cnt_v)begin if(end_cnt_v)begin cnt_v <= 0;endelse begin cnt_v <= cnt_v + 1'b1;end endend assign add_cnt_v = end_cnt_h;assign end_cnt_v = add_cnt_v && cnt_v == `V_TP-1; //750 //h_vld行有效数据always @(posedge clk or negedge rst_n)begin if(!rst_n)beginh_vld <= 0;end else if(add_cnt_h && cnt_h==(`H_SW+`H_BP-1) )begin //h_vld<=1'b1; end else if(add_cnt_h&&cnt_h== (`H_SW +`H_BP +`H_AP-1) )begin h_vld<=0; end end //v_vldalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginv_vld <= 0;end else if(add_cnt_v &&cnt_v== (`V_SW +`V_BP-1 ) )begin v_vld<=1'b1;end else if(add_cnt_v && cnt_v==(`V_SW +`V_BP +`V_AP-1) )begin v_vld<=0; end end//rdreqalways @( * )begin if( v_vld && h_vld && empty==0 )begin rdreq=1'b1;end else begin rdreq=0; end end//dout_rgbalways @(* )begin if(!rst_n)begindout_rgb = 0;end else if( rdreq )begin dout_rgb=q;end else begindout_rgb=0;endend //hsync always @(posedge clk or negedge rst_n)begin if(!rst_n)beginhsync <= 0;end else if( add_cnt_h && cnt_h== `H_SW-1 )begin hsync<=1'b1;end else if(end_cnt_h)begin hsync<=0;end end //vhync always @(posedge clk or negedge rst_n)begin if(!rst_n)beginvhync <= 0;end else if( add_cnt_v && cnt_v== `V_SW-1 )begin vhync<=1'b1;end else if(end_cnt_v)begin vhync<=0;end end buffer u_buffer(.clock ( clk ) ,.data ( din ) ,.rdreq ( rdreq) ,.wrreq ( wrreq) ,.empty ( empty) ,.full ( full ) ,.q( q) ,.usedw ( usedw) );//req向sdram读数据always @(posedge clk or negedge rst_n)begin if(!rst_n)beginreq <= 0;end else if( usedw<200 )begin req<=1'b1;end else if(usedw>900)begin req<=0; end endassign wrreq= (full==0) ? din_vld : 1'b0 ;endmodule
参数模块
/**************************************sdram************************************///sdram工作参数//`define BUSRT_1//`define BUSRT_2//`define BUSRT_4//`define BUSRT_8`define BUSRT_512 //突发长度`ifdef BUSRT_1`define BUSRT_MAX 1`define BL 3'b000//A0-A2`elsif BUSRT_2`define BUSRT_MAX 2`define BL 3'b001`elsif BUSRT_4`define BUSRT_MAX 4`define BL 3'b010`elsif BUSRT_8`define BUSRT_MAX 8`define BL 3'b011`define FIFO_L 300 //写FIFO的数量低值`define FIFO_H 1500 //写FIFO的数量高值`elsif BUSRT_512`define BUSRT_MAX 512`define BL 3'b111`define FIFO_L 300 //写FIFO的数量低值`define FIFO_H 1500 //写FIFO的数量高值`endif //连续突发//`define BT 1//A3`define BT1'b0//突发写/单写//`define OP 1`define OP1'b0 //A9`define CAS_LTY1 //列选 延迟//`define CAS_LTY2`ifdef CAS_LTY1 //列选通延迟设置 延迟 A4-A6`define CAS_LTY 3'b010`define CL 2 `elsif CAS_LTY2`define CAS_LTY 3'b011`define CL 3 `endif`define ADDR_MAX 16777215 //时间参数`defineTIME_TRC 7//刷新命令生效时间 `defineTIME_TRP 3//预充电时间`defineTIME_TMRS 2 //2个clk时钟周期 模式寄存器`defineTIME_WAIT 20000//上电等待20us `defineTIME_TRCD 3 //激活命令`defineTIME_REF 780//自动刷新间隔 //cmd命令参数 cs_n使能,ras_n行选,cas_n列选,we_n写使能`defineCMD_WRITE 4'b0100 //写 `defineCMD_RDAD 4'b0101 //读`defineCMD_ACTI 4'b0011 //激活`defineCMD_NOP 4'b0111 //空操作`defineCMD_MRS 4'b0000 //模式寄存器`defineCMD_AREF 4'b0001 //自刷新 A10==1 `defineCMD_PREC 4'b0010 //预充电 所有bank /**************************************图像采集模块************************************/`define ROW_MAX 720 //一共720行`define COL_MAX 2560 //一行1280个像素,一个像素2字节`define FPS 921600 //一帧数据有多少个数据9216`define BUSRT_NUM 1800//突发1800次,一帧数据完/**************************************vga端口模块************************************///vga端口模块 1280*720像素`defineH_FP 110 //行前沿`defineH_SW 40//行同步脉冲`defineH_BP 220 //行后沿`defineH_AP 1280 //行有效像素`defineH_TP 1650 //行总时序`defineV_FP 5//场前沿 `defineV_SW 5//场同步脉冲 `defineV_BP 20//场后沿 `defineV_AP 720 //场有效像素 `defineV_TP 750 //场总时序/**************************************I2C端口模块************************************///I2C端口模块`define CMD_EN 5'b000_01 //[0]表示为开始工作`define CMD_RDST 5'b000_10 //[1]表示发起始位`define CMD_WR 5'b001_00 //[2] 表示写数据 `define CMD_RD 5'b010_00 //[3]表示接收数据`define CMD_NO 5'b100_00 //[4] 表示发送停止位不应答
备注:fifo_ctrl模块中调用了两个FIFO深度为1024,宽度一个为18bit(包含sop,eop),一个16bit;
VGA显示模块调用FIFO深度为1024,宽度为16bit;
另外调用pll生成时钟24M(摄像头配置输输出时钟)、75M(VGA显示模块输出时钟)、100M(SDRAM时钟)
如果觉得《基于intel(altera)FPGA OV5640摄像头 图像采集系统(完整代码)》对你有帮助,请点赞、收藏,并留下你的观点哦!