十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32MP257 DCMIPP并行接口BT.656视频采集调试实战

STM32MP257 DCMIPP并行接口BT.656视频采集调试实战 最近在STM32MP257F-EV1评估板上调DCMIPP用并行接口接收BT.656格式的视频流从硬件连接到内核配置从设备树到V4L2采集整个流程完整走了一遍。这个需求在工业视觉、安防和视频采集类项目里非常典型但网上资料大多讲MIPI CSI-2很少讲到BT.656这种内嵌同步的并行信号。写这篇东西就是把我在评估板上实际调试的过程、设备树参数怎么定、media pipeline怎么配、常见坑有哪些一次性整理出来给后来的人省点时间。适合正在做STM32MP25系列视频采集、或者想把老式模拟解码芯片接到新平台上的工程师参考。1. 项目概述与整体设计思路1.1 为什么是DCMIPP而不是老一代DCMISTM32MP1系列老平台上有个视频采集外设叫DCMI大家比较熟悉。到了STM32MP25这一代ST把DCMI升级成了DCMIPP全称是Digital Camera Memory Interface Pixel Processor。别看名字只多了个Pixel Processor这意味着它不只是把数据收进来写进内存这么简单而是在接收前端里多了一块可以做像素级处理的硬件模块支持裁剪、缩放、格式转换等操作。对实际项目来说最直接的好处是可以在采集的同时把图像缩小一块再送入内存或送显示减少了CPU和GPU的后续开销对带宽紧张的场景很有用。驱动层面差别也很大。老的DCMI在Linux里走的是普通的V4L2 video device而DCMIPP走的是media controller框架subdev和video node之间需要显式配置链路。内核源码里对应的是drivers/media/platform/st/stm32/stm32-dcmipp.c这个文件如果你在STM32MP257F-EV1上打开SDK源码可以在里面看到完整的驱动实现。我一开始按老DCMI的思路去配结果发现media链路根本起不来浪费了不少时间。DCMIPP支持两种输入路径一种是MIPI CSI-2另一种就是并行接口。BT.656走的是并行接口路径数据线8位加一根像素时钟信号格式上是完全兼容的。所以“DCMIPP 并行接口 BT.656”这个组合在硬件上其实并不复杂真正的复杂度在于协议理解、设备树参数和V4L2链路配置。1.2 BT.656协议到底在传什么BT.656是ITU-R BT.656标准定义的数字视频接口格式广泛用于模拟视频解码芯片输出数字YCbCr信号。它和BT.601最大的区别就是省掉了HSYNC和VSYNC两根同步线行场同步信息通过一组内嵌的定时基准码嵌在数据流里。具体来说BT.656的数据格式是8bit YCbCr 4:2:2按“Cb Y Cr Y”的顺序排列也就是V4L2里的UYVY。每行数据从EAV码开始接着是水平消隐样本然后是SAV码最后才是有效视频数据。EAV和SAV都是四个字节的定时基准码FF 00 00 XY。其中XY字节里包含了F场标识、V消隐标识、H行标识以及保护位信息解码端靠这些位来判断当前数据处于哪个场、是否处于消隐区以及当前是有效行还是消隐行。理解这个协议对调试至关重要。我举个例子BT.656在PAL制式下每帧625行其中有效行是576行每行总像素含消隐大约是864个像素时钟像素时钟典型值是13.5MHz。NTSC制式则是525行有效行480行每行总像素约858个像素时钟。同样的13.5MHzPAL帧率25fpsNTSC帧率30fps。在设备树和V4L2设置里这些参数都会直接出现。参数PALBT.656NTSCBT.656总行数625525有效行数576480每行总像素含消隐约864约858有效像素每行720720帧率25fps隔行30fps隔行像素时钟13.5MHz13.5MHz把这张表记牢后面遇到“为什么subdev格式是720x625”这类问题就不会懵了。1.3 评估板上的硬件连接怎么接STM32MP257F-EV1评估板通过扩展连接器把DCMIPP的并行接口引出来了包括D0到D7八根数据线、PIXCLK像素时钟以及可选的HSYNC和VSYNC。接BT.656信号时理论上只需要D0到D7和PIXCLK四组信号就够了HSYNC和VSYNC可以不接。但这里有个前提输出端必须确实工作在BT.656内嵌同步模式。很多视频解码芯片比如常见的TVP5150、ADV7180、ADV7280都支持通过I2C寄存器在BT.601离散同步和BT.656内嵌同步之间切换。默认出厂状态未必是BT.656如果你用I2C工具把寄存器配成BT.601模式DCMIPP这边又没有独立同步线可用那就彻底收不到行场信息表现出来就是V4L2一直超时一个buffer都没有。这一点在排查无信号问题时是第一个要确认的。电平匹配也要留意。早期的模拟解码芯片输出往往是3.3V逻辑电平STM32MP257F的DCMIPP引脚电平可能配置为1.8V或者3.3V接错轻则采不到数据重则损伤引脚。我在评估板上直接用3.3V电平没问题但如果你的解码芯片输出5V电平中间必须加电平转换不能直接怼到MPU引脚上。2. 内核配置与设备树让DCMIPP先跑起来2.1 先把DCMIPP驱动编译进内核在STM32MP257F-EV1上ST的OpenSTLinux SDK默认内核通常已经包含了DCMIPP驱动但如果你用的是自己裁剪的内核或者从mainline kernel拉下来配置的就需要确认CONFIG_VIDEO_STM32_DCMIPP这个选项有没有打开。配置路径在menuconfig里大概是Device Drivers - Multimedia support - Media drivers - STM32 Digital Camera Memory Interface Pixel Processor。它依赖于VIDEO_DEV、MEDIA_CONTROLLER、MEDIA_SUPPORT这些基础选项这些一般默认都有。另外你用的解码芯片驱动也要编进内核或者作为模块加载比如CONFIG_VIDEO_TVP5150、CONFIG_VIDEO_ADV7180这取决于你的硬件方案。我推荐先把DCMIPP和解码芯片都编进内核y这样启动后设备节点一定是存在的后续调试少一层变量。等整个链路通了再改成模块也不迟。编译完启动后检查/dev/下有没有video0设备同时看看/media0是否存在。如果没有media0多半是内核的media controller支持或者dcmipp驱动没有正确probe优先回头检查设备树。2.2 设备树endpoint参数逐个拆解设备树是BT.656并行接口调试里最容易出问题的地方。DCMIPP节点在设备树里通过port/endpoint来描述与外部解码芯片的连接remote-endpoint指向解码芯片输出端的endpoint。下面是一个通用结构具体以你使用的内核版本和SDK里的dtsi为准dcmipp { pinctrl-names default, sleep; pinctrl-0 dcmipp_pins; pinctrl-1 dcmipp_sleep_pins; status okay; port { dcmipp_ep: endpoint { remote-endpoint bt656_sensor_ep; bus-width 8; bus-type 2; /* V4L2_MBUS_BT656 */ pclk-sample 1; }; }; }; i2c2 { bt656_sensor: video-decoder20 { compatible your,decoder-compatible; reg 0x20; reset-gpios gpioi 5 GPIO_ACTIVE_LOW; status okay; port { bt656_sensor_ep: endpoint { remote-endpoint dcmipp_ep; bus-width 8; bus-type 2; pclk-sample 1; }; }; }; };逐个解释关键参数。bus-width表示并行数据线位宽BT.656一般是8。bus-type是关键中的关键它告诉DCMIPP当前使用的是哪种总线类型。对于BT.656内核头文件include/dt-bindings/media/video-interfaces.h里定义V4L2_MBUS_BT656的值为2设备树里编译时可以直接引用宏也可以写数字。如果你的代码里bus-type没配成BT.656驱动就会按普通并行接口来处理同步信号解析完全对不上基本不出图。pclk-sample表示在像素时钟的哪个沿采样数据0是下降沿1是上升沿。这个参数看似简单实际调试时经常要来回试。BT.656的PCLK由解码芯片产生MPU和芯片之间可能因为走线长度、电平转换引入延迟导致数据在时钟沿上不稳定。sample沿选反了图像会显著错位严重的直接黑屏。另外还有hsync-active、vsync-active这类离散同步极性参数对于BT.656内嵌同步来说不需要也不建议在设备树里写免得驱动以为你走的是并行离散同步模式。数据端的active-high、active-low也是一样普通数据线没有极性概念除非你用的是10位甚至更高位宽的奇偶分开接口。2.3 时钟和引脚复用DCMIPP收不到PCLK十有八九是这里很多人在DCMIPP上卡住不是协议问题是引脚复用根本没对。PCLK不是由MPU生成的是解码芯片输出的所以设备树里不存在“配置时钟频率”这个动作你需要做的是确保PCLK引脚和D0-D7引脚的复用功能选成了DCMIPP而不是GPIO或者其他外设。在STM32MP257F-EV1上通过STM32CubeMX可以很方便地根据硬件原理图生成pinctrl配置然后拷到设备树里。例如一组典型的并行接口引脚定义如下pinctrl { dcmipp_pins: dcmipp-0 { pins1 { pinmux STM32PINMUX(PI, 0, AF11), /* D0 */ STM32PINMUX(PI, 1, AF11), /* D1 */ STM32PINMUX(PI, 2, AF11), /* D2 */ STM32PINMUX(PI, 3, AF11), /* D3 */ STM32PINMUX(PI, 4, AF11), /* D4 */ STM32PINMUX(PI, 5, AF11), /* D5 */ STM32PINMUX(PI, 6, AF11), /* D6 */ STM32PINMUX(PI, 7, AF11); /* D7 */ slew-rate 2; drive-push-pull; bias-disable; }; pins2 { pinmux STM32PINMUX(PI, 8, AF11); /* PIXCLK */ slew-rate 2; drive-push-pull; bias-disable; }; }; };不同板卡引脚编号不一样这里只是示例。有一点要注意PCLK引脚的slew rate尽量选快一些尤其是信号频率在13.5MHz附近slower slew rate会让边沿变缓导致采样抖动。驱动端一般不需要上拉数据线是推挽输出上拉反而可能引入额外负载。如果条件允许建议在原理图阶段就给PCLK和D0-D7加串阻靠近MPU端放能有效改善信号质量。3. 用V4L2把BT.656视频流转起来3.1 理解media controller的管线DCMIPP在Linux里是一个media controller设备启动后会在/dev下注册media0、video0等节点。整个pipeline从解码芯片的subdev开始经过DCMIPP的subdev最后到达video设备节点。与普通V4L2设备最大的不同是即使硬件上已经连接好了软件上不显式建立链路数据流也不会通。第一步是用media-ctl把当前拓扑打出来看media-ctl -d /dev/media0 -p你会看到几个entity比如“your-decoder 2-0020”是解码芯片的subdev“stm32-dcmipp.0”是DCMIPP的subdev后面还带着main、jpeg、pp等video node。这里的main path就是普通并行接口采集应该走的路径jpeg path和pp path是DCMIPP内部的不同输出管线分别对应JPEG编码和像素后处理通道。BT.656采集直接用main path就行不要选错。3.2 用media-ctl配置链路与格式启动后第一件事是清空链路避免上次配置残留影响判断media-ctl -d /dev/media0 -r然后把解码芯片输出和DCMIPP输入链路起来media-ctl -d /dev/media0 -l your-decoder 2-0020:0 - stm32-dcmipp.0:0 [1]注意实体名要跟media-ctl -p打印出来的一致不同驱动注册名称有差异直接复制命令很可能报找不到entity。链路建立后设置subdev输出格式media-ctl -d /dev/media0 -V your-decoder 2-0020:0[fmt:UYVY8_2X8/720x6251/25]这里你会看到720x625这个尺寸PAL制式下subdev格式包含消隐行所以是625行而不是576行。很多人在这一步会犹豫怀疑自己是不是写错了其实这是正常的。V4L2的video节点层才设置有效分辨率subdev层看到的是完整行数。最后设置video节点的格式v4l2-ctl -d /dev/video0 --set-fmt-videowidth720,height576,pixelformatUYVY这里就是576了因为video节点输出的是有效画面。BT.656是隔行信号两个场合成一帧输出所以你在video节点拿到的720x576其实是交替场的合成帧直接看会有横条纹加个去隔行处理即可。3.3 实际采集并验证数据链路和格式都配好之后用v4l2-ctl抓几帧验证v4l2-ctl -d /dev/video0 --stream-mmap --stream-count30 --stream-tobt656_30f.yuv抓完检查文件大小。720x576x2字节x30帧理论上应该是24883200字节差太大说明有丢帧或者分辨率配置不对用ls -l确认一下。如果有ffplay可以本地预览ffplay -f rawvideo -pixel_format uyvy422 -video_size 720x576 -i bt656_30f.yuv看到画面正常基本链路就是通的。如果不想用命令行也可以用GStreamergst-launch-1.0 v4l2src device/dev/video0 ! video/x-raw,formatUYVY,width720,height576 ! videoconvert ! autovideosink这里要注意GStreamer里的format要写UYVY跟BT.656的Cb Y Cr Y顺序一致写YUYV的话颜色会是错的。实际项目中很多人喜欢用yavta也同样可以做采集yavta -f UYVY -s 720x576 -n 4 --capture30 --fileout.yuv /dev/video0yavta的好处是打印信息详细能直接看到每个buffer的timestamp和帧间隔判断帧率是否稳定很方便。4. 踩坑记录与排查思路4.1 黑屏无数据先确认时序再怀疑驱动BT.656调试最常见的现象就是v4l2-ctl一直打印select timeout一个buffer都拿不到。遇到这个问题我建议按下面这个顺序排查。先用i2cdetect确认解码芯片在总线上接着用i2cget读解码芯片的状态寄存器。不同芯片的状态寄存器地址不一样但通常会有lock状态位和输出格式状态位确认芯片已经锁定输入信号并且输出BT.656。很多解码芯片内部有同步检测寄存器能看到当前有没有识别到CVBS或者YPbPr输入这个信息能直接排除前端问题。然后是示波器这一步别省。拿示波器探PCLK确认频率是不是13.5MHz附近。然后看D0数据线上的电平BT.656的同步头是FF 00 00在示波器上能看到很明显的周期性脉冲串。如果PCLK没有查解码芯片供电和I2C配置如果PCLK有但数据线上没有同步头查解码芯片是否配成BT.601离散同步模式了。软件侧再回头检查设备树里的bus-type是否配置成了BT.656以及pclk-sample方向是否正确。这个顺序非常重要我在实际调试中遇到过好几次一开始软件参数没问题但硬件电平没拉起来导致全黑的情况。先把时序确认了再回来纠结驱动参数效率高很多。4.2 画面花屏、锯齿和颜色错乱如果图像能出来但位置不对比如整幅画面明显左右偏移几个像素或者看起来有斜向撕裂大概率是pclk-sample采样沿反了。BT.656的PCLK沿和数据跳变沿如果没有对齐接收端会在数据跳变过程中采样采到不稳定的值表现出来就是整行错位。把设备树里的pclk-sample从1改成0或者从0改成1重新编译设备树通常马上能解决。颜色异常是另一个高频问题。BT.656默认数据顺序是Cb Y Cr Y也就是UYVY。如果你在media-ctl或v4l2-ctl里配成了YUYV采集来的数据虽然能出图但色度和亮度会错乱画面看起来像滤镜坏了红蓝通道明显不对。遇到颜色怪先确认链路格式和video节点格式都是UYVY不要只看一边。隔行带来的横向条纹也容易被误判成故障。BT.656输入本来就是隔行信号一帧由奇偶两场组成V4L2输出到应用层时通常会保留这个交替结构。直接用播放器看会看到明显的横线这是正常的后续加个去隔行滤波器就行。判断是否为“故障”横向条纹可以抓单帧看是不是只有偶数行或奇数行内容如果是那是场模式配置问题如果两场都有但错开那就是隔行显示的正常现象。4.3 帧率与分辨率对不上subdev格式里设的是720x625video节点设的是720x576很多人会嘀咕分辨率到底对不对。分开理解就行625是PAL的完整总行数576是有效视频行数。如果你的是NTSC输入总行数是525有效行是480那么subdev要设成720x525video节点设成720x480。帧率这里有个容易混淆的点。BT.656是隔行传输PAL是25fps但场频是50Hz。也就是说每秒有50个场两个场合成一帧。V4L2驱动通常按帧上报所以你在v4l2-ctl里看到的是25fps。如果用的解码芯片输出是NTSC帧率就是30fps。如果发现实际采集帧率和预期对不上先确认解码芯片输出的是PAL还是NTSC再看看media-ctl里subdev格式是否跟硬件制式匹配。还有一个容易被忽略的场景是某些解码芯片内部带scaler或者对输入做了resize它输出的行数未必等于625或者525而是某个中间分辨率。这时候不要死磕BT.656标准值直接看示波器上每行有多少个PCLK每帧有多少个行同步头用实际测量值去配V4L2格式。4.4 调试三板斧示波器、debugfs、Trace最后把我常用的三个调试手段完整列一遍。第一是示波器这真的是并行接口调试的最终裁判。我不止一次在软件里翻来覆去找问题最后发现就是PCLK被干扰导致边沿抖动。示波器挂在PCLK和D0上直接看13.5MHz时钟是否稳定看FF 00 00同步头是否周期性出现比看一百遍dmesg都管用。第二是内核动态调试。DCMIPP驱动在probe和stream on阶段会打印不少有用信息默认情况下这些打印被关了。可以动态打开echo file stm32-dcmipp* p /sys/kernel/debug/dynamic_debug/control然后重新跑采集观察dmesg里有没有同步检测失败、超时、DMA错误之类的关键字。ST的驱动在frame start、frame end中断处理里也有调试打印打开后能看到帧中断是否正常触发。如果frame中断压根没触发基本可以肯定是前端同步信号没进来。第三是debugfs。DCMIPP驱动通常会暴露一些内部状态比如中断计数、帧计数、当前配置的时序参数。在/sys/kernel/debug下找dcmipp相关目录把里面的状态文件cat出来看看中断计数在stream on之后有没有递增。这个信息在定位“驱动起来了但不出帧”的问题时特别管用比反复读寄存器省事得多。这几招组合下来BT.656并行接口的绝大多数问题都能在一个小时内定位到具体环节。我在这个平台上印象最深的坑是第一次上电时明明所有配置都对就是不出图折腾了一下午。后来用示波器抓数据线才发现解码芯片输出的同步码完全正常
返回列表