文章目录
- 一、系统框架
- 1.摄像头模块
- 摄像头配置
- 摄像头数据处理
- 2.SDRAM模块
- SDRAM控制模块
- SDRAM读写仲裁
- SDRAM接口
- 读写FIFO
- 3.vga显示模块
- 4.PLL时钟模块
- 二、部分模块实现代码
- 1.摄像头配置
- I2C接口
- 摄像头配置
- 摄像头数据处理
- 2.SDRAM控制模块
- 3.vga模块
- 4.顶层文件
- 三、源码
一、系统框架
首先整个系统由摄像头模块、SDRAM数据缓存模块、vga显示模块、PLL时钟模块以及图像处理模块组成,这里先不用图像处理模块。
1.摄像头模块
摄像头配置
摄像头模块里面负责处理摄像头采集的数据,根据ov5640摄像头手册说明,我们需要先通过I2C协议去配置摄像头相关寄存器的参数。在摄像头上电后需要等待20ms。然后再通过I2C发送设备ID、写地址和数据,其中地址先发送高8位再发送低8位。这里包含摄像头时钟、图像大小、帧率以及其他和图像相关的参数。这里最重要的配置参数就是摄像头的图像分辨率和图像的色彩格式,这里通过配置的分辨率为1280*720,RGB565格式。
摄像头数据处理
根据时序图可以看到,当场同步信号到来后,会跟随着许多HREF数据有效信号,我们在HREF高电平的时候去采集数据即可。
但是摄像头的数据是把16位RGB拆分为高八位和低八位发送的,我们需要通过移位+位拼接的方式把两个8bit数据合并成16bit数据输出。同时为了SDRAM模块更好的识别帧头和帧尾,在图像的第一个像素点以及最后一个像素点的时候分别拉高sop和eop信号,其余像素点这拉低。
1
2
3
4
5
6
7
8
9
10
11
12
13
14always @(posedge clk or negedge rst_n)begin if(~rst_n)begin data <= 0; end else begin data <= {data[7:0],din};//左移 end end assign pixel = data; assign sop = cnt_h == 1 && cnt_v == 0; assign eop = data_eop; assign vld = cnt_h[0];
2.SDRAM模块
SDRAM控制模块
由于摄像头数据时钟(84M)和vga时钟(75M)不一样,为了避免读写数据速度不一致就需要把摄像头的数据进行缓存,常见的缓存可以通过fifo,但是这里的数据量十分庞大,故需要通过SDRAM进行缓存。缓存的方式则是通过乒乓缓存,把SDRAM的两个blank单独作为数据的写入和读出,并在读写完成后切换读写blank,并且需要通过丢帧和复读的操作来保证读写图像的完整性。
SDRAM读写仲裁
由于摄像头的数据输出和vga的数据请求都是源源不断的,此时到底是读还是写SDRAM就需要一个规则约束。首先为了保证写FIFO数据过多或者读FIFO数据过少。当写FIFO数据大于阈值后就会发起一个写请求,当读FIFO的数据低于阈值后就会发起读请求。仲裁机制根据读写请求进行仲裁,如果同一时间只有单独的读或者写请求直接执行即可,如果同时存在两种请求,则会根据上次执行的是读或者写来进行相反的请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26//flag_sel ;//标记上一次操作 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin flag_sel <= 0; end else if(read2done)begin flag_sel <= 1; end else if(write2done)begin flag_sel <= 0; end end //prior_flag ;//优先级标志 0:写优先级高 1:读优先级高 仲裁读、写的优先级 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin prior_flag <= 0; end else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin //突发写优先级高 prior_flag <= 1'b0; end else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin //突发读优先级高 prior_flag <= 1'b1; end end
SDRAM接口
这里直接使用quartus自带的SDRAM接口ip。
需要注意的是SDRAM的blank数据是数据位的最高位和第10位,行地址为第23位到11位,列地址则为低9位当然前提是这里的为4blank13行9列。如图为ip接口部分源码
所以地址需要这样进行拼接
1
2
3
4assign avm_addr = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]} :((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]} :0);
读写FIFO
整个SDRAM模块涉及到摄像头模块到SDRAM模块(慢时钟域到快时钟域),SDRAM数据到vga模块(快时钟域到慢时钟域)的跨时钟域数据同步的问题,这里使用两个异步FIFO来缓存两个跨时钟域的数据。
首先是写FIFO(这里的读写相对于SDRAM)
写FIFO的写使能条件是FIFO非满、输入数据有效、旧的一帧被vga读完且新的一帧到来或者是当前帧数据。
写FIFO的读使能条件是当前需要往SDRAM写数据且FIFO非空,这里通过SDRAM控制模块的仲裁机制决定。
1
2
3assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag); assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest;
然后是读FIFO
读FIFO的写使能是当前把SDRAM读出来的数据有效且FIFO非满
读FIFO的读使能为当前FIFO非空且vga发起数据请求。
1
2
3assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld; assign rfifo_rdreq = ~rfifo_empty & rdreq;
3.vga显示模块
vga显示这边使用1280*780@60HZ参数输出图像,在行场有效区域内通过拉发起请求读取读FIFO的数据内的数据,再输出到屏幕上面。可以参考前面的博客基于FPGA的VGA显示彩条、字符、图片,这里不多描述。
4.PLL时钟模块
整个系统由50M的基准时钟驱动,并且通过PLL生成配置摄像头的驱动时钟(24M)、SDRAM接口驱动时钟(100M),vga驱动时钟(75M)还有输出到SDRAM外设的100M带相位偏移的时钟。
二、部分模块实现代码
1.摄像头配置
I2C接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296`include "param.v" module i2c_intf( input clk , input rst_n , input req , input [3:0] cmd , input [7:0] din , output [7:0] dout , output done , output slave_ack , output i2c_scl , input i2c_sda_i , output i2c_sda_o , output i2c_sda_oe ); //状态机参数定义 localparam IDLE = 7'b000_0001, START = 7'b000_0010, WRITE = 7'b000_0100, RACK = 7'b000_1000, READ = 7'b001_0000, SACK = 7'b010_0000, STOP = 7'b100_0000; //信号定义 reg [6:0] state_c ; reg [6:0] state_n ; reg [8:0] cnt_scl ;//产生i2c时钟 wire add_cnt_scl ; wire end_cnt_scl ; reg [3:0] cnt_bit ;//传输数据 bit计数器 wire add_cnt_bit ; wire end_cnt_bit ; reg [3:0] bit_num ; reg scl ;//输出寄存器 reg sda_out ; reg sda_out_en ; reg [7:0] rx_data ; reg rx_ack ; reg [3:0] command ; reg [7:0] tx_data ;//发送数据 wire idle2start ; wire idle2write ; wire idle2read ; wire start2write ; wire start2read ; wire write2rack ; wire read2sack ; wire rack2stop ; wire sack2stop ; wire rack2idle ; wire sack2idle ; wire stop2idle ; //状态机 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin state_c <= IDLE ; end else begin state_c <= state_n; end end always @(*) begin case(state_c) IDLE :begin if(idle2start) state_n = START ; else if(idle2write) state_n = WRITE ; else if(idle2read) state_n = READ ; else state_n = state_c ; end START :begin if(start2write) state_n = WRITE ; else if(start2read) state_n = READ ; else state_n = state_c ; end WRITE :begin if(write2rack) state_n = RACK ; else state_n = state_c ; end RACK :begin if(rack2stop) state_n = STOP ; else if(rack2idle) state_n = IDLE ; else state_n = state_c ; end READ :begin if(read2sack) state_n = SACK ; else state_n = state_c ; end SACK :begin if(sack2stop) state_n = STOP ; else if(sack2idle) state_n = IDLE ; else state_n = state_c ; end STOP :begin if(stop2idle) state_n = IDLE ; else state_n = state_c ; end default : state_n = IDLE ; endcase end assign idle2start = state_c==IDLE && (req && (cmd&`CMD_START)); assign idle2write = state_c==IDLE && (req && (cmd&`CMD_WRITE)); assign idle2read = state_c==IDLE && (req && (cmd&`CMD_READ )); assign start2write = state_c==START && (end_cnt_bit && (command&`CMD_WRITE)); assign start2read = state_c==START && (end_cnt_bit && (command&`CMD_READ )); assign write2rack = state_c==WRITE && (end_cnt_bit); assign read2sack = state_c==READ && (end_cnt_bit); assign rack2stop = state_c==RACK && (end_cnt_bit && (command&`CMD_STOP )); assign sack2stop = state_c==SACK && (end_cnt_bit && (command&`CMD_STOP )); assign rack2idle = state_c==RACK && (end_cnt_bit && (command&`CMD_STOP ) == 0); assign sack2idle = state_c==SACK && (end_cnt_bit && (command&`CMD_STOP ) == 0); assign stop2idle = state_c==STOP && (end_cnt_bit ); //计数器 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_scl <= 0; end else if(add_cnt_scl) begin if(end_cnt_scl) cnt_scl <= 0; else cnt_scl <= cnt_scl+1 ; end end assign add_cnt_scl = (state_c != IDLE); assign end_cnt_scl = add_cnt_scl && cnt_scl == (`SCL_PERIOD)-1 ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_bit <= 0; end else if(add_cnt_bit) begin if(end_cnt_bit) cnt_bit <= 0; else cnt_bit <= cnt_bit+1 ; end end assign add_cnt_bit = (end_cnt_scl); assign end_cnt_bit = add_cnt_bit && cnt_bit == (bit_num)-1 ; always @(*)begin if(state_c == WRITE | state_c == READ) begin bit_num = 8; end else begin bit_num = 1; end end //command always @(posedge clk or negedge rst_n)begin if(~rst_n)begin command <= 0; end else if(req)begin command <= cmd; end end //tx_data always @(posedge clk or negedge rst_n)begin if(~rst_n)begin tx_data <= 0; end else if(req)begin tx_data <= din; end end //scl always @(posedge clk or negedge rst_n)begin if(~rst_n)begin scl <= 1'b1; end else if(idle2start | idle2write | idle2read)begin//开始发送时,拉低 scl <= 1'b0; end else if(add_cnt_scl && cnt_scl == `SCL_HALF-1)begin scl <= 1'b1; end else if(end_cnt_scl && ~stop2idle)begin scl <= 1'b0; end end //sda_out always @(posedge clk or negedge rst_n)begin if(~rst_n)begin sda_out <= 1'b1; end else if(state_c == START)begin //发起始位 if(cnt_scl == `LOW_HLAF)begin //时钟低电平时拉高sda总线 sda_out <= 1'b1; end else if(cnt_scl == `HIGH_HALF)begin //时钟高电平时拉低sda总线 sda_out <= 1'b0; //保证从机能检测到起始位 end end else if(state_c == WRITE && cnt_scl == `LOW_HLAF)begin //scl低电平时发送数据 并串转换 sda_out <= tx_data[7-cnt_bit]; end else if(state_c == SACK && cnt_scl == `LOW_HLAF)begin //发应答位 sda_out <= (command&`CMD_STOP)?1'b1:1'b0; end else if(state_c == STOP)begin //发停止位 if(cnt_scl == `LOW_HLAF)begin //时钟低电平时拉低sda总线 sda_out <= 1'b0; end else if(cnt_scl == `HIGH_HALF)begin //时钟高电平时拉高sda总线 sda_out <= 1'b1; //保证从机能检测到停止位 end end end //sda_out_en 总线输出数据使能 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin sda_out_en <= 1'b0; end else if(idle2start | idle2write | read2sack | rack2stop)begin sda_out_en <= 1'b1; end else if(idle2read | start2read | write2rack | stop2idle)begin sda_out_en <= 1'b0; end end //rx_data 接收读入的数据 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin rx_data <= 0; end else if(state_c == READ && cnt_scl == `HIGH_HALF)begin rx_data[7-cnt_bit] <= i2c_sda_i; //串并转换 end end //rx_ack always @(posedge clk or negedge rst_n)begin if(~rst_n)begin rx_ack <= 1'b1; end else if(state_c == RACK && cnt_scl == `HIGH_HALF)begin rx_ack <= i2c_sda_i; end end //输出信号 assign i2c_scl = scl ; assign i2c_sda_o = sda_out ; assign i2c_sda_oe = sda_out_en ; assign dout = rx_data; assign done = rack2idle | sack2idle | stop2idle; assign slave_ack = rx_ack; endmodule
摄像头配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452`include "param.v" module cmos_config( input clk , input rst_n , //i2c_master output req , output [3:0] cmd , output [7:0] dout , input done , output config_done ); //定义参数 localparam WAIT = 4'b0001,//上电等待20ms IDLE = 4'b0010, WREQ = 4'b0100,//发写请求 WRITE = 4'b1000;//等待一个字节写完 parameter DELAY = 1000_000;//上电延时20ms开始配置 //信号定义 reg [3:0] state_c ; reg [3:0] state_n ; reg [19:0] cnt0 ; wire add_cnt0/* synthesis syn_keep*/ ; wire end_cnt0/* synthesis syn_keep*/ ; reg [1:0] cnt1 ; wire add_cnt1/* synthesis syn_keep*/ ; wire end_cnt1/* synthesis syn_keep*/ ; reg config_flag ;//1:表示在配置摄像头 0:表示配置完成 reg [23:0] lut_data ; reg tran_req ; reg [3:0] tran_cmd ; reg [7:0] tran_dout ; wire wait2idle ; wire idle2wreq ; wire write2wreq ; wire write2idle ; //状态机 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin state_c <= WAIT; end else begin state_c <= state_n; end end always @(*)begin case(state_c) WAIT :begin if(wait2idle) state_n = IDLE; else state_n = state_c; end IDLE :begin if(idle2wreq) state_n = WREQ; else state_n = state_c; end WREQ :state_n = WRITE; WRITE :begin if(write2wreq) state_n = WREQ; else if(write2idle) state_n = IDLE; else state_n = state_c; end default:state_n = IDLE; endcase end assign wait2idle = state_c == WAIT && end_cnt0; assign idle2wreq = state_c == IDLE && config_flag; assign write2wreq = state_c == WRITE && done && ~end_cnt1; assign write2idle = state_c == WRITE && end_cnt1; //计数器 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt0 <= 0; end else if(add_cnt0)begin if(end_cnt0) cnt0 <= 0; else cnt0 <= cnt0 + 1; end end assign add_cnt0 = state_c == WAIT || state_c == WRITE && end_cnt1; assign end_cnt0 = add_cnt0 && cnt0 == ((state_c == WAIT)?(DELAY-1):(`REG_NUM-1)); always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt1 <= 0; end else if(add_cnt1)begin if(end_cnt1) cnt1 <= 0; else cnt1 <= cnt1 + 1; end end assign add_cnt1 = state_c == WRITE && done; assign end_cnt1 = add_cnt1 && cnt1 == 4-1; //config_flag always @(posedge clk or negedge rst_n)begin if(~rst_n)begin config_flag <= 1'b1; end else if(config_flag & end_cnt0 & state_c != WAIT)begin //所有寄存器配置完,flag拉低 config_flag <= 1'b0; end end //输出寄存器 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin tran_req <= 0; tran_cmd <= 0; tran_dout <= 0; end else if(state_c == WREQ)begin case(cnt1) 0:begin tran_req <= 1; tran_cmd <= {`CMD_START | `CMD_WRITE}; tran_dout <= `WR_ID; end 1:begin tran_req <= 1; tran_cmd <= `CMD_WRITE; tran_dout <= lut_data[23:16]; end 2:begin tran_req <= 1; tran_cmd <= `CMD_WRITE; tran_dout <= lut_data[15:8]; end 3:begin tran_req <= 1; tran_cmd <= {`CMD_STOP | `CMD_WRITE}; tran_dout <= lut_data[7:0]; end default:tran_req <= 0; endcase end else begin tran_req <= 0; tran_cmd <= 0; tran_dout <= 0; end end //输出 assign config_done = ~config_flag; assign req = tran_req; assign cmd = tran_cmd; assign dout = tran_dout; //lut_data always@(*)begin case(cnt0) //15fps VGA YUV output // 24MHz input clock, 84MHz PCLK 0 :lut_data = {24'h3103_11}; // system clock from pad, bit[1] 1 :lut_data = {24'h3008_82}; // software reset, bit[7] 2 :lut_data = {24'h3008_42}; // software power down, bit[6] 3 :lut_data = {24'h3103_03}; // system clock from PLL, bit[1] 4 :lut_data = {24'h3017_ff}; // FREX, Vsync, HREF, PCLK, D[9:6] output enable 5 :lut_data = {24'h3018_ff}; // D[5:0], GPIO[1:0] output enable 6 :lut_data = {24'h3034_1a}; // MIPI 10-bit 7 :lut_data = {24'h3037_13}; // PLL root divider, bit[4], PLL pre-divider, bit[3:0] 8 :lut_data = {24'h3108_01}; // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2] 9 :lut_data = {24'h3630_36};//SCLK root divider, bit[1:0] 10 :lut_data = {24'h3631_0e}; 11 :lut_data = {24'h3632_e2}; 12 :lut_data = {24'h3633_12}; 13 :lut_data = {24'h3621_e0}; 14 :lut_data = {24'h3704_a0}; 15 :lut_data = {24'h3703_5a}; 16 :lut_data = {24'h3715_78}; 17 :lut_data = {24'h3717_01}; 18 :lut_data = {24'h370b_60}; 19 :lut_data = {24'h3705_1a}; 20 :lut_data = {24'h3905_02}; 21 :lut_data = {24'h3906_10}; 22 :lut_data = {24'h3901_0a}; 23 :lut_data = {24'h3731_12}; 24 :lut_data = {24'h3600_08}; // VCM control 25 :lut_data = {24'h3601_33}; // VCM control 26 :lut_data = {24'h302d_60}; // system control 27 :lut_data = {24'h3620_52}; 28 :lut_data = {24'h371b_20}; 29 :lut_data = {24'h471c_50}; 30 :lut_data = {24'h3a13_43}; // pre-gain = 1.047x 31 :lut_data = {24'h3a18_00}; // gain ceiling 32 :lut_data = {24'h3a19_f8}; // gain ceiling = 15.5x 33 :lut_data = {24'h3635_13}; 34 :lut_data = {24'h3636_03}; 35 :lut_data = {24'h3634_40}; 36 :lut_data = {24'h3622_01}; // 50/60Hz detection 50/60Hz 灯光条纹过滤 37 :lut_data = {24'h3c01_34}; // Band auto, bit[7] 38 :lut_data = {24'h3c04_28}; // threshold low sum 39 :lut_data = {24'h3c05_98}; // threshold high sum 40 :lut_data = {24'h3c06_00}; // light meter 1 threshold[15:8] 41 :lut_data = {24'h3c07_08}; // light meter 1 threshold[7:0] 42 :lut_data = {24'h3c08_00}; // light meter 2 threshold[15:8] 43 :lut_data = {24'h3c09_1c}; // light meter 2 threshold[7:0] 44 :lut_data = {24'h3c0a_9c}; // sample number[15:8] 45 :lut_data = {24'h3c0b_40}; // sample number[7:0] 46 :lut_data = {24'h3810_00}; // Timing Hoffset[11:8] 47 :lut_data = {24'h3811_10}; // Timing Hoffset[7:0] 48 :lut_data = {24'h3812_00}; // Timing Voffset[10:8] 49 :lut_data = {24'h3708_64}; 50 :lut_data = {24'h4001_02}; // BLC start from line 2 51 :lut_data = {24'h4005_1a}; // BLC always update 52 :lut_data = {24'h3000_00}; // enable blocks 53 :lut_data = {24'h3004_ff}; // enable clocks 54 :lut_data = {24'h300e_58}; //MIPI power down,DVP enable 55 :lut_data = {24'h302e_00}; 56 :lut_data = {24'h4300_61}; // RGB, 57 :lut_data = {24'h501f_01}; // ISP RGB 58 :lut_data = {24'h440e_00}; 59 :lut_data = {24'h5000_a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on // AEC target 自动曝光控制 60 :lut_data = {24'h3a0f_30}; // stable range in high 61 :lut_data = {24'h3a10_28}; // stable range in low 62 :lut_data = {24'h3a1b_30}; // stable range out high 63 :lut_data = {24'h3a1e_26}; // stable range out low 64 :lut_data = {24'h3a11_60}; // fast zone high 65 :lut_data = {24'h3a1f_14}; // fast zone low // Lens correction for ? 镜头补偿 66 :lut_data = {24'h5800_23}; 67 :lut_data = {24'h5801_14}; 68 :lut_data = {24'h5802_0f}; 69 :lut_data = {24'h5803_0f}; 70 :lut_data = {24'h5804_12}; 71 :lut_data = {24'h5805_26}; 72 :lut_data = {24'h5806_0c}; 73 :lut_data = {24'h5807_08}; 74 :lut_data = {24'h5808_05}; 75 :lut_data = {24'h5809_05}; 76 :lut_data = {24'h580a_08}; 77 :lut_data = {24'h580b_0d}; 78 :lut_data = {24'h580c_08}; 79 :lut_data = {24'h580d_03}; 80 :lut_data = {24'h580e_00}; 81 :lut_data = {24'h580f_00}; 82 :lut_data = {24'h5810_03}; 83 :lut_data = {24'h5811_09}; 84 :lut_data = {24'h5812_07}; 85 :lut_data = {24'h5813_03}; 86 :lut_data = {24'h5814_00}; 87 :lut_data = {24'h5815_01}; 88 :lut_data = {24'h5816_03}; 89 :lut_data = {24'h5817_08}; 90 :lut_data = {24'h5818_0d}; 91 :lut_data = {24'h5819_08}; 92 :lut_data = {24'h581a_05}; 93 :lut_data = {24'h581b_06}; 94 :lut_data = {24'h581c_08}; 95 :lut_data = {24'h581d_0e}; 96 :lut_data = {24'h581e_29}; 97 :lut_data = {24'h581f_17}; 98 :lut_data = {24'h5820_11}; 99 :lut_data = {24'h5821_11}; 100:lut_data = {24'h5822_15}; 101:lut_data = {24'h5823_28}; 102:lut_data = {24'h5824_46}; 103:lut_data = {24'h5825_26}; 104:lut_data = {24'h5826_08}; 105:lut_data = {24'h5827_26}; 106:lut_data = {24'h5828_64}; 107:lut_data = {24'h5829_26}; 108:lut_data = {24'h582a_24}; 109:lut_data = {24'h582b_22}; 110:lut_data = {24'h582c_24}; 111:lut_data = {24'h582d_24}; 112:lut_data = {24'h582e_06}; 113:lut_data = {24'h582f_22}; 114:lut_data = {24'h5830_40}; 115:lut_data = {24'h5831_42}; 116:lut_data = {24'h5832_24}; 117:lut_data = {24'h5833_26}; 118:lut_data = {24'h5834_24}; 119:lut_data = {24'h5835_22}; 120:lut_data = {24'h5836_22}; 121:lut_data = {24'h5837_26}; 122:lut_data = {24'h5838_44}; 123:lut_data = {24'h5839_24}; 124:lut_data = {24'h583a_26}; 125:lut_data = {24'h583b_28}; 126:lut_data = {24'h583c_42}; 127:lut_data = {24'h583d_ce}; // lenc BR offset // AWB 自动白平衡 128:lut_data = {24'h5180_ff}; // AWB B block 129:lut_data = {24'h5181_f2}; // AWB control 130:lut_data = {24'h5182_00}; // [7:4] max local counter, [3:0] max fast counter 131:lut_data = {24'h5183_14}; // AWB advanced 132:lut_data = {24'h5184_25}; 133:lut_data = {24'h5185_24}; 134:lut_data = {24'h5186_09}; 135:lut_data = {24'h5187_09}; 136:lut_data = {24'h5188_09}; 137:lut_data = {24'h5189_75}; 138:lut_data = {24'h518a_54}; 139:lut_data = {24'h518b_e0}; 140:lut_data = {24'h518c_b2}; 141:lut_data = {24'h518d_42}; 142:lut_data = {24'h518e_3d}; 143:lut_data = {24'h518f_56}; 144:lut_data = {24'h5190_46}; 145:lut_data = {24'h5191_f8}; // AWB top limit 146:lut_data = {24'h5192_04}; // AWB bottom limit 147:lut_data = {24'h5193_70}; // red limit 148:lut_data = {24'h5194_f0}; // green limit 149:lut_data = {24'h5195_f0}; // blue limit 150:lut_data = {24'h5196_03}; // AWB control 151:lut_data = {24'h5197_01}; // local limit 152:lut_data = {24'h5198_04}; 153:lut_data = {24'h5199_12}; 154:lut_data = {24'h519a_04}; 155:lut_data = {24'h519b_00}; 156:lut_data = {24'h519c_06}; 157:lut_data = {24'h519d_82}; 158:lut_data = {24'h519e_38}; // AWB control // Gamma 伽玛曲线 159:lut_data = {24'h5480_01}; //Gamma bias plus on, bit[0] 160:lut_data = {24'h5481_08}; 161:lut_data = {24'h5482_14}; 162:lut_data = {24'h5483_28}; 163:lut_data = {24'h5484_51}; 164:lut_data = {24'h5485_65}; 165:lut_data = {24'h5486_71}; 166:lut_data = {24'h5487_7d}; 167:lut_data = {24'h5488_87}; 168:lut_data = {24'h5489_91}; 169:lut_data = {24'h548a_9a}; 170:lut_data = {24'h548b_aa}; 171:lut_data = {24'h548c_b8}; 172:lut_data = {24'h548d_cd}; 173:lut_data = {24'h548e_dd}; 174:lut_data = {24'h548f_ea}; 175:lut_data = {24'h5490_1d}; // color matrix 色彩矩阵 176:lut_data = {24'h5381_1e}; // CMX1 for Y 177:lut_data = {24'h5382_5b}; // CMX2 for Y 178:lut_data = {24'h5383_08}; // CMX3 for Y 179:lut_data = {24'h5384_0a}; // CMX4 for U 180:lut_data = {24'h5385_7e}; // CMX5 for U 181:lut_data = {24'h5386_88}; // CMX6 for U 182:lut_data = {24'h5387_7c}; // CMX7 for V 183:lut_data = {24'h5388_6c}; // CMX8 for V 184:lut_data = {24'h5389_10}; // CMX9 for V 185:lut_data = {24'h538a_01}; // sign[9] 186:lut_data = {24'h538b_98}; // sign[8:1] // UV adjust UV 色彩饱和度调整 187:lut_data = {24'h5580_06}; // saturation on, bit[1] 188:lut_data = {24'h5583_40}; 189:lut_data = {24'h5584_10}; 190:lut_data = {24'h5589_10}; 191:lut_data = {24'h558a_00}; 192:lut_data = {24'h558b_f8}; 193:lut_data = {24'h501d_40}; // enable manual offset of contrast // CIP 锐化和降噪 194:lut_data = {24'h5300_08}; //CIP sharpen MT threshold 1 195:lut_data = {24'h5301_30}; //CIP sharpen MT threshold 2 196:lut_data = {24'h5302_10}; // CIP sharpen MT offset 1 197:lut_data = {24'h5303_00}; // CIP sharpen MT offset 2 198:lut_data = {24'h5304_08}; // CIP DNS threshold 1 199:lut_data = {24'h5305_30}; // CIP DNS threshold 2 200:lut_data = {24'h5306_08}; // CIP DNS offset 1 201:lut_data = {24'h5307_16}; // CIP DNS offset 2 202:lut_data = {24'h5309_08}; //CIP sharpen TH threshold 1 203:lut_data = {24'h530a_30}; //CIP sharpen TH threshold 2 204:lut_data = {24'h530b_04}; //CIP sharpen TH offset 1 205:lut_data = {24'h530c_06}; //CIP sharpen TH offset 2 206:lut_data = {24'h5025_00}; 207:lut_data = {24'h3008_02}; //wake up from standby,bit[6] // input clock 24Mhz, PCLK 84Mhz 208:lut_data = {24'h3035_21}; // PLL 209:lut_data = {24'h3036_69}; // PLL 210:lut_data = {24'h3c07_07}; // lightmeter 1 threshold[7:0] 211:lut_data = {24'h3820_47}; // flip 212:lut_data = {24'h3821_01}; // no mirror 213:lut_data = {24'h3814_31}; // timing X inc 214:lut_data = {24'h3815_31}; // timing Y inc 215:lut_data = {24'h3800_00}; // HS 216:lut_data = {24'h3801_00}; // HS 217:lut_data = {24'h3802_00}; // VS 218:lut_data = {24'h3803_fa}; // VS 219:lut_data = {24'h3804_0a}; // HW : 220:lut_data = {24'h3805_3f}; // HW : 221:lut_data = {24'h3806_06}; // VH : 222:lut_data = {24'h3807_a9}; // VH : 223:lut_data = {24'h3808_05}; // DVPHO 1280 224:lut_data = {24'h3809_00}; // DVPHO 225:lut_data = {24'h380a_02}; // DVPVO 720 226:lut_data = {24'h380b_d0}; // DVPVO 227:lut_data = {24'h380c_07}; // HTS 228:lut_data = {24'h380d_64}; // HTS 229:lut_data = {24'h380e_02}; // VTS 230:lut_data = {24'h380f_e4}; // VTS 231:lut_data = {24'h3813_04}; // timing V offset 232:lut_data = {24'h3618_00}; 233:lut_data = {24'h3612_29}; 234:lut_data = {24'h3709_52}; 235:lut_data = {24'h370c_03}; 236:lut_data = {24'h3a02_02}; // 60Hz max exposure 237:lut_data = {24'h3a03_e0}; // 60Hz max exposure 238:lut_data = {24'h3a14_02}; // 50Hz max exposure 239:lut_data = {24'h3a15_e0}; // 50Hz max exposure 240:lut_data = {24'h4004_02}; // BLC line number 241:lut_data = {24'h3002_1c}; // reset JFIFO, SFIFO, JPG 242:lut_data = {24'h3006_c3}; // disable clock of JPEG2x, JPEG 243:lut_data = {24'h4713_03}; // JPEG mode 3 244:lut_data = {24'h4407_04}; // Quantization scale 245:lut_data = {24'h460b_37}; 246:lut_data = {24'h460c_20}; 247:lut_data = {24'h4837_16}; // MIPI global timing 248:lut_data = {24'h3824_04}; // PCLK manual divider 249:lut_data = {24'h5001_83}; // SDE on, CMX on, AWB on 250:lut_data = {24'h3503_00}; // AEC/AGC on 251:lut_data = {24'h4740_20}; // VS 1 252:lut_data = {24'h503d_00}; // color bar 253:lut_data = {24'h4741_00}; // default:lut_data = 0; endcase end endmodule
摄像头数据处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126`include "param.v" module capture ( input wire clk, input wire rst_n, input wire vsync, input wire href, input wire [ 7:0 ] din, input wire din_vld, output wire sop, output wire eop, output wire vld, output wire [ 15:0 ] pixel ); // localparam red = 16'd63488; // localparam orange = 16'd64384; // localparam yellow = 16'd65472; // localparam green = 16'd1024; // localparam blue = 16'd31; // localparam indigo = 16'd18448; // localparam purple = 16'd32784; // localparam white = 16'd65503; // localparam black = 16'd0; //信号定义 reg [11:0] cnt_h ; wire add_cnt_h ; wire end_cnt_h ; reg [9:0] cnt_v ; wire add_cnt_v ; wire end_cnt_v ; reg [1:0] vsync_r ;//同步打拍 wire vsync_nedge ;//下降沿 reg flag ;//串并转换标志 reg [15:0] data ; reg data_vld ; reg data_sop ; reg data_eop ; //计数器 always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_h <= 0; end else if(add_cnt_h) begin if(end_cnt_h) cnt_h <= 0; else cnt_h <= cnt_h+1 ; end end assign add_cnt_h = flag & href; assign end_cnt_h = add_cnt_h && cnt_h == (`H_AP << 1)-1; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin cnt_v <= 0; end else if(add_cnt_v) begin if(end_cnt_v) cnt_v <= 0; else cnt_v <= cnt_v+1 ; end end assign add_cnt_v = end_cnt_h; assign end_cnt_v = add_cnt_v && cnt_v == `V_AP-1 ; //vsync同步打拍 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin vsync_r <= 2'b00; end else begin vsync_r <= {vsync_r[0],vsync}; end end assign vsync_nedge = vsync_r[1] & ~vsync_r[0]; always @(posedge clk or negedge rst_n)begin if(~rst_n)begin flag <= 1'b0; end else if(din_vld & vsync_nedge)begin //摄像头配置完成且场同步信号拉低之后开始采集有效数据 flag <= 1'b1; end else if(end_cnt_v)begin //一帧数据采集完拉低 flag <= 1'b0; end end //data always @(posedge clk or negedge rst_n)begin if(~rst_n)begin data <= 0; end else begin data <= {data[7:0],din};//左移 end end //data_sop always @(posedge clk or negedge rst_n)begin if(~rst_n)begin data_sop <= 1'b0; data_eop <= 1'b0; data_vld <= 1'b0; end else begin data_sop <= add_cnt_h && cnt_h == 2-1 && cnt_v == 0; data_eop <= end_cnt_v; data_vld <= add_cnt_h && cnt_h[0] == 1'b0 ; end end assign pixel = data; assign sop = cnt_h == 1 && cnt_v == 0; assign eop = data_eop; assign vld = cnt_h[0]; endmodule //capture
2.SDRAM控制模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350`include"param.v" module sdram_ctrl ( input clk , input clk_in , input clk_out , input rst_n , //数据输入 input [15:0] din ,//摄像头输入像素数据 input din_sop , input din_eop , input din_vld , //数据输出 input rdreq ,//vga的读数据请求 output [15:0] dout ,//输出给vga的数据 output dout_vld ,//输出给vga的数据有效标志 //sdram_interface output avm_write ,//输出给sdram 接口 IP 的写请求 output avm_read ,//输出给sdram 接口 IP 的读请求 output [23:0] avm_addr ,//输出给sdram 接口 IP 的读写地址 output [15:0] avm_wrdata ,//输出给sdram 接口 IP 的写数据 input [15:0] avs_rddata ,//sdram 接口 IP 输入的读数据 input avs_rddata_vld , input avs_waitrequest ); //参数定义 localparam IDLE = 4'b0001, WRITE = 4'b0010, READ = 4'b0100, DONE = 4'b1000; //信号定义 reg [3:0] state_c ; reg [3:0] state_n ; reg [8:0] cnt ;//突发读写计数器 wire add_cnt ; wire end_cnt ; reg [1:0] wr_bank ;//写bank reg [1:0] rd_bank ;//读bank reg [21:0] wr_addr ;//写地址 行地址 + 列地址 wire add_wr_addr ; wire end_wr_addr ; reg [21:0] rd_addr ;//读地址 行地址 + 列地址 wire add_rd_addr ; wire end_rd_addr ; reg change_bank ;//切换bank reg wr_finish ;//一帧数据写完 reg [1:0] wr_finish_r ;//同步到写侧 reg wr_data_flag;//wrfifo写数据的标志 reg wr_flag ; reg rd_flag ; reg flag_sel ; reg prior_flag ; wire idle2write ; wire idle2read ; wire write2done ; wire read2done ; reg [15:0] rd_data ;//rfifo读数据输出 reg rd_data_vld ; wire [17:0] wfifo_data ; wire wfifo_rdreq ; wire wfifo_wrreq ; wire [17:0] wfifo_q ; wire wfifo_empty ; wire [10:0] wfifo_usedw ; wire wfifo_full ; wire [15:0] rfifo_data ; wire rfifo_rdreq ; wire rfifo_wrreq ; wire [15:0] rfifo_q ; wire rfifo_empty ; wire rfifo_full ; wire [10:0] rfifo_usedw ; //状态机 always @(posedge clk or negedge rst_n)begin if(~rst_n)begin state_c <= IDLE; end else begin state_c <= state_n; end end always @(*)begin case(state_c) IDLE :begin if(idle2write) state_n = WRITE; else if(idle2read) state_n = READ; else state_n = state_c; end WRITE :begin if(write2done) state_n = DONE; else state_n = state_c; end READ :begin if(read2done) state_n = DONE; else state_n = state_c; end DONE :state_n = IDLE; default:state_n = IDLE; endcase end assign idle2write = state_c == IDLE && (~prior_flag && wfifo_usedw >= `USER_BL); assign idle2read = state_c == IDLE && prior_flag && rfifo_usedw <= `RD_UT; assign write2done = state_c == WRITE && end_cnt; assign read2done = state_c == READ && end_cnt; //计数器 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt <= 0; end else if(add_cnt)begin if(end_cnt) cnt <= 0; else cnt <= cnt + 1; end end assign add_cnt = (state_c == WRITE | state_c == READ) & ~avs_waitrequest; assign end_cnt = add_cnt && cnt== `USER_BL-1; /************************读写优先级仲裁*****************************/ //rd_flag ;//读请求标志 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin rd_flag <= 0; end else if(rfifo_usedw <= `RD_LT)begin rd_flag <= 1'b1; end else if(rfifo_usedw > `RD_UT)begin rd_flag <= 1'b0; end end //wr_flag ;//写请求标志 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin wr_flag <= 0; end else if(wfifo_usedw >= `USER_BL)begin wr_flag <= 1'b1; end else begin wr_flag <= 1'b0; end end //flag_sel ;//标记上一次操作 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin flag_sel <= 0; end else if(read2done)begin flag_sel <= 1; end else if(write2done)begin flag_sel <= 0; end end //prior_flag ;//优先级标志 0:写优先级高 1:读优先级高 仲裁读、写的优先级 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin prior_flag <= 0; end else if(wr_flag && (flag_sel || (~flag_sel && ~rd_flag)))begin //突发写优先级高 prior_flag <= 1'b0; end else if(rd_flag && (~flag_sel || (flag_sel && ~wr_flag)))begin //突发读优先级高 prior_flag <= 1'b1; end end /******************************************************************/ /******************** 地址设计 ****************************/ //wr_bank rd_bank always @(posedge clk or negedge rst_n)begin if(~rst_n)begin wr_bank <= 2'b00; rd_bank <= 2'b11; end else if(change_bank)begin wr_bank <= ~wr_bank; rd_bank <= ~rd_bank; end end // wr_addr rd_addr always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin wr_addr <= 0; end else if(add_wr_addr) begin if(end_wr_addr) wr_addr <= 0; else wr_addr <= wr_addr+1 ; end end assign add_wr_addr = (state_c == WRITE) && ~avs_waitrequest; assign end_wr_addr = add_wr_addr && wr_addr == `BURST_MAX-1 ; always @(posedge clk or negedge rst_n) begin if (rst_n==0) begin rd_addr <= 0; end else if(add_rd_addr) begin if(end_rd_addr) rd_addr <= 0; else rd_addr <= rd_addr+1 ; end end assign add_rd_addr = (state_c == READ) && ~avs_waitrequest; assign end_rd_addr = add_rd_addr && rd_addr == `BURST_MAX-1; //wr_finish 一帧数据全部写到SDRAM always @(posedge clk or negedge rst_n)begin if(~rst_n)begin wr_finish <= 1'b0; end else if(~wr_finish & end_wr_addr)begin //写完 从wrfifo读出eop wr_finish <= 1'b1; end else if(wr_finish && end_rd_addr)begin //读完 wr_finish <= 1'b0; end end //change_bank ;//切换bank always @(posedge clk or negedge rst_n)begin if(~rst_n)begin change_bank <= 1'b0; end else begin change_bank <= wr_finish && end_rd_addr; end end /****************************************************************/ /*********************** wrfifo 写数据 ************************/ //控制像素数据帧 写入 或 丢帧 always @(posedge clk_in or negedge rst_n)begin if(~rst_n)begin wr_data_flag <= 1'b0; end else if(~wr_data_flag & ~wr_finish_r[1] & din_sop)begin//可以向wrfifo写数据 wr_data_flag <= 1'b1; end else if(/*wr_finish_r[1] && din_sop*/wr_data_flag & din_eop)begin//不可以向wrfifo写入数据 wr_data_flag <= 1'b0; end end always @(posedge clk_in or negedge rst_n)begin //把wr_finish从wrfifo的读侧同步到写侧 if(~rst_n)begin wr_finish_r <= 0; end else begin wr_finish_r <= {wr_finish_r[0],wr_finish}; end end /****************************************************************/ always @(posedge clk_out or negedge rst_n)begin if(~rst_n)begin rd_data <= 0; rd_data_vld <= 1'b0; end else begin rd_data <= rfifo_q; rd_data_vld <= rfifo_rdreq; end end wfifo wrfifo_inst ( .aclr (~rst_n ), .data (wfifo_data ), .rdclk (clk ), .rdreq (wfifo_rdreq), .wrclk (clk_in ), .wrreq (wfifo_wrreq), .q (wfifo_q ), .rdempty(wfifo_empty), .rdusedw(wfifo_usedw), .wrfull (wfifo_full ) ); assign wfifo_data = {din_eop,din_sop,din}; assign wfifo_wrreq = ~wfifo_full & din_vld & ((~wr_finish_r[1] & din_sop) ||wr_data_flag); assign wfifo_rdreq = state_c == WRITE && ~avs_waitrequest; rfifo u_rdfifo( .aclr (~rst_n ), .data (rfifo_data ), .rdclk (clk_out ), .rdreq (rfifo_rdreq), .wrclk (clk ), .wrreq (rfifo_wrreq), .q (rfifo_q ), .rdempty (rfifo_empty), .wrfull (rfifo_full ), .wrusedw (rfifo_usedw) ); assign rfifo_data = avs_rddata; assign rfifo_wrreq = ~rfifo_full & avs_rddata_vld; assign rfifo_rdreq = ~rfifo_empty & rdreq; //输出 assign dout = rd_data; assign dout_vld = rd_data_vld; assign avm_wrdata = wfifo_q[15:0]; assign avm_write = ~(state_c == WRITE && ~avs_waitrequest); assign avm_read = ~(state_c == READ && ~avs_waitrequest); assign avm_addr = (state_c == WRITE)?{wr_bank[1],wr_addr[21:9],wr_bank[0],wr_addr[8:0]} :((state_c == READ)?{rd_bank[1],rd_addr[21:9],rd_bank[0],rd_addr[8:0]} :0); endmodule
3.vga模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179module vga_dirve (input wire clk, //系统时钟 input wire rst_n, //复位 input wire [ 15:0 ] rgb_data, //16位RGB对应值 output reg h_sync, //行同步信号 output reg v_sync, //场同步信号 output reg [ 11:0 ] addr_h, //行地址 output reg [ 11:0 ] addr_v, //列地址 output wire [ 4:0 ] rgb_r, //红基色 output wire [ 5:0 ] rgb_g, //绿基色 output wire [ 4:0 ] rgb_b //蓝基色 ); //1280 * 640 60HZ localparam H_FRONT = 110; // 行同步前沿信号周期长 localparam H_SYNC = 40; // 行同步信号周期长 localparam H_BLACK = 220; // 行同步后沿信号周期长 localparam H_ACT = 1280; // 行显示周期长 localparam V_FRONT = 5; // 场同步前沿信号周期长 localparam V_SYNC = 5; // 场同步信号周期长 localparam V_BLACK = 20; // 场同步后沿信号周期长 localparam V_ACT = 720; // 场显示周期长 // 640 * 480 60HZ // localparam H_FRONT = 16; // 行同步前沿信号周期长 // localparam H_SYNC = 96; // 行同步信号周期长 // localparam H_BLACK = 48; // 行同步后沿信号周期长 // localparam H_ACT = 640; // 行显示周期长 // localparam V_FRONT = 11; // 场同步前沿信号周期长 // localparam V_SYNC = 2; // 场同步信号周期长 // localparam V_BLACK = 31; // 场同步后沿信号周期长 // localparam V_ACT = 480; // 场显示周期长 // 800 * 600 72HZ // localparam H_FRONT = 40; // 行同步前沿信号周期长 // localparam H_SYNC = 120; // 行同步信号周期长 // localparam H_BLACK = 88; // 行同步后沿信号周期长 // localparam H_ACT = 800; // 行显示周期长 // localparam V_FRONT = 37; // 场同步前沿信号周期长 // localparam V_SYNC = 6; // 场同步信号周期长 // localparam V_BLACK = 23; // 场同步后沿信号周期长 // localparam V_ACT = 600; // 场显示周期长 localparam H_TOTAL = H_FRONT + H_SYNC + H_BLACK + H_ACT; // 行周期 localparam V_TOTAL = V_FRONT + V_SYNC + V_BLACK + V_ACT; // 列周期 reg [ 11:0 ] cnt_h ; // 行计数器 reg [ 11:0 ] cnt_v ; // 场计数器 reg [ 15:0 ] rgb ; // 对应显示颜色值 // 对应计数器开始、结束、计数信号 wire flag_enable_cnt_h ; wire flag_clear_cnt_h ; wire flag_enable_cnt_v ; wire flag_clear_cnt_v ; wire flag_add_cnt_v ; wire valid_area ; // 行计数 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin cnt_h <= 0; end else if ( flag_enable_cnt_h ) begin if ( flag_clear_cnt_h ) begin cnt_h <= 0; end else begin cnt_h <= cnt_h + 1; end end else begin cnt_h <= 0; end end assign flag_enable_cnt_h = 1; assign flag_clear_cnt_h = cnt_h == H_TOTAL - 1; // 行同步信号 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin h_sync <= 1; end else if ( cnt_h == H_SYNC - 1 ) begin // 同步周期时为1 h_sync <= 0; end else if ( flag_clear_cnt_h ) begin // 其余为0 h_sync <= 1; end else begin h_sync <= h_sync; end end // 场计数 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin cnt_v <= 0; end else if ( flag_enable_cnt_v ) begin if ( flag_clear_cnt_v ) begin cnt_v <= 0; end else if ( flag_add_cnt_v ) begin cnt_v <= cnt_v + 1; end else begin cnt_v <= cnt_v; end end else begin cnt_v <= 0; end end assign flag_enable_cnt_v = flag_enable_cnt_h; assign flag_clear_cnt_v = cnt_v == V_TOTAL - 1; assign flag_add_cnt_v = flag_clear_cnt_h; // 场同步信号 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin v_sync <= 1; end else if ( cnt_v == V_SYNC - 1 ) begin v_sync <= 0; end else if ( flag_clear_cnt_v ) begin v_sync <= 1; end end // 对应有效区域行地址 1-640 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin addr_h <= 0; end else if ( valid_area ) begin addr_h <= cnt_h - H_SYNC - H_BLACK + 1; end else begin addr_h <= 0; end end // 对应有效区域列地址 1-480 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin addr_v <= 0; end else if ( valid_area ) begin addr_v <= cnt_v -V_SYNC - V_BLACK + 1; end else begin addr_v <= 0; end end // 有效显示区域 assign valid_area = cnt_h >= H_SYNC + H_BLACK && cnt_h < H_SYNC + H_BLACK + H_ACT && cnt_v >= V_SYNC + V_BLACK && cnt_v < V_SYNC + V_BLACK + V_ACT; // 显示颜色 always @( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin rgb <= 16'h0; end else if ( valid_area ) begin rgb <= rgb_data; end else begin rgb <= 16'b0; end end assign rgb_r = rgb[ 15:11 ]; assign rgb_g = rgb[ 10:5 ]; assign rgb_b = rgb[ 4:0 ]; endmodule // vga_dirve
4.顶层文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172module camera_top ( input wire clk, input wire rst_n, /* 配置寄存器 */ output wire cmos_pwdn, output wire cmos_reset, output wire cmos_sioc, output wire cmos_siod, output wire cmos_xclk, input wire cmos_pclk, input wire cmos_vsync, input wire cmos_href, input wire [7:0] cmos_din, output sdram_clk , output sdram_cke , output sdram_csn , output sdram_rasn , output sdram_casn , output sdram_wen , output [1:0 ] sdram_bank , output [12:0] sdram_addr , inout [15:0] sdram_dq , output [1: 0] sdram_dqm , //vga output wire h_sync, output wire v_sync, output wire [15:0] vga_rgb ); wire clk_24 ; wire clk_50 ; wire clk_75 ; wire clk_100 ; wire clk_84 ; wire clk_100_s ; wire clk_150 ; wire clk_200 ; wire pclk ; wire has_config ; wire rd_req ; wire [ 15:0 ] dout ; wire sop ; wire eop ; wire vld ; wire [ 15:0 ] data ; wire dout_vld ; wire [ 11:0 ] addr_h ; wire [ 11:0 ] addr_v ; wire [ 15:0 ] rgb_data ; //PLL pll pll_inst ( .areset ( ~rst_n ), .inclk0 ( clk ), .c0 ( clk_50 ),//50M .c1 ( clk_24 ),//24M .c2 ( clk_75 ),//75M .c3 ( clk_100 ),//100M .c4 ( clk_84 ) ); pll1 pll1_inst ( .areset ( ~rst_n ), .inclk0 ( clk ), .c0 ( clk_100_s ), .c1 ( clk_150 ), .c2 ( clk_200 ) ); iobuf u_iobuf( .datain (cmos_pclk ), .dataout (pclk ) ); assign sdram_clk = clk_100_s; assign cmos_xclk = clk_24; cmos_top u_cmos_top( .clk ( clk ), .rst_n ( rst_n ), .scl ( cmos_sioc ), .sda ( cmos_siod ), .pwdn ( cmos_pwdn ), .reset ( cmos_reset ), .cfg_done ( has_config ) ); // camera_config_drive u_camera_config_drive( // .clk ( clk ), // .rst_n ( rst_n ), // .pwdn ( cmos_pwdn ), // .reset ( cmos_reset ), // .sioc ( cmos_sioc ), // .siod ( cmos_siod ), // .done ( has_config ) // ); capture u_capture( .clk ( pclk ), .rst_n ( rst_n ), .vsync ( cmos_vsync ), .href ( cmos_href ), .din ( cmos_din ), .din_vld(has_config), .sop ( sop ), .eop ( eop ), .vld ( vld ), .pixel ( data ) ); sdram_controller u_sdram_controller( .clk ( clk_100 ), .clk_in ( pclk ), .clk_out ( clk_75 ), .rst_n ( rst_n ), .sop ( sop ), .eop ( eop ), .din ( data ), .din_vld ( vld ), .rd_req ( rd_req ), .dout ( dout ), .dout_vld ( dout_vld ), .mem_cke (sdram_cke ), .mem_csn (sdram_csn ), .mem_rasn (sdram_rasn ), .mem_casn (sdram_casn ), .mem_wen (sdram_wen ), .mem_bank (sdram_bank ), .mem_addr (sdram_addr ), .mem_dq (sdram_dq ), .mem_dqm (sdram_dqm ) ); vga_control u_vga_control( .clk ( clk_75 ), .rst_n ( rst_n ), .din ( dout ), .din_vld ( dout_vld ), .addr_h ( addr_h ), .addr_v ( addr_v ), .rd_req ( rd_req ), .rgb_data ( rgb_data ) ); vga_dirve u_vga_dirve( .clk ( clk_75 ), .rst_n ( rst_n ), .rgb_data ( rgb_data ), .h_sync ( h_sync ), .v_sync ( v_sync ), .addr_h ( addr_h ), .addr_v ( addr_v ), .rgb_r ( rgb_r ), .rgb_g ( rgb_g ), .rgb_b ( rgb_b ) ); // vga_interface u_vga( // /*input */.clk (clk_75 ), // /*input */.rst_n (rst_n ), // /*input [15:0] */.din (dout ), // /*input */.din_vld (dout_vld ), // /*output */.rdy (rd_req ), // /*output [15:0] */.vga_rgb (rgb_data ), // /*output */.vga_hsync(h_sync ), // /*output */.vga_vsync(v_sync ) // ); assign vga_rgb = rgb_data; endmodule //camera_top
三、源码
https://github.com/TangtangSix/ov5640
最后
以上就是动人路人最近收集整理的关于基于FPGA的图像实时采集一、系统框架二、部分模块实现代码三、源码的全部内容,更多相关基于FPGA内容请搜索靠谱客的其他文章。
发表评论 取消回复