登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

DOS编程技术

讨论在纯DOS下的编程技术

 
 
 

日志

 
 
关于我

1984年大学毕业,1985年底有机会开始接触PC机,1986年开始在PC机上做开发工作,曾接触过MS-DOS、CP/M、UNIX、VMS、LINUX、iRMX等众多的操作系统并在上面从事技术开发,擅长做底层与硬件相关的软件开发,目前主要在DOS和LINUX平台下工作,主要从事软件,在硬件开发上也有一定造诣,亦有在8051系列、6502系列(凌阳)、z80系列、ARM、X86等各类平台下开发软硬件的经历。更详细情况可以参考http://resume.whowin.net

在DOS下玩转BT878A进行视频采集  

2008-05-19 13:54:00|  分类: 杂七杂八 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    BT878A是目前广泛采用的一种视频采集芯片,市面上可以很容易地买到PCI的以BT878A位核心的视频采集卡,这些卡大都仅提供windows下的驱动,如果你要把这颗芯片用在工控领域,如果你的系统不采用windows,那么困难就来了。
    本文是以前一个使用BT878A芯片用于视频采集的项目的关于BT878A使用的一个总结,主要叙述如何从底层直接对BT878A进行操作和控制,掌握本文介绍的内容,应该说不仅是在DOS下,应该在任何环境下,你都将有能力驾驭这颗芯片。本文并不打算对BT878A的所有功能进行解释,仅对其视频采集部分的基本功能进行说明并实际完成一个例子。

1、BT878A介绍

    该芯片的datasheet可以在下面地址下载:

    http://blog.whowin.net/datasheet/bt878.pdf(2017年3月22日注:链接已修复)

    BT878A功能十分强大,下面仅就本文感兴趣的功能作一下介绍:

  • 有PCI接口,可以方便地连接到PCI总线上
  • 有DMA控制器,可以通过PCI总线实现DMA
  • 支持PAL和NTSC制式的输入模拟信号
  • 内部有一套精简指令系统,在实现数据采集和高质量视频流时十分有用
  • 最大分辨率可以达到768X576(实例中我们将在NTSC下实现640X480分辨率)
  • 可以实现32位真彩色(我们实际使用24位真彩)
  • 24位的GPIO,同时有一个I2C接口(将另文介绍使用BT878的I2C接口的体会)
  • 可以调整亮度、对比度等

2、对BT878A编程及相关寄存器介绍

    前面提到,BT878A可以很方便地连接到PCI总线上,BT878A实现了PCI的配置空间,所以有关BT878的基地址等一系列信息要通过PCI的配置寄存器获得,这个方法在我以前的博文里有介绍,请参阅博文:《遍历PCI设备》和《在DOS下针对AC'97编程》,BT878A是CONEXANT的产品,该公司的Vendor ID是0x109E,Function 0的Device ID是0x036E,Function 1的Device ID是0x0878,我们可以使用这些数据来搜寻你的PCI总线上是否有这个设备。

    BT878A的寄存器非常多(具体多少我没有数,偏移地址从0x000--0x2FF,中间有些地方不连续),寄存器是以存储器地址映射的,从PCI配置空间得到基地址后,加上偏移地址,使用内存的访问方法就可以访问到所有的寄存器(我们只用FUNCTION 0的寄存器),由于涉及到许多视频的知识,有些寄存器的作用我也没有完全搞清楚,好在很多寄存器并不需要我们过多地关心,所有寄存器的作用,请看BY878A的datasheet的5-3 Local Registers,这里只就我们比较关心的一些寄存器以功能为序进行一下介绍。

  • 芯片的软件复位:
    SRESET——Software Reset Register(0x07c)
    向SRESET寄存器写入任何数据会复位BT878的视频部分,同时所有寄存器将复位成缺省值。
    WriteLocalDWORD(SRESET,oxff)
  • 停止RISC的运行
    涉及寄存器:CAP_CTL: Capture Control Register(0x0dc)
    Bit0:CAPTURE_EVEN:为1时,在偶域可以向FIFO写数据
    Bit1:CAPTURE_ODD:为1时,在奇域可以想FIFO写数据
    Bit2:CAPTURE_VBI_EVEN:为1时,在偶域可以采集VBI数据并写入FIFO
    Bit3:CAPTURE_VBI_ODD:为1时,在奇域可以采集VBI数据并写入FIFO
    Bit4:DITH_FRAME: 0—抖动矩阵应用于一个域的连续行上
                       1—全屏幕方式
    其它位备用
    WriteLocalDWORD(CAP_CTL,0x00)
  • 初始化GPIO和DMA
    涉及寄存器
    GPIO_DMA_CTL:GPIO and DMA Control Register (0x10c)
    设置成Packed方式,一次传递8 WORDS,故bit3,bit2设置成01
    bit0:FIFO_ENABLE,为1时,允许FIFO数据
    bit1:RISC_ENABLE:为1时,开始运行RISC
    启动RISC时要将这两位置1
    WriteLocalDWORD(GPIO_DMA_CTL, 0x04)
  • 设置中断寄存器
    INT_MASK:Interrupt Mask Register(0x104)

    不使用中断,所以此寄存器设置为0
    WriteLocalDWORD(INT_MASK,0X00)
  • 设置彩色格式
    Color_FMT:Color Format Register(0x0D4)

    Bit[3:0]:偶域的色彩 bit[7:4]:奇域的色彩

    0000=RGB32        0001=RGB24          0010=RGB16          0011=RGB15

    0100=YUV2 4:2:2   0101=BtYUV 4:1: 1   0110=Y8(Gray scale) 0111=RGB(Dithered)

    1000=YcrCb 4:2:2 planar(YUV12)        1001=YcrCb 4:1:1 plannar (YUV9)

    1010—1101=备用    1110=Raw 8xData    1111=备用

    我们使用RGB16,故设为0010

    WriteLocalDWORD(COLOR_FMT,0x22)
  • 设置ADC
    ADC:ADC Interface Register (0x068)

    Bit[7:6]=10  bit5:备用
    bit4:0—AGC Enabled   1—AGC disabled

    Bit3:CLK_SLEEP,0—普通时钟操作,1—关闭系统时钟

    Bit2:Y_SLEEP,0—普通Y ADC操作,1—停止Y ADC操作,

    Bit1:C_SLEEP,0—普通C ADC操作,1—停止C ADC操作,

    Bit0:0—无自适应AGC  1—自适应AGC

    WriteLocalDWRD(ADC,ox82)设置成缺省值 
  • 色度设置
    YUV简介
         YUV(亦称YCrCb)是被欧洲电视系统所采用的一种颜色编码方法(属于PAL)。YUV主要用于优化系统视频信号的传输,使其向后兼容老式黑白电视机,与RGB视频信号传输相比,它最大的优点在于只需占用极少的带宽(RGB要求三个独立的视频信号同时传输)。其中Y表示明亮度(Luminance或Luma),也就是灰阶值;而U和V表示的则是色度(Chrominance或Chroma),作用是描述影像色彩和饱和度,用于指定像素的颜色。“亮度”是通过RGB输入信号来创建的,方法是将RGB信号的特定部分叠加到一起。“色度”则定义了颜色的两个方面—--色调与饱和度,分别用Cr和Cb来表示,其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是GRB输入信号蓝色部分与RGB信号亮度值之间的差异。
    色度设置设计到4个寄存器
    SAT_U_LO:Chroma(U)Gain Register,Lower Byte(0x034)
    SAT_V_LO:Chroma(V)Gain Register,Lower Byte(0x038)
    E_CONTROL: Miscellaneous Control Register (Even Field)(0x02C)
    O_CONTROL: Miscellaneous Control Register (Odd Field)(0x0AC)
    其中E_CONTROL 和O_CONTROL的Bit0为色度调节(v)的高位,bit1为色度调节(U)的高位,所以,色度调节寄存器共有9位,设备为缺省值即可:SAT_U_LO为0xfe,SAT_V_LO为0xb4
    WriteLocalDWORD(SAT_U_LO, 0xfe)
    WriteLocalDWORD(STA_V_LO, 0xb4)
  • 亮度调节
    BRIGHT:Brightness Control Register(0x028)亮度值设为0x20

    WriteLocalDWORD(BRIGHT, 0x20)
  • 对比度设置
    涉及三个寄存器
    CONTRAST_LO:Luma Gain Register,Lower Byte(0x030)
    E_CONTROL: Miscellaneous Control Register (Even Field)(0x02C)
    O_CONTROL: Miscellaneous Control Register (Odd Field)(0x0AC)
    其中E_CONTROL和O_CONTROL的bit2为亮度调节的高位,故亮度调节共有9位。
    亮度设为0x30,WriteLocalDWORD(CONTRAST_LO, 0x030)
  • 色彩控制
    COLOR_CTL:Color Control Register(0x0D8)

    具体说明请看Page5—29

    设为0x10,WriteLocalDWORD(COLOR_CTL,0x10)
  • 输入格式设置
    IFORM:Input Format Register(0x004)

    Bit[2:0]输入格式

    000—自动控制格式       001—NTSC(M)         010—NTSS(JAPAN)

    011—PAL(B,G,H,I)       100—PAL(M)         101—PAL(N)

    110—SECAM              111—PAL(N—联合)

    bit[6:5]输入端口

    00—MUX3      01—MUX2      10—MUX0      11—MUX1

    其他位备用

    本机使用MUX0,输入格式为NTSC(M)

    bit[4:3]:必须设为11

    WriteLocalDWORD(IFORM, 0x59)(01011001B)
  • AGC延迟和Burst延迟
    ADELAY:AGC Delay Register(0x060)

    BDELAY:Burst Delay Register(0x064)

    ADELAY的计算公式ADELAY = (6.8&181;s X 4 X Fsc) + 15

    对于NTSC输入信号ADELAY=(6.8&181;s X 4 X 14.32MHz) + 15 = 112(0x70)

    BDELAY的计算公式BDELAY=(6.5&181;s X 4 X Fsc)

    对于NTSC输入信号:BDELAY=(6.5&181;s X 4 X 14.32MHz) = 93(0x5D)

    这两个值的含义不很清楚,故按缺省值设置

    WriteLocalDWORD(ADELAY,0x70)

    WriteLocalDWORD(BDELAY,0x50)
  • 缩放比例设置
    涉及寄存器

    E_HSCALE_HI: Horizontal Scale Register(Even Field),Upper Byte(0x020)

    O_HSCALE_HI: Horizontal Scale Register(Odd Field),Upper Byte(0x0A0)

    E_HSCALE_HI: Horizontal Scale Register(Even Field),Lower Byte(0x024)

    O_HSCALE_HI: Horizontal Scale Register(Odd Field),Lower Byte(0x0A4)

    E_VSCALE_HI: Vertical Scaling Register(Even Field),Upper Byte(0x04c)

    O_ VSCALE _HI: Vertical Scaling Register(Odd Field),Upper Byte(0x0AC)

    E_ VSCALE _LO: Vertical Scaling Register(Even Field),Lower Byte(0x050)

    O_ VSCALE_LO: Vertical Scaling Register(Odd Field),Lower Byte(0x0D0)

    首先说HSCALE,定义横向的缩放比例,对NTSC而言,BT878A每行产生910个像素,PAL产生1135像素,HSCALE的计算公式如下:

    NTSC:HSCALE = [(910 / Pdesird) - 1] x 4096

    PAL:HSCALE = [(1135 / Pdesird) - 1] x 4096

    其中Pdesired为你希望得到的像素值,其中包括active,sync或blanking由于sync或blanking的值不好掌握,还有下面的公式

    NTSC:HSCALE = [(754 / HACTIVE) - 1] x 4096

    PAL:HSCALE = [(922 / HACTIVE) - 1] x 4096

    HACTIVE:每行像素值,不包含sync或blanking,这个公式与前面的公式相比会略有差别,只能粗略地反映实际情况。

    实际设置我们以后面的公式计算。NTSC,HACTIVE = 640最后得出

    HSCALE = 729.6(0x2D9)

    WriteLocalDWORD(O_HCALE_HI, 0x02)

    WriteLocalDWORD(O_HCALE_LO, 0xD9)

    WriteLocalDWORD(E_HCALE_HI, 0x02)

    WriteLocalDWORD(E_HCALE_LO, 0xD9)

    垂直缩放VSCALE

    公式如下

    VSCALE = (0x10000 - {[(scale - ratio) - 1] x 512} + 0x1fff

    要注意的是VSCALE只有13位有效位,即其MSB中只有5位有效,其高三位用于其它控制,在设置VSCALE时不要破坏。在这里垂直方向比例为1:1,故而SCALE设为0

    WriteLocalDWORD(O_VSCALE_LO,0)

    WriteLocalDWORD(O_VSCALE_HI,0)

    WriteLocalDWORD(E_VSCALE_LO,0)

    WriteLocalDWORD(E_VSCALE_HI,0)

    在Page2—16有各种情况下HSCALE和VSCALE的表格可供参考
  • 图像裁减设置
    涉及寄存器

    HDELAY: Horizontal Delay Register,Lower Byte

    E_HDELAY_LO(0x18)        O_HDELAY_LO(0x098)

    MSB Cropping Register的bit[3:2]

    E_CROP(0x00C)            O_CROP(0x010)

    VDELAY:Vertical Delay Register Lower Byte

    E_VDELAY_LO(0x090)       O_HACTIVE_LO(0x010)

    MSB Cropping Register的bit[7:6]

    HDELAY: Horizontal Delay Register,Lower Byte

    E_HDELAY_LO(0x01c)       O_HDELAY_LO(0x09c)

    MSB Cropping Register的bit[1:0]

    VDELAY:Vertical Delay Register Lower Byte

    E_VDELAY_LO(0x014)       O_HACTIVE_LO(0x094)

    MSB Cropping Register的bit[5:4]

    HDELAY:水平同步信号到显示的第一个像素之间像素数

    HACTIVE:实际要显示的水平像素数

    VDELAY:锯齿状脉冲的结束到显示的第一行之间的行数的一半

    VACTIVE:实际要显示的行数

    详细描述请看P2—18,P2—19

    其中,HACTIVE和VACTIVE比较好设置,但HDELAY和VDELAY不太好办。

    HDELAY的计算公式

    HDELAY(NTSC) = (135 / 754 x HACTIVE) & 0x3FE

    HDELAY(PAL) = (186 / 922 x HACTIVE) & 0x3FE

    可以适当增加HDELAY或者减少HACTIVE,但是要遵从下面的原则:

    NTSC  HDELAY + HACTIVE ≤ 889 x HSCALE

    PAL    HDELAY + HACTIVE ≤ 1108 x HSCALE

    如果HDELAY + HACTIVE太大,将会看到Front Porch或Back Porch像素

    实际应用中     HACTIVE = 640(0x280)

                  VACTIVE = 480(0x1E0)
                  HDELAY = 0x72
                  VDELAY = 0x1d
    WriteLocalDWORD(E_HACTIVE_LO, 0x80)
    WriteLocalDWORD(O_HACTIVE_LO, 0x80)
    WriteLocalDWORD(E_VACTIVE_LO, 0xE0)
    WriteLocalDWORD(O_VACTIVE_LO, 0xE0)
    WriteLocalDWORD(E_CROP, 0x12)
    WriteLocalDWORD(O_CROP, 0x12)
    WriteLocalDWORD(E_HDELAY, 0x72)
    WriteLocalDWORD(O_HDELAY, 0x72)
    WriteLocalDWORD(E_VDELAY, 0x1D)
    WriteLocalDWORD(O_VDELAY, 0x1D)
  • SC LOOP设置
    SC_LOOP:SC LOOP Control Register

    E_SCLOOP(0x040)      O_SCLOOP(0x0c0)

    对此寄存器的认识不是很清楚,对整体的影响也不大详细描述在Page5—19,实际应用中设为0x60

    WriteLocalDWORD(O_SCLOOP,0x60)

    WriteLocalDWORD(E_SCLOOP,0x60)
  • PLL设置
    涉及寄存器

    TGCTRL:Timing Generator Controll Register(0x084)

    PLL_F_LO:PLL Reference Maltiplier Register (0x0F0)

    PLL_F_HI:PLL Reference Maltiplier Register (0x0F4)

    PLL_XCI:PLL Integer Register (0x0F8)

    PLL:phase Lock Loop(锁相环)

    Bt878A的内部锁相环使其可以使用一只晶振就可以解码NTSC和PAL的图像,晶振频率为28.636363精度为50ppm,产生的频率接如下公式计算:
    Frequency = (F_INPUT ÷ PLL_X) X PLL_I.LL_F ÷ PLL_C
         F_INPUT = 28.636363MHz
         PLL_X = 预分频(除以2)(为1或2)
         PLL_I = 整数部分
         PLL_F = 小数部分
         PLL_C = 被除数(为4或6)
         PLL_X:PLL_XCI寄存器的bit7,bit7=0则PLL_X=1,Bit7=1则PLL_X=2
         PLL_I:PLL_XCI寄存器的bit[5:0]取值范围6—63,如果为0则PLL不工作,注意PLL_I.PLL_F = 6.8000(最小值)
         PLL_F:由PLL_F_LO和PLL_F_HI组成,共16位
         PLL_C:PLL_XCI寄存器的 bit6,bit6=0则PLL_C=6,bit6=1则PLL_C=4
         注意,Frequency是所需频率的2倍(CLKx2)如NTSC时Frequency=28.636363(14.32x2)
         PAL时Frequency=35.46895(17.73x2)
         当为NTSC时,所有值可以设为0,当为PAL时做如下设置如下:
         PLL_X = 1      PLL_I = 0x0E         PLL_F = 0xDCF9     PLL_C=0
         设置PLL要按如下步骤:
         1、在TGCTRL寄存器中的TGCKI设为Normal XTALO/XTALI Mode即TGCKI=00
         2、PLL寄存器的值变化后,DSTAUS寄存器的PLOCK位会被拉低,直至PLL被锁住(大约500ms)
         3、将TGCKI设置为PLL方式,即TGCKI=01
         通常不必等PLOCK位变高,简单的方法是将TGCKI = 00然后设置PLL寄存器,再延迟1秒后将TGCKI = 01即可,实际应用中由于使用NTSC,所以PLL寄存器可以设为0

    WriteLocalDWORD(TGCTRL, 0x00)

    WriteLocalDWORD(PLL_F_LO, 0x00)

    WriteLocalDWORD(PLL_F_HI, 0x00)

    WriteLocalDWORD(PLL_XCI, 0x00)

    Sleep(1)
    WriteLocalDWORD(TGCTRL,0x08)

    TGCTRL的说明情况Page5—26其中TGCKI为bit[4:3]
  • 启动RISC
    涉及寄存器

    INT_STAT:/Interrupt status Register (0x100)中断状态寄存器的详细说明Page5—32

    RISC_START_ADD:RISC Program Status Address Register(0x114)

    GPIO_DMA_CTL:Capture Contrd Register(0x0DC)

    GPIO_CTL说明见Page 5—29

    启动RISC按如下步骤:

    (1)中断状态寄存器设置 WriteLocalDWORD(INT_STAT,0xC000),bit[32:31]必须为11

    (2)设置RISC程序地址WriteLocalDWORD(RISS_STRT_ADD,RISCAddr)

    (3)设置CAP_CTL寄存器,启动奇偶行采集WriteLocalDWORD(CAP_CTL, 0x03)

    (4)设置GPIO_DMA_CTL,启动RISC,启动FIFO,一次传8DWORDS

    WriteLocalDWORD(GPIO_DMA_CTL, 0x07)
  • TDEC设置
    当带宽不够或系统限制时,可能系统无法完成所有帧的传输,此时bt878A有一个减少传输帧的解决方案,通过设置TDEC寄存器来完成。

    TDEC:Temporal Decimation Register(0x008)

    TDEC的值可以是1—60(NTSC)或者是1—50(PAL/SECAM),这个值的含义在60帧(NTSC)或50帧(PAL/SECAM)中,丢掉的帧数(或域数)

    Bit7=0 丢掉帧   bit7=1 丢掉域

    Bit6=0,从奇域开始丢弃 bit6=1从偶域开始丢弃

    Bit[5:0]丢掉的帧或域数

    实际应用中设为0,WriteLocalDWORD(TDEC,0x00)
  • 色温调节
    HUE:Hue Control Register(0x03c)

    色温可以有256级,每级为0.7℃,调节范围为 -90°— +89.3°此寄存器为有符号数

    实际应用中设为0 WriteLocalDWORD(HVE,0x00)
  • 白平衡调节
    涉及寄存器

    WC_UP:White Crush Up Register(0x044)

    WC_UP:White Crush Down Register(0x078)

    WC_UP寄存器说明见Page5—20

    Bit[7:6]:确定比较点

    00—最大亮度值的3/4     01—最大亮度值的1/2  10—最大亮度值的1/4     11—自动

    bit[5:0],可以设定为0—63,当比较点的大多数像素的值低于此值时,将适当增加AGC的值

    实际应用中使用缺省值0xcF

    WriteLocalDWORD( WC_UP,0xCF)

    WC_DN寄存器的含义不是很清楚,使用缺省值0x7F

    WriteLocalDWORD(WC_UP,0x7F)
  • 输出色彩范围
    OFORM:Output Format Register(0x048)

    Bit7=0   普通操作(亮度范围16—253,色度范围2—253)

                    (Luma:16—253,chroma:2—253)

             Y=16为黑色(基色)
    Cr,Cb=128为无色
    Bit7=1   全范围输出(Luma:0—255,chroma:2—253)

             Y=0为黑色
       Cr,Cb=128为无色
    Bit[6:5]:当中心区为Luma值小于设置值时,其Luma值将强行为0
             00—无效  01—8   02—16  03—32
    使用缺省值0,WriteLocalDWORD(OFORM,0x00)
  • 音频复位
    ARESET:Audio Reset Register(0x05B)

    Bit7从高跳变到低将复位音频电路

    没有使用该寄存器
  • VTC控制
    VTC:Vidio Timing Register

    O_VTC(0x6c)          E_VTC(0Xec)

    此寄存器含义不明,使用缺省值

    WriteLocalDWORD( O_VTC,0x00)

    WriteLocalDWORD( E_VTC,0x00)
  • TGLB
    TGLB:Timing Generator Load Byte(0x080)
    含义不明,不做设置

  • 垂直同步计数调节
    VTOTAL:Total Line Count Register
    VTOTAL_LO(0x0B0)  VTOTAL_HI(0x0B4)
    当VTOTAL中的值不为0时,解码器的垂直同步线计数将从原来的525/625线变成设定值
    VTOTAL_HI只有bit0和bit1有效,VTOTAL共10位
    VTOTAL = (水平视频线数 / 帧数) - 1
    VTOTAL = (number of horizontal video lines / frame) - 1
    实际应用中没有设定

3、BT878的精简指令系统

    BT878A内置了一个DMA控制器,使其性能大大增加,它的精简指令引擎可以执行放在内存中的精简指令,从而使DMA控制器可以自动地把图像数据及时地移动到内容缓冲区中,也可以直接移动到显示缓冲区,而这一切不需要CPU的干预。有关这部分的详细内容请看datasheet的2.12 DMA Controller。前面介绍寄存器时提到的启动和停止RISC,指的就是精简指令。下面仅就相关概念做一下介绍。

    经过解码的复合视频数据存储在BT878A的FIFO中,精简指令放在内存中,软件把精简指令的起始地址放在BT878A的寄存器中,然后通过设置寄存器启动DMA,DMA控制器从精简指令中逐条读取指令,并根据指令读取FIFO中的数据,并把数据存储到适当的存储器地址上,整个精简指令集只有五类指令:WRITE、WRITEC、SKIP、SYNC和JUMP。

    说到这里可能不得不介绍一下878的VBI(Vertical Blanking Interval)数据格式,一帧视频图像,NTSC制式有525线,PAL制式有625线,实际BT878A的每帧视频数据的格式如下图:

在DOS下玩转BT878A进行视频采集 - whowin - DOS编程技术

    我们会注意到,在NTSC制式的525行中,1--20行、264--283行并不是视频数据,实际的视频数据只有21--263行以及284--525行,一共只有485行,另外一点就是实际数据流是分奇和偶的,奇数行放在21--263行共243行,偶数行放在284--525行,共242行;PAL制式的情况与NTSC类似,可以自行看下图(这些图截自datasheet)。

 在DOS下玩转BT878A进行视频采集 - whowin - DOS编程技术

    以下我们均易NTSC制式为例,1--9行和264--272行是垂直同步信号数据,这个应该是我们所需要的,不然我们不知道一帧的开始位置,10--20行和273--283行中没有视频数据,但可能会有其他数据,如果只是为了软件编程,我们不必过于深究这个部分,我们更关心的是数据最后存到FIFO里的格式,因为DMA控制器是从FIFO里取数据,对这部分有兴趣的读者可以仔细阅读datasheet。

    视频数据采集的时序图如下,本文就不过多解释了。 

在DOS下玩转BT878A进行视频采集 - whowin - DOS编程技术

    下面这幅图对我们也有一定的意义,所以我们把它截过来,从图中看出,采集的视频数据并不时直接放到了FIFO中,中间还要经过一个视频数据格式转换处理,这一点我们了解就可以了,总之,FIFO中的数据是经过了转换后的数据,相对当然要规范一些。

在DOS下玩转BT878A进行视频采集 - whowin - DOS编程技术

    现在应该可以说FIFO中的数据了,在BT878A中有三个FIFO,其结构如下:

        FIFO1 :70 X 36 bits
        FIFO2 :35 X 36 bits
        FIFO3 :35 X 36 bits

    我们发现,FIFO都是36 bits的,而一个DWORD的视频数据只有32位,那么另外4位被用来存储数据的状态,这四位的含义如下表:

在DOS下玩转BT878A进行视频采集 - whowin - DOS编程技术

     在正常视频数据的前面是FIFO模式,也就是FM1或FM3,正常视频数据的第一组数据的状态字是SOL,最后一组视频数据的状态字是EOL,当奇数域完毕时存一个VRO,偶数域完毕后存一个VRE。我们根据这些就可以考虑我们启动DMA的精简指令应该怎样写。

    在继续往下看之前,希望能认真看一下BT878A的datasheet中第2-38页的2.12.3 RISC Instructions这一节,了解一下BT878A的精简指令,并记得在看到不明白的指令时回来查一下,否则可能会不知所云。

    我们假定BT878A的FIFO工作在packed data模式。首先,一帧数据从奇数行开始,奇数行开始前一定是偶数行结束,偶数行结束时的状态字是VRE,而奇数行开始时应该先有FM1,然后是我们要的数据,这些数据的第一个应该带有SOL,而最后一个应该带有EOL,好了,大致思路应该是这样,用SYNC指令先找到VRE,然后再用一次SYNC指令找到FM1,然后用WRITE指令把SOL和EOL中的数据写我们需要的部分到内存里我们已经申请到的地址中,则奇数行完毕;偶数行类似,用SYNC指令先找到VRO,然后再用一次SYNC指令找到FM1,然后用WRITE指令把SOL和EOL中的数据写我们需要的部分到内存里我们已经申请到的地址中,至于如何在内存中存放数据,当然随自己需要了,当把奇数行和偶数行都搬移一边后,一帧数据处理完毕,我们可以用JUMP指令跳转到开头进行下一帧就可以了。

4、实际范例

    这个例子的条件大致是这样的,我们从BT878A的MUX0输入一个NTSC制式的视频信号(实际来自一个摄像头),我们要把摄像头的视频信号实时地显示在电脑的屏幕上,不做缩放和剪裁,图像分辨率为640 X 480像素,色彩为16位,我们使用BT878A的精简指令通过DMA直接把图像数据写到显存里,按任意键,程序将退出。

    程序使用C++完成,在DJGPP下编译完成,再看这段程序之前,希望读者能了解一些有关VESA的知识,否则其中有些地方可能会不大明白,而且本程序需要在支持VESA的机器上运行,现在一般的机器都是支持VESA的,你的机器是否支持VESA,可以下载下面这个程序检查一下。

    http://blog.hengch.com/software/vesa.exe(2017年3月22日注:链接已修复)

    不带参数运行vesa.exe可以看到你的机器支持的VESA的版本号,也可以看到你的机器支持的所有显示模式,我们的范例中用的显示模式号是:111h,所以要确认在你的显示模式中有这个号,这个模式是640 X 480,16 bits彩色,我们之所以选用这个模式是因为我的测试环境仅支持VESA 1.2,不支持24 bits的彩色模式。运行vesa a可以看到你的机器支持的所有显示模式的详细信息。

    好了,范例程序可以在下面地址上下载:

    http://blog.whowin.net/source/bt878.zip(2017年3月22日注:链接已修复)

    程序由三个文件组成:test878.cc、bt878.h、test878.h,其中bt878.h中只是定义了所有的BT878A的寄存器的偏移;test878.h定义了一些数据结构,两个类:DOS_MEM和VIRTUAL_MEM,以及关于RISC的相关定义。主要的程序代码均在test878.cc中,下面我们的解释主要围绕着test878.cc展开,以思路为主,具体细节请自行看源程序。

    为了方便,程序中的有些地方调用了实模式下的功能,但涉及BT878A的操作部分都是32位的。

    主程序开始CheckBIOS检查机器的BIOS是否支持PCI BIOS,如果不支持,后面函数中的一些调用将不成立;然后调用FindDevice搜寻是否有BT878A芯片,其中的0x036e和0x109e分别是BT878A的device ID和Vendor ID,最后一个参数是device index,BT878A是个多功能设备,但我们范例中只使用function 0,然后调用ReadConfigDWORD读出PCI配置空间的0x10位置的内容,从配置空间的定义看,这是BT878A的基地址,实际上,前面的所有工作就是为了读出这个基地址,通过这个基地址再加上偏移,我们就可以以访问存储器的方式访问所有的BT878A的内部存储器(BT878A是存储器映射的),然后我们用这个基地址初始化了一个VIRUAL_MEM类Bt878MapedAddr,通过这个类,我们将可以访问BT878A的寄存器。

    下面SetMode我们把屏幕的显示模式设成0x111,这个我们在上面已经说过了,这是一个640 X 480,16 bits的显示模式,然后用GetModeInfo调用得到关于这种显示模式下的显示缓冲区的物理地址(32位地址,不是实模式下的段:偏移模式),最后我们以显示缓冲基地址初始化了一个VIRUAL_MEM类scr_virtual,通过这个类,我们可以访问到显示缓冲区,我们对这个类定义的大小是640 X 480 X 2 + 4096 + 16384,前面的640 X 480 X 2是这种显示模式所需要的空间大小,每个象素占2个字节,4096是为了防止显示缓冲区溢出特别增加的4K空间,最后的16384这16K的空间我们准备用来存放RISC指令。

    至此我们有了几个重要的变量,如下:

    VesaPhysAddr          显示缓冲区的物理地址

    Bt878PhisicalAddr     BT878A的基地址

    Bt878MapedAddr        BT878A的寄存器映射类

    scr_virtual           显示缓冲区及RISC虚拟空间类

    下面的很多代码我们在初始化BT878A的寄存器,以期适应我们的需要,这部分请参阅前面有关BT878A寄存器的介绍,

    特别要注意的是create_risc_WxH函数,建立了RISC的指令序列,这个指令序列的思路在前面已经有介绍,请参照这个介绍以及BT878A的datasheet的有关章节来理解这段代码。

    在设置完BT878A的寄存器,建立完RISC的指令序列后,我们启动RISC,你会在屏幕上看到摄像头拍到的连续图像,按任意键将停止RISC的运行,并推出图形显示模式,并退出程序。


  评论这张
 
阅读(2667)| 评论(1)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018