对DE2_TV例程做了分析,并阐明了相关概念。
硬件平台:DIY_DE2
软件平台:Quartus II 9.0
常见的电视信号制式是PAL和NTSC,另外还有SECAM等。 NTSC即正交平衡调幅制,PAL为逐行倒像正交平衡调幅制。
(1)PAL电视标准
PAL电视标准,每秒25帧,电视扫描线为625线,奇场在前,偶场在后,标准的数字化PAL电视标准分辨率为720*576, 24比特的色彩位深,画面的宽高比为4:3,PAL电视标准用于中国、欧洲等国家和地区。
(2)NTSC电视标准
NTSC电视标准,每秒29.97帧(简化为30帧),电视扫描线为525线,偶场在前,奇场在后,标准的数字化NTSC电视标准分辨率为720*486,24比特的色彩位深,画面的宽高比为4:3。NTSC电视标准用于美、日等国家和地区。
下面重点说一下PAL制式。PAL一帧的数据行格式如图1所示。
图1 一帧图像数据行格式
对于有效数据行的格式,如图2所示。
图2 有效视频行的数据格式
如上图所示,EAV和SAV为嵌入式控制字,分别表示有效视频的终点和起点。EAV和SAV均为4个字节构成,前3个字节FF、00、00为固定头,“XY”为控制字。“XY”的8个bit含义如下:
EAV与SAV的详细定义如表1所示。
表1
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3-0(P3P2P1P0) |
Hex |
Description |
1 |
0 |
0 |
0 |
0000 |
0x80 |
Even,Active,SAV |
1 |
0 |
0 |
1 |
1101 |
0x9d |
Even, Active,EAV |
1 |
0 |
1 |
0 |
1011 |
0xab |
Even,Blank, SAV |
1 |
0 |
1 |
1 |
0110 |
0xb6 |
Even, Blank, EAV |
1 |
1 |
0 |
0 |
0111 |
0xc7 |
Odd, Active, SAV |
1 |
1 |
0 |
1 |
1010 |
0xda |
Odd, Active, EAV |
1 |
1 |
1 |
0 |
1100 |
0xec |
Odd, Blank, SAV |
1 |
1 |
1 |
1 |
0001 |
0xf1 |
Odd, Blank, EAV |
Blanking为水平消隐区,通常由80H/10H来填充。
对于图2中的Valid data(有效数据)区,其数据排列顺序如图3所示。即Y : Cb : Cr=4 : 2 : 2。从图像的像素点上来理解,就是每个像素点有一个单独的Y值,而相邻的两个像素点的Cb和Cr数据是一样的。
图3 数据排列顺序
关于这两种信号的区别:
ITU-R BT.601: 16位数据传输,21芯;Y、U、V信号并行传输,并有场频和行频传输线。最后更新的文档代号为:ITU-R BT.601-5。
ITU-R BT.656: 8位数据传输,9芯;串行视频传输,不需要同步信号;传输速率是601的2倍。最后更新的文档代号为:ITU-R BT.656-4。
656输出的是串行数据,行场同步信号嵌入在数据流中,601是并行数据,行场同步有单独输出。
DE2视频解码芯片是SAA7181,DIY_DE2采用的视频解码芯片是SAA7113,两种芯片都支持ITU-R BT.656和ITU-R BT.601两种信号接口。SAA7113采用的外部晶振为24.576MHz,经过内部锁相环之后,像素时钟为27MHz,亮度信号时钟为13.5MHz,色差信号时钟为6.75MHz。
DE2_TV例程是针对NTSC制式的摄像头,而我这里是用的PAL制式的摄像头,视频信号为隔行扫描,奇场在前,偶场在后。使用的视频接口为ITU-R BT.656,因此对例程做了些修改。
具体配置方法上篇博文中已经说明,这里给出配置参数。
SET_VIDEO+0: LUT_DATA <= 16'h0108; SET_VIDEO+1: LUT_DATA <= 16'h02C3;//default C0 SET_VIDEO+2: LUT_DATA <= 16'h0333;//default 33 SET_VIDEO+3: LUT_DATA <= 16'h0400;//default 00 SET_VIDEO+4: LUT_DATA <= 16'h0500;//default 00 SET_VIDEO+5: LUT_DATA <= 16'h06e9; SET_VIDEO+6: LUT_DATA <= 16'h070d; SET_VIDEO+7: LUT_DATA <= 16'h0898; SET_VIDEO+8: LUT_DATA <= 16'h0901; SET_VIDEO+9: LUT_DATA <= 16'h0a80; SET_VIDEO+10: LUT_DATA <= 16'h0b47; SET_VIDEO+11: LUT_DATA <= 16'h0c40; SET_VIDEO+12: LUT_DATA <= 16'h0d00; SET_VIDEO+13: LUT_DATA <= 16'h0e01; SET_VIDEO+14: LUT_DATA <= 16'h0f2a; SET_VIDEO+15: LUT_DATA <= 16'h1000; SET_VIDEO+16: LUT_DATA <= 16'h110c; SET_VIDEO+17: LUT_DATA <= 16'h12b7; SET_VIDEO+18: LUT_DATA <= 16'h1300; SET_VIDEO+19: LUT_DATA <= 16'h1500; SET_VIDEO+20: LUT_DATA <= 16'h1600; SET_VIDEO+21: LUT_DATA <= 16'h1700; SET_VIDEO+22: LUT_DATA <= 16'h4002; SET_VIDEO+23: LUT_DATA <= 16'h41ff; SET_VIDEO+24: LUT_DATA <= 16'h42ff; SET_VIDEO+25: LUT_DATA <= 16'h43ff; SET_VIDEO+26: LUT_DATA <= 16'h44ff; SET_VIDEO+27: LUT_DATA <= 16'h45ff; SET_VIDEO+28: LUT_DATA <= 16'h46ff; SET_VIDEO+29: LUT_DATA <= 16'h47ff; SET_VIDEO+30: LUT_DATA <= 16'h48ff; SET_VIDEO+31: LUT_DATA <= 16'h49ff; SET_VIDEO+32: LUT_DATA <= 16'h4aff; SET_VIDEO+33: LUT_DATA <= 16'h4bff; SET_VIDEO+34: LUT_DATA <= 16'h4cff; SET_VIDEO+35: LUT_DATA <= 16'h4dff; SET_VIDEO+36: LUT_DATA <= 16'h4eff; SET_VIDEO+37: LUT_DATA <= 16'h4fff; SET_VIDEO+38: LUT_DATA <= 16'h50ff; SET_VIDEO+39: LUT_DATA <= 16'h51ff; SET_VIDEO+40: LUT_DATA <= 16'h52ff; SET_VIDEO+41: LUT_DATA <= 16'h53ff; SET_VIDEO+42: LUT_DATA <= 16'h54ff; SET_VIDEO+43: LUT_DATA <= 16'h55ff; SET_VIDEO+44: LUT_DATA <= 16'h56ff; SET_VIDEO+45: LUT_DATA <= 16'h57ff; SET_VIDEO+46: LUT_DATA <= 16'h5800; SET_VIDEO+47: LUT_DATA <= 16'h5954; SET_VIDEO+48: LUT_DATA <= 16'h5a07; SET_VIDEO+49: LUT_DATA <= 16'h5b83; SET_VIDEO+50: LUT_DATA <= 16'h5e00;
由于采用PAL制式的摄像机,其视频画面的有效分辨率为720*576,即总共576行(分奇偶两场,各占288行),每行720个像素点,由于是8位串行输出,因此每行总共有1440个字节。
视频解码文件完成的任务就是:通过判断SAV信号,来判断接下来的数据是否为有效视频数据,如果是,则进一步分离出有效数据的同步信号oDVAL和有效数据Y、Cb、Cr。
视频的裁剪是通过一个除法器辅助完成的,主要是对每行视频数据进行裁剪,从720个像素裁剪到640像素。除数为当前视频行的当前字节数右移1位,被除数为9。也就是说,每9个像素中,有一个像素点被裁剪掉,即720-720/9=640。具体代码如下。
if(iSwap_CbCr) begin case(Cont[1:0]) // Swap 0: Cb <= iTD_DATA; 1: YCbCr <= {iTD_DATA,Cr}; 2: Cr <= iTD_DATA; 3: YCbCr <= {iTD_DATA,Cb}; endcase end else begin case(Cont[1:0]) // Normal 0: Cb <= iTD_DATA; 1: YCbCr <= {iTD_DATA,Cb}; 2: Cr <= iTD_DATA; 3: YCbCr <= {iTD_DATA,Cr}; endcase end
上述代码中,iSwap_CbCr除数除以被除数的商,Cont为当前视频行的当前字节数,下面以例子示之。
Cont[1:0] 00 01 10 11 00 01 10 11 00 01 10 11 00 01 10 11 00 01 10 11 00 01 10 11
iSwap_CbCr 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1
iTD_DATA Cb1 Y1 Cr1 Y2 Cb2 Y3 Cr2 Y4 Cb3 Y5 Cr3 Y6 Cb4 Y7 Cr4 Y8 Cb5 Y9 Cr5 Y10 Cb6 Y11 Cr6 Y12
YCbCr Y1Cb1 Y2Cr1 Y3Cb2 Y4Cr2 Y5Cb3 Y6Cr3 Y7Cb4 Y8Cr4 Y9Cb5 Y10Cb5 Y11Cr5 Y12Cb6
从上述例子可以看出,每4个时钟周期输出2个像素值,即输出像素值的频率减小了一半,这与分离出来的同步信号oDVAL的时钟频率一样,同为13.5MHz。
另外,裁剪部分,由于Y9Cb5与Y10Cb5有很大的相似性,这里应该是将Y9Cb5剔除,但是具体在什么位置剔除,却不得而知。莫非是分析错误?求解。
存储在SDRAM采用的是1入2出的模式,即一个端口写入,2个端口读出。存取数据的代码如下:
// SDRAM frame buffer Sdram_Control_4Port u6 ( // HOST Side .REF_CLK(CLOCK_50), //.CLK_18(AUD_CTRL_CLK), .RESET_N(1'b1), // FIFO Write Side 1 .WR1_DATA(YCbCr), .WR1(TV_DVAL), .WR1_FULL(WR1_FULL), .WR1_ADDR(0), .WR1_MAX_ADDR(640*576), // 525-18 .WR1_LENGTH(9'h80), .WR1_LOAD(!DLY0), .WR1_CLK(PPI_CLK), // FIFO Read Side 1 .RD1_DATA(m1YCbCr), .RD1(m1VGA_Read), .RD1_ADDR(640*12), // Read odd field and bypess blanking .RD1_MAX_ADDR(640*252), .RD1_LENGTH(9'h80), .RD1_LOAD(!DLY0), .RD1_CLK(PPI_CLK), // FIFO Read Side 2 .RD2_DATA(m2YCbCr), .RD2(m2VGA_Read), .RD2_ADDR(640*300), // Read even field and bypess blanking .RD2_MAX_ADDR(640*540), .RD2_LENGTH(9'h80), .RD2_LOAD(!DLY0), .RD2_CLK(PPI_CLK), // SDRAM Side .SA(DRAM0_A), .BA(DRAM0_BA), .CS_N(DRAM0_CS), .CKE(DRAM0_CKE), .RAS_N(DRAM0_RAS), .CAS_N(DRAM0_CAS), .WE_N(DRAM0_WE), .DQ(DRAM0_D), .DQM(DRAM0_DQM), .SDR_CLK(DRAM0_CLK) );
经过裁剪后的视频分辨率为640*576。
写入SDRAM中时,是将640*576个像素全部写入进去,由于是奇场数据在前,偶场数据在后,因此前640*288行数据为奇场数据,后 640*288行数据为偶场数据。
读取视频数据时,采用乒乓操作,先后读取奇偶场的数据,各240行,组成一幅完整的画面,读取方法如下:
1
……
11
读取12到251行,共240行
252
……
288
以上为奇数场数据,以下为偶数场数据
289
……
299
读取300到539行,共240行
540
……
576
其中,1~288行为奇数场数据,289~576为偶数场数据。上述数据是从11~251,300~539读取的,当然也可以从1~240,289~529读取,只要满足奇偶场的起始行为相邻行,且保证奇数场的数据行在前即可。
因此,最后得到的视频分辨率为640*480.
进行色彩空间转换之前,对视频行数据进行了简单的算法处理,代码如下:
// Line buffer, delay one line Line_Buffer u10 ( .clken(VGA_Read), .clock(PPI_CLK), .shiftin(mYCbCr_d), .shiftout(m3YCbCr)); Line_Buffer u11 ( .clken(VGA_Read), .clock(PPI_CLK), .shiftin(m3YCbCr), .shiftout(m4YCbCr)); wire [15:0] m4YCbCr; wire [15:0] m5YCbCr; wire [8:0] Tmp1,Tmp2; wire [7:0] Tmp3,Tmp4; assign Tmp1 = m4YCbCr[7:0]+mYCbCr_d[7:0]; assign Tmp2 = m4YCbCr[15:8]+mYCbCr_d[15:8]; assign Tmp3 = Tmp1[8:2]+m3YCbCr[7:1]; assign Tmp4 = Tmp2[8:2]+m3YCbCr[15:9]; assign m5YCbCr = {Tmp4,Tmp3};
对视频行进行了加和及数乘运算。
最后将YUV 4:2:2先转换为YUV4:4:4的形式,之后再通过公式转换成RGB色彩空间,最后通过VGA显示在显示器上。