zoukankan      html  css  js  c++  java
  • 利用AXI VDMA实现OV5640摄像头采集笔记(二)

    导读:摄像头采样图像数据后经过VDMA进入DDR,通过PS部分控制,经过三级缓存,将DDR中保持的图形数据通过VDMA发送出去。在FPGA的接收端口产生VID OUT时序驱动HDMI显示器显示图形。

    一、 基础知识点

    1、OV5640和VDMA之间同步信号的配置,输入端采样视频流协议中的tuser作为同步信号。

    2、VDMA主要端口:

    (1)、S_AXI_Lite:寄存器配置接口,用于软件配置VDMA,并读取状态信息。

    (2)、S_AXI_S2MM:视频流入端,接收外来的视屏数据。

    (3)、M_AXI_MM2S:AXI4全协议主端,从DDR中读取数据给M_AXI_MM2S。

    (4)、M_AXI_S2MM:AXI4全协议主端,从DDR中读取的数据给M_AXI_S2MM

    (5)、M_AXIS_MM2S:视频流接口,向外发送数据。

    3、PL中的DMA IP对于DDR和PL存储资源来说是主机(Master),DMA对于PS来说是从机(Slave),PS通过AXI_Lite配置DMA的寄存器从原地址向目的地址转移多少数据,DMA通过AHP读写DDR,通过AXI读写PL资源。

    4、在ZYNQ芯片内部,PS和PL是共享DDR控制器的,PS访问DDR只要操作DDR虚拟地址的应设计可,对于PL,要接入DDR,必须通过AXI_HP端口。

    二、框架分析

    1、PS配置

    (1)、processing_system7_0

    PS模块处理单元,选择的是端口M_AXI_GP0,用于通过AXI-Lite总线配置外设,引出IIC总线,硬件中已经将其搭到IIC总线,同时引出两个时钟FCLK_CLK0(100Mhz)作为系统时钟,FCLK_CLK1(25Mhz)作为OV640摄像头驱动时钟,同时引出FCLK_RESET_N复位信号,用于系统中断时对系统进行复位。引出PS侧的DDR管脚,由此可以通过DMA读写DDR,同时PL侧DMA可以通过AXI-HP总线读取DDR的数据。对于两个AXI总线(S_AXI_HP0和M_AXI_GP0),两个时钟工作在相同的时钟下,都连接到FCLK_CLK0(100Mhz)。

    此外,PS侧开启中断输入,检测中断。

    (2)VDMA

    输出部分:

    VDMA数据接口可以分为读写通过,通过M_AXI_S2MM端口将数据流写入DDR3。通过M_AXI_MM2S端口可以从DDR3读取数据,并通过M_AXI_MM2S端口将DDR3中读取出的数据以AXIS_Stream类型输出。

    VDMA与Zynq processor通过AXI_Interconnect进行连接。

    输入部分:

    在VDMA的IP配合部分,通过ZYNQ processor的M_AXI_GP接口进行配置。

    视频数据输入是OV5640的数据经过video to Stream核实现的数据输入。对于这些AXI协议类型的总线,需要同步于ZYNQ自带的GP时钟,也就是ACLK,S00_ACLK,M00_ACLK,S01_ACLK都要用FCLK_CLK0时钟。

    (3)Processor System Reset

    输入信号:

    系统复位处理器,slowest_sync_clk最小同步时钟,复位信号与时钟进行同步。复位输入信号是ZYNQ processor的FCLK_RESET_N。

    输出信号

    interconnect_aresetn:互联复位信号,连接到各个AXI connect连接模块的复位信号输入。

    peripheral_aresetn:外围模块复位信号,作为其他各个外设模块的AXI总线复位信号输入。

    三、代码分析

    1、 关于XiixPs_MasterSendPolled()函数

    该函数在主模式下启动轮循(Polled)模式发送,讲数据发送到Fifo,并等待从机接收数据,如果设备无法送fifo中读取数据,导致发送超时而失败。

    1.1     传入参数:

    s32 XIicPs_MasterSendPolled(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount, u16 SlaveAddr)

    @ 指向XiicPs实例结构的指针

    @指向发送缓存区的指针

    @要发送的字节数

    @从设备的地址

    1.2 语句1

    if (((InstancePtr->IsRepeatedStart) != 0) ||

            ((ByteCount > XIICPS_FIFO_DEPTH) != 0U)) {

            XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,

                    XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |

                            (u32)XIICPS_CR_HOLD_MASK);

    }

    作用:

    判断实例结构中时都设置为重复启动或者要放的数据的长度大于FIFO的深度。

    由于条件1、2不成立,所以不会执行将IIC控制寄存器的bit4置一,该位的作用是:

    0:在接收、发送完毕后,允许主机终止发送

    1:在没有数据接收与发送时,保持时钟线为低直到主机操作

    1.2     语句2 建立主机模式

    (void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE);

    SENDING_ROLE=1;

    1.3     将所有的错误中断进行关联

    Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TX_OVR_MASK |      (u32)XIICPS_IXR_NACK_MASK;

    1.4     启用中断前将所有中断标志位清除

    IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);

      XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);

    采用的方式为先读取中断标志寄存器,然后将读出的数据再进行写入,寄存器类型为wtc,即write true clear,由此清除标志位。

    1.5     清空发送fifo

       (void)TransmitFifoFill(InstancePtr);

    然后写入0V5640的地址,再将要发送的数据经过fifo发送出去。

    1.6     等待发送完毕

    while (XIicPs_BusIsBusy(InstancePtr));

    查询IIC状态寄存器的发送标志位,0x04,改为为1的话表示发送完毕,结束该部分。

    2、 对于VDMA的配置

    (1)、Xil_Out32((VDMA_BASEADDR + 0x030), 0x108B);// enable circular mode

    该地址对应的是S2MM VDMA控制寄存器,写入0x108B,即0x1000_1000_1010。该寄存器用于控制VDMA S2MM,可以实现复位、使能锁相同步、设定帧存切换模式、启动VDMA读写通道等操作。

    bit3使能锁相同步或者动态锁相同步模式

    0:关闭Genlocken和动态Genlock同步。

    1:开启Genlock或Genlock同步

    该位仅在通道配置为锁相同步从接口或者动态锁相从接口时才起作用。配置成锁相同步主接口时,该位为保留位,恒为0.

    bit2:先将通道进行复位

     0:正常操作

    1:复位S2MM通道。

    bit1:制定帧存为循环模式还是停留模式。

    0:停留模式,显示用缓存也将停留在指定的帧存

    1:循环模式,循环切换显示用缓存页。

    bit0:控制VDMA的运行和停止,开始任何VDMA操作前,该位必须置一。

    0:停止

    1:运行

    (2)、设置帧存的起始地址

    Xil_Out32((VDMA_BASEADDR + 0x0AC), VIDEO_BASEADDR0);  // start address

        Xil_Out32((VDMA_BASEADDR + 0x0B0), VIDEO_BASEADDR1);  // start address

        Xil_Out32((VDMA_BASEADDR + 0x0B4), VIDEO_BASEADDR2);  // start address

    S2MM_START_ADDRESS(1-16):Ach~E8h 

    以上几位用于设置S2MM的帧存地址。有最多32个寄存器用于存放帧存的起始地址,其分布在两个bank上:bank0和bank1,每个bank上有16个寄存器。

    存储地址如何配置:

    本次例程中用到了三帧缓存,采用了VIDEO_BASEADDR0、VIDEO_BASEADDR1、VIDEO_BASEADDR2三个地址,三个基地址的分配取决于输入和输出的位宽,DDR3的地址是0x0000_0000,实际使用时最好偏移0x0100_00000的地址来存储应用的数据,因为程序在运行过程中需要一些内存来运行,为了保证不冲突,对其加一个偏移量。设置的空间还要保证可以存储一帧数据,例如使用1280*720*4=3,686,400,转换成16进制为384,000,0x0100_0000+0x0138_4000,小于VIDEO_BASEADDR1的基地址0x0200_0000,因此分配是合理的。

    (3)、设置帧延迟和跨度寄存器

    Xil_Out32((VDMA_BASEADDR + 0x0A8), (H_STRIDE*3)); //h offset (H_STRIDE* 3) bytes

    S2MM帧延迟和跨度寄存器,该寄存器有两个作用,

    bit24~bit28:

    仅用于Genlock从模式,bit24~bit28指定从接口要比主接口延迟多少个帧。

    bit15~bit0:

    低16位指定水平方向的跨度,同样以字节为单位。跨度指的是每两行第一个像素之间间隔的数据个数,可参见VDMA帧存格式。

    (4)、设置S2MM_HSIZE S2MM水平方向尺寸

    该寄存器的低16bit用于指定每一行有多少个字节的数据要传输,例如显示分辨率为640*480,每个像素4个字节(RGB+Alpha),其值应该设置为640*4

    Xil_Out32((VDMA_BASEADDR + 0x0A4), (H_ACTIVE*3));     // h size (H_ACTIVE * 3) bytes

    对于纯24bit的RGB数据,每一行有1280x3个字节,所以向寄存器写入行有效数x3。

    (5)、设置S2MM_VSIZE垂直方向尺寸

    Xil_Out32((VDMA_BASEADDR + 0x0A0), V_ACTIVE);         // v size (V_ACTIVE)

    该寄存器的低13bit用于指定总共有多少行。

    3、 对于DDR3读取数据的配置

    3.1 MM2S VDMA控制寄存器配置

    Xil_Out32((VDMA_BASEADDR + 0x000), 0x8B);     // enable circular mode

    0x8B:  0x1000_1010

    bit3:使能锁相同步或者动态锁相同步模式。

    0:关闭锁相同步或者动态锁相同。

    1:开启锁相同步或者动态锁相同。

    bit2:复位控制

    0:正常操作

    1:复位MM2S通道

    bit1:指定帧存为循环模式还是停留模式

    0:停留模式,显示用的缓存页将停留在指定的帧存。

    1:循环模式,循环切换显示用缓存页。

    bit0:运行/停止控制,控制VDMA通道的停止和运行。

    0:停止。

    1:运行。

    3.2 设置MM2S帧存起始地址

    Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); // start address

    Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); // start address

    Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR2); // start address

    3.3 设置MM2S帧延迟和跨度寄存器

    该寄存器仅适用于Genlock从模式,指定从接口要比主接口至少延迟多少个帧。低16位指定水平方向上的跨度,以字节为单位。

    Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3));         // h offset (H_STRIDE * 3) bytes

    3.4 MM2S水平方向显示大小寄存器设置

    低16bit指定每一行有多少字节的数据要进行传输。

    Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3));         // h size (H_ACTIVE * 3) bytes

    3.5 MM2S垂直方向显示大小寄存器配置

    该寄存器的低13位指定总共有多少行要进行显示

    Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE);         // v size (V_ACTIVE)

  • 相关阅读:
    168. 吹气球
    395. 硬币排成线 II
    436. 最大正方形
    362. 滑动窗口的最大值(单调队列)
    python-网络安全编程第二天(文件操作)
    重闯Sqli-labs关卡第一天(1-4关)
    python-网络安全编程第一天(requests模块)
    PHP代码审计入门(SQL注入漏洞挖掘基础)
    PHP代码审计入门(敏感函数回溯参数过程)
    PHP核心配置基础解读
  • 原文地址:https://www.cnblogs.com/luxinshuo/p/11615573.html
Copyright © 2011-2022 走看看