fpga实现ADV模拟视频

ADV模拟图像实现,最难的是实现ADV数据结构和跨时钟域处理。数据结构需要严格遵循协议去写各个位置的非图像数据,例如EAV,SAV等等。数据跨时钟要最要注意两边图像帧频是否一致,FIFO大小是否满足要求。

本文讨论的项目中使用ADV7391的PAL Square模式把20M时钟50Hz帧频的640*512数字视频转换为模拟视频。(注:本文不详细说明配置ADV7391,配置在软核中完成)

1.ADV分析

PAL Square图像区为768*576,隔行扫描(I),帧频为25Hz,时钟为29.5M时钟。原图像隔行扫描,一帧取一半,采用奇帧偶行偶帧奇行的方式。

50Hz的图像帧频,图像一帧为20ms,PAL Square29.5M一行1888个数据,时长为64us,可算得一共312.5行,当然ADV不存在半行,只要保证两帧图像平均帧率符合25Hz即可,例如奇帧313行偶帧312行。

现在可以设计ADV的结构了:

313/312行可以划分为三部分:PRE_INTEG    INTEG    IDLE 三部分 分别为22行 288行 3/2行。288行为数据区。解释一下768*576,576行为两帧拼起来的288*2,每帧图像仅为256行(512隔行扫描),剩余32行可作为消隐,前后各16行,数据区每行768个数据,此处采用YCrCb协议,灰度图像中Y为真实数据,CrCb无数据,故真实数据为50%,768*2 = 1536,真实数据每行640, 640*2=1280,其余区域256列为消隐,左右各128列。

 PAL制具体数据模式如下:

EAVSAV为嵌入式控制字,分别表示有效视频的终点和起点。EAVSAV均为4个字节构成,前3个字节FF0000为固定头,“XY”为控制字。“XY”8bit含义如下:

Bit7Const:常数,总为1                                                                                                      Bit6F:场同步信号,表示该行数据处于奇场还是偶场。                                                 Bit5V:垂直同步信号,表示处于场消隐区间还是正程区间(有效数据行)。                            Bit4H:水平同步信号,表示是“SAV”还是“EAV”。                                                                  Bit3  :纠错位 B3 = V (XOR) H                                                                                                       Bit2  :纠错位 B2 = F (XOR) H                                                                                                      Bit1  :纠错位 B1 = F (XOR) V                                                                                                            Bit0 :纠错位 B0 = F (XOR) V (XOR) H

解析出所有的数据,写出adv具体代码,如下:


        begin
            if(count_x==0)
                begin
                    data_outbuf<=8'hff;
                    HS<=0;
                end
            else if(count_x<=2)
                begin
                    data_outbuf<=8'h00;
                    HS<=1;
                end
            else if(count_x==3)
                begin
                    HS<=HS;
                    if(odd_even_flag==1)
                        begin
                             case(state)
                                PRE_INTEG:data_outbuf<=8'hb6;
                                INTEG:   data_outbuf<=8'h9d;
                                IDLE:        data_outbuf<=8'hb6;
                             endcase
                        end
                    else
                        begin
                         case(state)
                                PRE_INTEG:data_outbuf<=8'hf1;
                                INTEG:   data_outbuf<=8'hda;
                                IDLE:        data_outbuf<=8'hf1;
                         endcase
                        end
                end
            else if(count_x<=347)
                begin
                    HS<=HS;
                    data_outbuf<=(count_x%2)?8'h10:8'h80;
                end
            else if(count_x==348)
                begin
                    HS<=HS;
                    data_outbuf<=8'hff;
                end
            else if(count_x<=350)
                begin
                    HS<=HS;
                    data_outbuf<='h00;
                end
            else if(count_x==351)
                begin
                    HS<=HS;
                    if(odd_even_flag==1)
                        begin
                         case(state)
                                PRE_INTEG:data_outbuf<=8'hab;
                                INTEG:   data_outbuf<=8'h80;
                                IDLE:        data_outbuf<=8'hab;
                         endcase
                        end
                    else
                        begin
                            case(state)
                                PRE_INTEG:data_outbuf<=8'hec;
                                INTEG:   data_outbuf<=8'hc7;
                                IDLE:        data_outbuf<=8'hec;
                           endcase
                        end
                end
            else if(count_x<=1888-1)
                begin
                    HS<=HS;
                    case(state)
                        PRE_INTEG:data_outbuf<=(count_x%2)?8'h10:8'h80;
                        INTEG:
                            if((count_x>=480&&count_x<=1888-1-32*4)&&(count_y>=0+16&&count_y<=287-16))
                                begin
                                    if(count_x[1:0]==2'b00)
                                        begin
                                        data_outbuf<=8'h80;//Cb
                                        Rd_req<=1;
                                        end
                                    else if(count_x[1:0]==2'b10)
                                        begin
                                        data_outbuf<=8'h80;//Cr
                                        Rd_req<=1;
                                        end
                                    else
                                        begin
                                        data_outbuf<=datain;
                                        Rd_req<=0;
                                        end
                                end
                            else
                                begin
                                    Rd_req<=0;
                                    case(state_tmp)
                                        Cb:
                                            begin
                                                data_outbuf<=8'h80;
                                                state_tmp <= Y1;
                                            end
                                        Y1:
                                            begin
                                                data_outbuf<=8'h00;
                                                state_tmp <= Cr;
                                            end
                                        Cr:
                                            begin
                                                data_outbuf<=8'h80;
                                                state_tmp <= Y2;
                                            end
                                        Y2:
                                            begin
                                                data_outbuf<=8'h00;
                                                state_tmp <= Cb;
                                            end
                                    endcase
                                    
                                end
                        IDLE:        data_outbuf<=(count_x%2)?8'h10:8'h80;
                    endcase
                end
            end

count_x,count_y分别为列行计数,在三个区域分别计数,区域间无间隔。实现代码简单,此处不贴。

2.跨时钟域分析

20M时钟的图像转换为29.5M时钟的ADV数据。原始图像为640*512,具体如图。

 此前已说明每两帧图像的平均帧率是相匹配的,在数据有效时,把奇帧偶行,偶帧奇行写入FIFO等待,ADV读出。

FIFOwr_en = (((~OEFlag)&RowNum[0])|((OEFlag&(~RowNum[0]))))? iDataValid : 0;//写使能

OEFlag标志奇偶帧,帧有效上升沿取反,RowNum为行计数,最低位可标志奇偶行。

way1.

ADV在读出FIFO之前需要经过22行消隐和16行黑色边框,38*64000/50=48640clk(换算为20M时钟),原始数据此时需要存在FIFO中,48640/645=75.4 38*640=24320 通过计算48640clk最多存放24320个数据,需要官方ip核最小为32768,消耗资源较大。

 

way2.考虑另一种方式:舍弃第一帧,可以大大节约资源。

 第一帧图像写入FIFO,FIFO会写满,直到原始图像下一帧到来,frame_valid的上升沿作为FIFO清空信号。由于第一帧没有读出,写入的数据会被清除。

原始数据一共需要645*512*50ns = 16512000ns,ADV数据区256*64000ns = 16384000ns            可见ADV速度略快,这就要求FIFO中需要预存一定量的数据,以免FIFO发生读空。

16512000 - 16384000 = 128000ns  2560clk(20M)

取整,3000clk,48640 + 3000 = 45640  399999 - 45640 = 354359

在原始图像上循环计数,frame_valid开始从0到399999(每帧400000clk),在第一次计数达到354359 时对ADV模块复位,可以保证图像不会读空,不会写满。开始产生第一个数据,经过38行过后开始读FIFO,计数在353000的时候原始数据已经结束,注意此时原始帧已经进入消隐,数据个数为640*512=327680 < 354359。                                         

ADV读出之前,原始图像已写入3000clk,3000/645= 4.65,最多存入2.65行按3行算即         640*3= 1920个数据,此时需要FIFO最小取4096。

可见FIFO大大缩小,节约大量资源。

3.后续问题

注意两点

第一,产生29.5M时钟的pll不可以与其他时钟同pll,尤其20M,同一pll的输出时钟最好是相互之间成倍数关系,不然生成的时钟不准确,就会造成两边帧率不统一,以上计算都是严格按照准确时钟计算的,如果出现时钟不准确,必然导致图像不同步然后移动。

第二,也是时钟问题,一般情况下不出现,时钟不同源的问题。例如原始图像以及其时钟是由此前的硬件中传输过来的,而ADV29.5M时钟是在此块fpga上生成的,这样的时钟也必然会导致图像同步失败产生移动,因为非同源时钟有误差。我在设计中遇到过这个问题。

此时的解决办法是人为给出同步信号,承接2中的分析,在计数为354359时,复位ADV行列计数清零,虽然只复位一次,由于帧频一致,每次计数到354359时,ADV的行列计数都归零了,标志ADV新的一帧开始,然而时钟如果有问题,此时ADV所处的位置就会超前或落后。每两帧在计数354359处复位ADV就不会出现未同步的问题,注意不可以每帧都复位,因为ADV奇偶帧长度不一,此前已说过,当然平均帧频与原始帧是一致的。

感谢阅读 😁

o_e   o_e    o_e 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值