[ZYNQ Linux] V4L2视频驱动

[ZYNQ Linux] V4L2视频驱动

文章目录

数据流搭建过程及问题

HDMI输入 → ADV7611解码 →AXIVDMA →PS

  1. ADV7611复位接到了PL需要进行复位

    使用一个axi gpio进行复位,axi gpio会注册一个gpiochip出来。

    bash 复制代码
    echo 507 > /sys/class/gpio/export
    echo out > /sys/class/gpio/gpio507/direction
    echo 0 > /sys/class/gpio/gpio507/value
    sleep 1
    echo 1 > /sys/class/gpio/gpio507/value
  2. ADV7611初始化

    配置一些寄存器

  3. i2cdetect -r -y扫描不到0x4C等地址,不过可以读到数据

    可能是设备没有对i2cdetect的扫描进行响应
    不要过度依赖和相信工具

    bash 复制代码
    i2cget -y 1 0x4c 0x42
  4. EDID设置

    问题:插电脑不能识别屏幕。

    HDMI接口的I2C没连接到解码芯片,需要连接到一个EEPROM的IP核。

    EDID IP核需要配置1080的文件,里面的名称不能空着

    添加之后连接到电脑,电脑会识别到一个屏幕。

  5. 时钟和缓冲问题

    问题:VDMA传输的一帧图像只有前几行有效,其余全0。

    数据流程中有一个IP核把视频时序转为了AXI Stream格式,需要考虑写入速率(写入时钟)和读取速率(读出时钟)的问题。

    位宽一样的话,读出时钟的频率要高于写入时钟。

    输入是148.5M(1080p60帧),AXIVDMA的时钟也要高于或者等于该时钟。

    还要考虑缓冲区设置多大比较合适。

AXI VDMA图像接收思路

  1. 通过DMA接口申请连续的内存块(DMA传输需要特殊的内存块,不能是malloc常规申请的)
  2. 调用DMA接口接收数据到内存空间
  3. 内核空间提供mmap接口供用户空间使用
  4. 用户空间调用mmap获取图像

常规的图像接收思路原理都是类似的,提供的API接口或者使用方式有所区别。

还会有缓冲池、缓冲区、队列等等的区别。

V4L2驱动

V4L2驱动------视频设备

接口 说明
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); 注册 V4L2 设备实例,管理设备的数据结构,用户空间不可见
int video_register_device(struct video_device *vdev,int type, int nr); 注册视频设备,由V4L2设备进行管理即和上面注册v4l2设备关联,创建/dev/videoX的设备节点
struct video_device 视频设备结构体,设备的核心操作接口fops和ioctl_ops,图像队列queue
const struct v4l2_file_operations *fops; open、release、mmap等,内核有通用的vb2_fop_mmap操作接口等等
const struct v4l2_ioctl_ops *ioctl_ops; ioctl操作,一大堆,需要实现一些关键的
struct vb2_queue *queue; 管理视频缓冲区的队列,一个缓冲区或者缓冲池
内核fops通用接口
v4l2_fh_open 打开文件
vb2_fop_release 释放与视频缓冲区相关的资源
vb2_fop_mmap 内存映射
vb2_fop_poll poll机制
内核ioctl_ops通用接口
vb2_ioctl_create_bufs 创建视频缓冲区
vb2_ioctl_expbuf 导出视频缓冲区,可以与其他设备或进程共享内存
ioctl_ops用户实现
vidioc_querycap 查询设备能力
vidioc_g_fmt_vid_cap 当前数据格式,包括分辨率、像素格式等参数 get format
vidioc_try_fmt_vid_cap 尝试设置视频捕获格式,但不实际应用
vidioc_s_fmt_vid_cap 设置视频捕获设备的格式(如分辨率、像素格式)set format
vidioc_enum_fmt_vid_cap 枚举视频捕获设备支持的像素格式 enum format
vidioc_reqbufs 申请视频缓冲区内存
vidioc_querybuf 查询已申请缓冲区的详细信息,包括内存偏移、长度等
vidioc_qbuf 将缓冲区放入队列或队列 queue buffer
vidioc_dqbuf 从队列中取出缓冲区 dequeue buffer
vidioc_streamon 启动视频流传输
vidioc_streamoff 停止视频流传输
struct vb2_queue 缓冲区队列需要实现对队列的操作vb2_ops,内存操作mem_ops
queue_setup 初始化视频缓冲区队列,分配缓冲区内存
buf_prepare 准备缓冲区数据
buf_queue 将缓冲区加入队列
start_streaming 启动流式传输
stop_streaming 停止流式传输,清空缓冲区队列
wait_prepare 等待缓冲区准备完成
wait_finish 等待缓冲区处理完
vb2(video buffer)的通用接口 vb2_fop层、vb2_ioctls层、vb2层、vb2_ops层
vb2_ops_wait_finish 等待所有缓冲区操作完成
vb2_ops_wait_prepare 等待所有缓冲区准备完成
vb2_plane_vaddr 获取缓冲区的虚拟地址
vb2_plane_size 获取缓冲区平面的大小
vb2_set_plane_payload 设置缓冲区平面的有效载荷大小
vb2_is_busy 检查缓冲区队列是否正忙
vb2_reqbufs 请求分配缓冲区
vb2_querybuf 查询缓冲区信息
vb2_qbuf 将缓冲区加入队列
vb2_dqbuf 从队列中取出缓冲区
vb2_streamon 启动流式传输
vb2_streamoff 停止流式传输
vb2_buffer_done 标记缓冲区处理完成
vb2_dma_contig_plane_dma_addr 获取DMA连续缓冲区的物理地址
vb2_is_streaming 检查是否正在流式传输
vb2_queue_init 初始化视频缓冲区队列
vb2_dma_contig_memops DMA内存操作集合,可传给mem_ops
ioctl_ops、vb2层与vb2_ops的调用关系

方向:用户空间 → ioctl_ops → vb2层 → vb2_ops。

  • 用户空间通过ioctl系统调用,传递到驱动中定义的ioctl_ops结构体
  • ioctl_ops调用vb2层,如vidioc_streamon等会进一步调用vb2层的通用接口vb2_streamon
  • vb2层回调驱动实现的vb2_ops,vb2_streamon回调start_streaming

分工:

  • ioctl_ops处理用户请求的解析与转发。
  • vb2层实现通用缓冲区管理逻辑。
  • vb2_ops由驱动实现硬件细节。

AXI VDMA实现细节

将缓冲区加入图像队列------buf_queue
  1. 获取缓冲区的物理地址,用于DMA传输------vb2_dma_contig_plane_dma_addr
  2. 准备交错传输DMA描述符,需要设置DMA地址等参数------dmaengine_prep_interleaved_dma
  3. 设置描述符的回调函数,如果DMA传输完成就把缓冲区标记为有图像数据(VB2_BUF_STATE_DONE)
  4. 将描述符提交到 DMA 控制器的工作队列(提交未开始)------dmaengine_submit
  5. 把缓冲区放到活动缓冲区队列里面(后续获取图像就是从这个队列里面获取)
  6. 触发 DMA 控制器开始执行已提交的传输------dma_async_issue_pending
启动流式传输------start_streaming
  1. 让DMA开始工作就行------dma_async_issue_pending
停止流式传输,清空缓冲区队列------stop_streaming
  1. 结束DMA传输------dmaengine_terminate_all
  2. 将活动缓冲区队列里面的缓冲区设置为无效(VB2_BUF_STATE_ERROR)
设备树设计
c 复制代码
    vdma_capture: vdma-capture {
        compatible = "xlnx,axi-vdma-capture";
        dmas = <&axi_vdma_1 1>;
        dma-names = "port0";
    };

用户空间使用

代码使用步骤

  1. 打开设备:使用open()函数打开V4L2设备文件,通常是/dev/video0。根据需要,可以选择阻塞或非阻塞模式。
  2. 查询设备能力:通过VIDIOC_QUERYCAP命令获取设备的能力,例如是否支持视频采集(V4L2_CAP_VIDEO_CAPTURE)和流式I/O(V4L2_CAP_STREAMING)。
  3. 设置图像格式:使用VIDIOC_S_FMT命令配置采集的图像格式,包括宽度、高度和像素格式(如YUYV、MJPEG等)。这是决定图像数据如何组织的关键一步。
  4. 申请与映射缓冲区:
  • 申请缓冲区:通过VIDIOC_REQBUFS向驱动程序申请若干个视频缓冲区。
  • 映射缓冲区:对于内存映射(mmap)方式,使用VIDIOC_QUERYBUF查询每个缓冲区信息,然后通过mmap()系统调用将内核空间的缓冲区映射到用户空间,以便应用程序可以直接访问。这是最常用且高效的方式。
  • 缓冲区入队:在开始采集前,需要通过VIDIOC_QBUF命令将所有空闲的缓冲区放入驱动层的输入队列中。
  1. 开始/停止视频流:
  • 通过VIDIOC_STREAMON命令启动视频采集,驱动程序开始向队列中的缓冲区填充数据。
  • 在循环中,使用VIDIOC_DQBUF从驱动层的输出队列中取出已填充好数据的缓冲区。应用程序处理完该帧数据后,必须再次调用VIDIOC_QBUF将其放回输入队列,以便驱动程序循环使用。
  • 采集完成后,通过VIDIOC_STREAMOFF命令停止视频流。
  1. 关闭设备:使用close()函数关闭设备文件,并释放所有资源。

命令行工具 v4l2-ctl

TODO

  1. 解码芯片在用户空间单独进行初始化和配置,未加入到驱动框架里边。可以使能内核相关驱动,作为v4l2子设备添加。
  2. 联动Framebuffer、DRM进行图像输出。

其它问题

内核有个VVIP的驱动,不是给AXI-VDMA用的,是给另外的IP核用的,不能直接使用。

  1. no VDMA channel found(VVIP驱动)

没找到DMA通道,设备树通道有问题,1是写,从DMA向外写(S2MM)。

bash 复制代码
[    2.246473] xilinx-video axi-video-cap: no VDMA channel found
[    2.251733] xilinx-video axi-video-cap: /axi-video-cap/ports/port@0 initialization failed
[    2.259907] xilinx-video axi-video-cap: DMA initialization failed

\linux-xlnx\Documentation\devicetree\bindings\dma\xilinx\xilinx_dma.txt

bash 复制代码
* DMA client

Required properties:
- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
	where Channel ID is '0' for write/tx and '1' for read/rx
	channel. For MCMDA, MM2S channel(write/tx) ID start from
	'0' and is in [0-15] range. S2MM channel(read/rx) ID start
	from '16' and is in [16-31] range. These channels ID are
	fixed irrespective of IP configuration.

- dma-names: a list of DMA channel names, one per "dmas" entry
  1. no subdev found in graph(VVIP驱动)

没有子设备,v4l2驱动需要子设备构成一个完整的数据流

bash 复制代码
[    2.253037] xilinx-video axi-video-cap: /axi-video-cap/ports/port@0 initialization failed
[    2.260727] xilinx-video axi-video-cap: DMA initialization failed
[    4.528033] xilinx-video axi-video-cap: no subdev found in graph
[    4.539722] xilinx-video: probe of axi-video-cap failed with error -2
  1. 创建一个虚拟的子设备可以创建成功,但还是不能用(VVIP驱动)
    驱动里面还是用不了,没能成功获取到图像格式,需要另一个IP核的支持。
bash 复制代码
[    2.373491] xilinx-video axi-video-cap: /axi-video-cap/ports/port@0 initialization failed
[    2.381152] xilinx-video axi-video-cap: DMA initialization failed
[    4.646649] xilinx-video axi-video-cap: device registered
相关推荐
s09071363 天前
【Zynq 进阶一】深度解析 PetaLinux 存储布局:NAND Flash 分区与 DDR 内存分配全攻略
linux·fpga开发·设备树·zynq·nand flash启动·flash分区
s09071363 天前
【Zynq开发避坑指南】PetaLinux核心配置与 Vivado DMA 地址分配深度解析
内存·zynq·petalinux·地址映射
s09071366 天前
ZYNQ无SD卡纯NAND Flash启动Linux全攻略
linux·fpga开发·zynq·nand flash启动
s090713611 天前
保姆级教程十二:USB摄像头接入!ZYNQ+OpenCV+FPGA硬件加速图像处理实战(视觉终极篇)
图像处理·opencv·fpga开发·zynq·硬件加速
s090713618 天前
保姆级教程一:ZYNQ-7030开发板安装/烧录Linux系统详细指南(小白必看)
linux·fpga开发·系统安装·zynq
s09071362 个月前
基于ZYNQ-7000 ARM端的水声声呐图像压缩方案
arm开发·zynq·图像压缩·水声工程
YprgDay3 个月前
Vitis固化Zynq流程
zynq
jz_ddk3 个月前
[实战] Zynq-7000 PCAP接口完全指南
fpga·ps·zynq·pcap·pl
whik11943 个月前
Xilinx ZYNQ-7000系列FPGA选型指南
fpga·xilinx·zynq·选型·高速接口·资源