基于intel(altera)FPGA OV5640摄像头 图像采集系统(完整代码)
此项目一共分为摄像头配置模块,图像采集模块,异步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 ;
wire add_cnt ;
wire end_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;end
end
always @(*)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;endcase
end
assign 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 end
end
assign add_cnt = state==WAIT|| (state==WRITE&&cnt_flag);
assign end_cnt = add_cnt && cnt==( (state==WAIT) ? (WAIT_TIME-1 ): (MAX+1) ) ;
//data_en
always @(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;end
end
assign 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 720226: 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
);
//高位先发msb
parameter 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;
wire idle_start ;
wire start_write ;
wire write_actack ;
wire actack_start ;
wire actack_write ;
wire actack_read ;
wire read_sendack ;
wire sendack_read ;
wire sendack_stop ;
wire stop_idle ;
wire actack_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;end
end
always@(*) 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;endcase
end
assign 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;end
end
assign 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;end
end
assign 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到了才为零
///max
always@( * ) 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 end
end
//scl
always@(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;end
end
//ack
always@(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;end
end
//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;end
end
//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;end
end
//sda_in data_flag
always@(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;end
end
//done
always@(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;end
end
//rd_out rd_out_vld
always@(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;end
end
endmodule
IIC控制模块
//IIC控制模块
`include "param.v"
module sio_drive(input clk ,input rst_n ,output reg redy , //写数据进来要求。1代表空闲中,0代表工作中input data_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 , //端口模块传回数据input i2c_din_vld , input done , //上次数据传输完成标志 input ack //端口模块
);
//
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 per
always@(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; //读数据end
end
//redy
always@(* ) beginif( per==0 ) begin //per==1 代表空闲状态redy=1'b1 ;endelse beginredy=0 ;end
end
//no_ack
always@(posedge clk or negedge rst_n) beginif(!rst_n) beginno_ack<=0;endelse if(cnt_byte>0&&cnt_byte<MAX) beginno_ack<=ack; endelse beginno_ack<=0; end
end
//cnt_byte
always@(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;end
end
assign add_cnt_byte= done==1 ; //提前一个周期给反馈信号
assign end_cnt_byte=(add_cnt_byte && cnt_byte==MAX)||no_ack ; //最后一个数据发完再接收一个done
//MAX
always@(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 ;end
end
//输出给端口模块数据data_i2c
always@(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;endendcaseend
endassign 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; //行计数器 2560
wire add_cnt_col;
wire end_cnt_col;reg [9:0] cnt_row; //场计数器 720
wire 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;end
end
always @(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 end
end
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 end
end
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
end
always @(*)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 (input clk , //100minput rst_n ,input clk_in , //84minput clk_out , //75m//采集模块数据input [15:0] din_pixel ,input din_pixel_vld ,input sop ,input eop , //vga模块input req_vga ,output reg[15:0] data_vga ,output reg data_vga_vld ,//wrfifo 读出给sdram数据output [11:0] wrfifo_rdusedw ,output [17:0] wrfifo_q , input wrfifo_rdreq ,//rdfifo sdram写进数据input [15:0] rdfifo_data ,input rdfifo_wrreq , output reg sop_vld , //一帧数据丢失,不能读wrfifo数据output [10:0] rdfifo_wrusedw ,output reg[23:0] cnt_num ,output reg[23:0] cnt_vld
);//wrfifo
reg [17:0] wrfifo_data ;
//reg eop_vld ;
reg fail ;
reg wrfifo_wrreq ;
wire wrfifo_wrfull ;
//rdfifo
wire rdfifo_rdempty ;
wire [15:0] rdfifo_q ;
wire rdfifo_rdreq ;
reg rdreq_flag ;
wire [11:0] wrfifo_wusedw ;
reg sop_flag ;
wire [17:0] wrfifo_qout ;//wrFIFO输出wrfifo u_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 reg sop ,
output reg eop
);//状态参数
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;
wire add_addr_wr;
wire end_addr_wr;reg [21:0] addr_rd;
wire add_addr_rd;
wire end_addr_rd;reg flag ; reg [1:0] wr_bank ;
reg [1:0] rd_bank ;
reg seop ;
reg bank_turn ;//bank翻转记录
//打拍reg sop_flag;wire nedge;wire pedge;reg sop_vld_r0;reg sop_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; end
end//**************************状态机**********************************************/
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;endcase
end
assign 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 end
end
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 end
end
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;end
end
// 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; //翻转后end
end
//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;end
endassign 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;endend
always @(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 ;end
end //**************************输出*********************************************/
//addr_iftc
always @(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(input clk ,input rst_n ,input wr_en , //写使能 .同时地址数据有效input rd_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 reg data_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;end
end
always@(*) 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;endcase
end
assign 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就不能为1
assign 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个clk
assign read_prech = state== READ && ( end_cnt0 ); //数据读完都可以立即发下一命令
//cnt_max
always@( * ) 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;endcase
endalways@(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;end
end
assign 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; //第一次模式寄存器标志end
end
//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;end
end
//刷新标志
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;end
end
//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;end
endalways @(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//命令cmd
assign cs_n = cmd[3] ;
assign ras_n= cmd[2] ;
assign cas_n= cmd[1] ;
assign we_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; end
end// 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; //列地址end
end//done反馈信号
always@(*) beginif(state!=IDLE||ref_flag ||work==1'b1 ) begin //780周期计数done=3'b100 ; //正在工作中 ,不能给读写命令endelse begindone=3'b000;end
end
//写数据
//dout_sdram_req
always@( 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; //提示下下个周期给写数据+列地址,数据时序输出,打一拍end
end
always@( posedge clk or negedge rst_n ) beginif(!rst_n) begindq_out<=0;endelse begin dq_out<=dout_sdram; end
end //读数据
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列延迟个周期后,出数据end
end//组合输出
always@(*) beginif(!rst_n) begindata_ctrl=0;endelse begindata_ctrl=dq_in;end
endalways@(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;end
end// 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;end
endalways@(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;end
endendmodule
VGA显示模块
VGA显示模块`include "param.v"
module vga_itfc(input clk ,input rst_n ,input [15:0] din ,input din_vld , //数据有效output reg req ,//请求数据信号output reg [15:0]dout_rgb,output reg hsync ,output reg vhync
);reg [11:0] cnt_h; //行计数器
wire add_cnt_h;
wire end_cnt_h;reg [9:0] cnt_v; //场计数器
wire add_cnt_v;
wire end_cnt_v;reg h_vld ; //行有效数据
reg v_vld ; //场有效数据reg rdreq ;
wire wrreq ;
wire empty ;
wire full ;
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_vld
always @(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//rdreq
always @( * )begin if( v_vld && h_vld && empty==0 )begin rdreq=1'b1;end else begin rdreq=0; end
end
//dout_rgb
always @(* )begin if(!rst_n)begindout_rgb = 0;end else if( rdreq )begin dout_rgb=q;end else begindout_rgb=0;end
end //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
end
assign 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 BT 1'b0//突发写/单写//`define OP 1`define OP 1'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 //时间参数`define TIME_TRC 7 //刷新命令生效时间 `define TIME_TRP 3 //预充电时间`define TIME_TMRS 2 //2个clk时钟周期 模式寄存器`define TIME_WAIT 20000 //上电等待20us `define TIME_TRCD 3 //激活命令`define TIME_REF 780 //自动刷新间隔 //cmd命令参数 cs_n使能,ras_n行选,cas_n列选,we_n写使能`define CMD_WRITE 4'b0100 //写 `define CMD_RDAD 4'b0101 //读`define CMD_ACTI 4'b0011 //激活`define CMD_NOP 4'b0111 //空操作`define CMD_MRS 4'b0000 //模式寄存器`define CMD_AREF 4'b0001 //自刷新 A10==1 `define CMD_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像素
`define H_FP 110 //行前沿
`define H_SW 40 //行同步脉冲
`define H_BP 220 //行后沿
`define H_AP 1280 //行有效像素
`define H_TP 1650 //行总时序`define V_FP 5 //场前沿
`define V_SW 5 //场同步脉冲
`define V_BP 20 //场后沿
`define V_AP 720 //场有效像素
`define V_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摄像头 图像采集系统(完整代码)相关推荐
- STM32H750获取OV5640摄像头图像及上位机解码(一维码二维码)
STM32H750获取OV5640摄像头图像及上位机解码(一维码&二维码) 1. 目的 针对静止拍摄图像场景,实现STM32H750对500万像素OV5640摄像头进行图像捕获,并通过串口将数 ...
- 煤炭价格预测:基于matlab的时间序列分析(主要流程+完整代码)
基于matlab的时间序列分析(主要流程+完整代码) 案例简介 时间序列简介 平稳时间序列{xt} ARIMA=AR+MA+INTEGRATER 实例应用 数据平稳化 模型选择--确定p.q ACF/ ...
- 太空人时间HTML,基于JavaScript实现网红太空人表盘的完整代码
一.效果展示 用javascript写的一个太空人表盘. 二.源代码 html代码 太空人表盘 function WatchMeter() { // 初始化dom this._initDom() // ...
- 基于intel x86+fpga智能驾驶舱和高级驾驶辅助系统硬件设计(二)
系统功能架构及各模块功能介绍 智能驾驶舱和高级驾驶辅助系统是一个车载智能终端嵌入式平台,系统是一个能够运行 虚拟化操作系统的软件和硬件的综合体.本文的车载主机包括硬件主控处理器.电源管理芯 片.存储设 ...
- 飞思卡尔比赛K60驱动OLED12864显示摄像头采集的赛道图像,完整代码分享
一.首先采集摄像头图像,由于硬件不同采集方式也不一样,我就不多做说明 二.将采集到的图像进行二值化 三.下面为完整显示函数 备注:大家主需要修改对应的引脚就行(修改初始化和宏定义) led.c文件 # ...
- 基于python的问答对联生成系统 附完整代码 毕业设计
软件标题:智能对联生成系统 b 系统概述 使用项目:智能对联生成系统 软件用途:通过网页端可以获取到根据已有上联只能生成的下联. 开发历史:本项目未曾有前置版本.但在服务器搭建,Tensorflow ...
- 二分类问题:基于BERT的文本分类实践!附完整代码
Datawhale 作者:高宝丽,Datawhale优秀学习者 寄语:Bert天生适合做分类任务.文本分类有fasttext.textcnn等多种方法,但在Bert面前,就是小巫见大巫了. 推荐评论展 ...
- 基于intel soc+fpga智能驾驶舱和高级驾驶辅助系统软件设计(三)
虚拟化操作系统介绍 车载平台有逐渐融合的趋势,车载 SoC 的计算性能和应用快速增长,面临着多种应用在 多个显示子系统融合在一起的问题,这就要求平台运行多个操作系统.虚拟化(Virtualizatio ...
- XILINX FPGA OV5640 摄像头驱动(一)
影像行业是一个值得深耕的方向,废话不多说 先看输入和输出 输入是光照,输出是光照的数字信号 image area:说的是感光矩阵,CMOS图像传感器的最核心部分,接收光照产生电信号的部分.决定了图像质 ...
最新文章
- ListBox滚动条置底
- 3G网络关闭,4G还会远吗?
- C#.NET通用权限管理在DB2数据库上运行的脚本参考 - 序列创建脚本参考
- java面试的基本问题_java面试常见基础问题之一
- string转map集合_[系列文章] Go - Map 集合
- ES6的 super 关键字
- oracle 常用故障,Oracle常见问题解决方案汇总
- .NET Core 3.0之深入源码理解HealthCheck(一)
- mac pip安装mysql_Mac pip安装mysql-python失败
- 您的浏览器由所属组织管理_FB频繁被封,关于多账号环境管理的知识你掌握了吗?...
- spring boot + vue + element-ui全栈开发入门——前端编辑数据对话框
- String 字符串问题一
- 【集群仿真】基于matlab固定翼无人机集群仿真演示平台【含Matlab源码 1497期】
- 时间序列R语言操作——非平稳时间序列变平稳
- android jni javah,JAVAH找不到类(android ndk)
- font-family
- 大数据平台架构有哪些
- 【网络通信 -- 直播】网络通信协议简介 -- RTP 协议的 H264 封包
- 手机内存如何快速自清理
- Linux command – Stressful Application Test
热门文章
- AI | 浅谈AI技术以及其今后发展
- speedoffice(PPT)怎么添加艺术字体
- 为什么显示芒果tv服务器异常怎么办,芒果TV显示MAC认证状态异常怎么办?
- 美团开放配送平台,争食即时配送业务
- Error while building/deploying project xxx
- 计算机科学与工程学院学位,山东科技大学-计算机科学与工程学院
- 思科双核心交换机双出口防火墙配置案例
- 新概念二册 Lesson 28 No parking禁止停车 ( 定语从句)
- 【2020·iSQE】峰会倒计时,日程抢先看!
- Linux命令之fdisk命令