基于PYNQ的MIPI成像平台

作者:Adam Taylor,文章转载自:电路城

我准备在 PYNQ 中创建一个 MIPI 成像平台,我们可以借此使用它来探索 Vitis 视觉库。

介绍
PYNQ 的优势之一是它能够非常轻松地生成高性能应用程序。除了 Ultra96V2 之外,我们看到的大多数 PYNQ 板都与 Zynq 7000 相关(PYNQ Z1、Z2)。

新的 PYNQ ZU 板为我们提供了 MPSOC 类设备和从 FMC 到 SYZYGY、Pmod 和 RPI 的一系列接口。对于图像处理,它具有 HDMI 输入和输出、Mini DP 以及最令我兴奋的 MIPI 接口,可与 Digilent PCAM5C 配合使用。

对于连接,该板为我们提供了 4 个 USB 主机和 1 个 USB 复合接口。

在这个项目中,我们将创建一个使用 MIPI 接口的设计,这将用于捕获帧。这将创建一个平台,使我能够探索 AMD-Xilinx Vitis Vision 库。

首先,我们需要下载 PYNQ ZU 的 PYNQ SD 卡映像并将其写入 SD 卡。这将为我们提供一个 PYNQ 映像,我们可以在板上启动并开始使用。

PYNQ 2.7 版本是在 AMD-Xilinx 工具链的 2020.2 版本中开发的。为了保持兼容性,我们还将在同一工具链中开发应用程序。

设置 Vivado
要开始开发 PYNQ 覆盖,我们首先需要下载并安装 PYNQ ZU 板定义。我们可以从 Xilinx Board Store 获取电路板定义。

https://github.com/Xilinx/XilinxBoardStore克隆存储库

克隆后,将 PYNQZU 板文件从克隆位置复制到 Vivado 安装。

将文件 PYNZU 文件夹复制到 <xilinx install>/Vivado/2020.2/data/boards/board_files

包含板文件后,下一阶段是从https://github.com/Xilinx/PYNQ/tree/image_v2.7克隆 PYNQ 存储库

克隆 PYNQ 存储库后,在 PYNQ\boards\ip\hls 目录下运行 build_ip.bt 或 sh,具体取决于开发机器。

这将构建 HSL IP 内核,以便我们可以在 Vivado 中使用它们

最后阶段是克隆下面的存储库——其中包含几个 TCL 脚本,可用于使用 MIPI 构建图像处理链。

https://github.com/ATaylorCEngFIET/Vivado_Blocks 

创建叠加层
克隆所有存储库后,下一步是在 Vivado 中创建一个项目:

选择新建项目

输入项目名称和位置

选择 RTL 项目并将源留待以后选择

选择 PYNQ ZU 板作为目标

单击完成以创建项目

打开项目后,下一步是创建一个新的框图,从左侧菜单中选择创建框图

保持框图名称不变

在 TCL 控制台中,将目录更改为克隆的 ADIUVO Vivado Blocks 存储库的位置

使用命令获取脚本 mipi_pcam.tcl:

source mipi_pcam.tcl

使用命令创建 PCam MIPI 块:

create_hier_cell_mipi_pcam5 . mipi_pcam

这将创建一个具有所有必要时钟和元素的 MIPI 接口块

展开块以检查内容

打开 IP 目录并选择添加存储库

导航并选择克隆的 PYNQ 目录中的 IP 文件夹

您将看到几个 IP 被检测到并添加到项目中

在 IP 目录中选择 Pixel Pack IP,注意它没有打包用于 MPSOC 设备。

右键单击 IP 块并选择在 IP 打包器中编辑:

这将打开一个带有要编辑的 IP 的新 Vivado 项目,选择兼容性并确保可以为所有设备生成 IP

重新打包IP核并关闭项目

回到主 Vivado 项目,我们现在可以添加 Pixel Pack IP 块

添加像素包IP

添加以下IP以创建最终系统

  • MPSOC - 处理器内核

  • 子集转换器 - 重新映射像素

  • VDMA - 配置为写入

  • AXI 中断控制器

  • 连接块

添加 MPSOC 模块后,运行模块自动化以针对 PYNQ ZU 设置配置处理器

最终设计应如下所示:

在 MIPI 模块中,我们需要将 MIPI IP 连接到设备上的引脚,如下表所示:

我添加了一个恒定输出设置为 1 以始终启用相机。

完成后,我们可以创建 HDL 包装器并构建位文件。

由于我们将把它用于 PYNQ 覆盖,我们只需要 HWH 文件和位文件。

创建笔记本
在 Jupyter 环境中(通过 WIFI 或 USB-以太网连接时,在浏览器中转到 PYNQ:9090)我创建了一个名为 Adiuvo 的新文件夹,并将 bit 和 HWH 移交文件上传到该文件夹。请注意,我将两个文件重命名为相同的名称。

我们还将创建一个笔记本以添加 python 命令。

我们通过 I2C 配置相机,PYNQ ZU 使用 PS I2C0 并使用 I2C 多路复用器连接到多个不同的 I2C 总线。

检查我们的相机已通电并以正确的方式连接到连接器中。我们可以在 PYNQ 中打开一个终端并运行 i2cdetect -l 命令来确定 I2C 通道。相机连接到 I2C Mux 通道 3。这使其成为 I2C-6,运行命令 i2cdetect -r -y 6 将列出网络上的所有 I2C 设备。如果存在,相机应响应地址 0x3C。

主要应用如下:

from pynq import Overlay
from pynq.lib.video import *
import PIL.Image
import cv2
import matplotlib.pyplot as plt
import scipy.ndimage
import matplotlib.image as mpimg
import smbus2
from smbus2 import SMBus, i2c_msg

ol = Overlay("/home/xilinx/jupyter_notebooks/adiuvo/mipi_ol.bit")
ol?

vdma = ol.axi_vdma_0
videomode = VideoMode(1280, 720, 24)

i2c_bus = smbus2.SMBus(6)
Sensor_addr = 0x3c

msg = i2c_msg.write(Sensor_addr, [0x31, 0x00])
i2c_bus.i2c_rdwr(msg)

msg = i2c_msg.read(Sensor_addr, 0x1)
i2c_bus.i2c_rdwr(msg)

data = list(msg)
print("Camera ID is = ",hex(data[0]))


demo = ol.mipi_pcam.v_demosaic_0

mipi = ol.mipi_pcam.mipi_csi2_rx_subsyst_0

cfg = [[0x3008, 0x42],[0x3103, 0x03],[0x3017, 0x00],[0x3018, 0x00],[0x3034, 0x18], [0x3035, 0x11],[0x3036, 0x38],[0x3037, 0x11],[0x3108, 0x01],[0x303D, 0x10],[0x303B, 0x19],[0x3630, 0x2e],[0x3631, 0x0e],[0x3632, 0xe2],[0x3633, 0x23],[0x3621, 0xe0],[0x3704, 0xa0],[0x3703, 0x5a],
[0x3715, 0x78],[0x3717, 0x01],[0x370b, 0x60],[0x3705, 0x1a],[0x3905, 0x02],[0x3906, 0x10],[0x3901, 0x0a],[0x3731, 0x02],[0x3600, 0x37],[0x3601, 0x33],[0x302d, 0x60],[0x3620, 0x52],[0x371b, 0x20],
[0x471c, 0x50],[0x3a13, 0x43],[0x3a18, 0x00],[0x3a19, 0xf8],[0x3635, 0x13],[0x3636, 0x06],[0x3634, 0x44],[0x3622, 0x01],[0x3c01, 0x34],[0x3c04, 0x28],[0x3c05, 0x98],[0x3c06, 0x00],[0x3c07, 0x08],
[0x3c08, 0x00],[0x3c09, 0x1c],[0x3c0a, 0x9c],[0x3c0b, 0x40],[0x503d, 0x00],[0x3820, 0x46],[0x300e, 0x45],[0x4800, 0x14],[0x302e, 0x08],[0x4300, 0x6f],[0x501f, 0x01],[0x4713, 0x03],[0x4407, 0x04],
[0x440e, 0x00],[0x460b, 0x35],[0x460c, 0x20],[0x3824, 0x01],[0x5000, 0x07],[0x5001, 0x03]]

for cmd in cfg:
    #print(hex(cmd[0]))
    #print(hex(cmd[1]))
    first = cmd[0].to_bytes(2,'big')
    #print(hex(first[0]), hex(first[1]), hex(cmd[1]))
    msg = i2c_msg.write(Sensor_addr, [first[0],first[1],cmd[1]])
    i2c_bus.i2c_rdwr(msg)

awb = [[0x518d ,0x00],[0x518f ,0x20],[0x518e ,0x00],[0x5190 ,0x20],[0x518b ,0x00],[0x518c ,0x00],[0x5187 ,0x10],[0x5188 ,0x10],
[0x5189 ,0x40],[0x518a ,0x40],[0x5186 ,0x10],[0x5181 ,0x58],[0x5184 ,0x25],[0x5182 ,0x11],[0x3406 ,0x00],[0x5183 ,0x80],[0x5191 ,0xff],
[0x5192 ,0x00],[0x5001 ,0x03]]

for cmd in awb:
    #print(hex(cmd[0]))
    #print(hex(cmd[1]))
    first = cmd[0].to_bytes(2,'big')
    #print(hex(first[0]), hex(first[1]), hex(cmd[1]))
    msg = i2c_msg.write(Sensor_addr, [first[0],first[1],cmd[1]])
    i2c_bus.i2c_rdwr(msg)

res_720p = [[0x3008, 0x42],    [0x3035, 0x21],[0x3036, 0x46],    [0x3037, 0x05],    [0x3108, 0x11],[0x3034, 0x1A],    [0x3800, (0 >> 8) & 0x0F],
[0x3801, 0 & 0xFF],[0x3802, (8 >> 8) & 0x07],[0x3803, 8 & 0xFF],[0x3804, (2619 >> 8) & 0x0F],[0x3805, 2619 & 0xFF],
[0x3806, (1947 >> 8) & 0x07],[0x3807, 1947 & 0xFF],[0x3810, (0 >> 8) & 0x0F],[0x3811, 0 & 0xFF],[0x3812, (0 >> 8) & 0x07],
[0x3813, 0 & 0xFF],[0x3808, (1280 >> 8) & 0x0F],[0x3809, 1280 & 0xFF],[0x380a, (720 >> 8) & 0x7F],[0x380b, 720 & 0xFF],
[0x380c, (1896 >> 8) & 0x1F],[0x380d, 1896 & 0xFF],[0x380e, (984 >> 8) & 0xFF],[0x380f, 984 & 0xFF],[0x3814, 0x31],
[0x3815, 0x31],[0x3821, 0x01],[0x4837, 36], [0x3618, 0x00], [0x3612, 0x59],[0x3708, 0x64],[0x3709, 0x52],[0x370c, 0x03],
[0x4300, 0x00],[0x501f, 0x03],[0x3008, 0x02]]

for cmd in res_720p:
    #print(hex(cmd[0]))
    #print(hex(cmd[1]))
    first = cmd[0].to_bytes(2,'big')
    #print(hex(first[0]), hex(first[1]), hex(cmd[1]))
    msg = i2c_msg.write(Sensor_addr, [first[0],first[1],cmd[1]])
    i2c_bus.i2c_rdwr(msg)

demo.write(0x10,1280)
demo.write(0x18,720)
demo.write(0x28,0x03)
demo.write(0x00,0x81)

pixel_in = ol.pixel_pack_0
pixel_in.bits_per_pixel = 24

mipi = ol.mipi_pcam.mipi_csi2_rx_subsyst_0
op =mipi.read(0x60)
print("virtual channel 0 status =", hex(op))

cam_vdma = ol.axi_vdma_0
lines = 720
framemode = VideoMode(1280, lines, 24)
cam_vdma.readchannel.mode = framemode
cam_vdma.readchannel.start()

cam_vdma.readchannel.running
cam_vdma.readchannel.mode

frame_camera = cam_vdma.readchannel.readframe()
frame_color=cv2.cvtColor(frame_camera,cv2.COLOR_BGR2RGB)
pixels = np.array(frame_color)
plt.imshow(pixels)
plt.show()

运行后,它将向我们展示 Jupyter 笔记本中的图像:

最后

现在,我们有了一个可以运行的图像平台,我们可以有办法来探索 Vitis 视觉库了。

如果您对此项目有任何想法、意见或问题,请在下方留言。

免责声明:本文转载于网络,转载此文目的在于传播相关技术知识,版权归原作者所有,如涉及侵权,请联系小编删除(联系邮箱:service@eetrend.com )。

最新文章

最新文章