RV1126 RK_MPI视频采集显示链路:从sensor到屏幕完整实战 1. 项目全貌从sensor到屏幕的一条完整链路1.1 这是一条什么链路RV1126 这颗芯片在国内的边缘智能硬件里出现频率一直很高尤其是带 IPC网络摄像机、行车记录仪、工业相机这一类需要同时处理图像采集和显示的方案。标题里那句“读取 sensor 输入输出到屏幕显示”听起来简单实际上就是把一条完整的视频采集显示链路跑通sensor 采集到 RAW RGB 或 YUV 原始图经过 MIPI CSI 接口进入主控再由主控内部图像处理单元做缩放、裁剪、旋转、帧率控制最后从 MIPI DSI 或 RGB 接口送出去点亮屏幕。这条链路里最核心的软件层就是 Rockchip 提供的 RK_MPI API 接口。刚接触 RV1126 的人经常被 MPP、RKMPI、V4L2 这些名词搞晕其实 RK_MPI 是 Rockchip 在 MPPMedia Process Platform基础上封装的一套面向视频输入输出、编码解码、AI 前处理的 C 语言接口。它比直接操作 kernel 层的 V4L2 更贴近应用等于把视频采集、多路分发、显示输出的底层细节隐藏起来让你像搭积木一样把 VI、VPSS、VO、VENC 这些模块串起来。这个项目特别适合三类人一是刚拿到 RV1126 开发板、想快速点亮摄像头和屏幕的硬件工程师二是做嵌入式Linux 应用开发需要把 sensor 图像实时显示到 LCD 上做预览的软件工程师三是想用 RK_MPI 做产品原型验证的创客。无论从哪类角色切入只要跑通了这条链路后续做抓拍、录像、RTSP 推流、AI 识别都有了地基。1.2 为什么选择 RK_MPI 而不是传统 V4L2很多人会问Linux 下做摄像头采集不是直接用 V4L2 吗为什么要绕一圈用 RK_MPI? 这个问题我刚开始也纠结过。V4L2 是 kernel 提供的视频采集标准接口单独用 V4L2 确实可以接到 sensor 的数据但它只解决“采进来”的问题。你还需要处理 buffer 管理、格式转换、缩放裁剪甚至要把数据送到显示设备、编码器、AI 加速器这种跨模块协同的事情在纯 V4L2 下就要自己写一堆 ioctl 而且不同平台兼容性还差。RK_MPI 的设计思路更像一个视频中间件。它把 VIVideo Input、VPSSVideo Process Sub System、VOVideo Output、VENCVideo Encoder、VDECVideo Decoder、RGNRegion这些硬件/软件模块统一建模用标准的数据流通道连接。你用 RK_MPI 做 sensor 预览本质上就是创建 VI 通道经过 VPSS 处理再绑定到 VO 输出整个过程 API 稳定、时序控制可靠而且官方提供大量 sample 代码可以直接抄。选择 RK_MPI 的另一个实际原因是 RV1126 的内核驱动对 sensor 的支持大多数走的是 Rockchip 的 camera 框架V4L2 子设备抽象出来的节点在链路绑定上并不直观。一旦你需要同时出多路不同分辨率的码流或者要叠加 OSD 菜单RK_MPI 的通道模型能省掉很多事。所以我个人的建议是嵌入式 Linux 上做 Rockchip 平台的视频应用别纠结直接学 RK_MPI 为主V4L2 作为了解底层辅助。1.3 适用场景与基础要求这个项目的应用场景非常广。最简单的就是把开发板当作一个“摄像头预览板”sensor 拍到什么屏幕就显示什么。进一步可以做电子显微镜、内窥镜、教学实验设备、工业视觉检测的取景器。还有一些做拇指相机、穿戴相机的小伙伴sensor 镜头选型时也会参考 RV1126 这类小型化 SoC因为它的封装和功耗适合便携设备。做这个项目需要的基础要求不算高但也不是纯小白就能直接上的。你需要一块 RV1126 核心板或开发板最好带 MIPI CSI 接口和 MIPI DSI/RGB 显示接口一个 MIPI 接口的 sensor 模组比如 IMX335、GC2053、SC3336 等常见型号一块适配的 LCD 屏幕分辨率不要超过 1080P最好确认驱动时序和板子的 VO 接口匹配Linux 开发环境交叉编译链、SDK 里的 mpp sample 源码。如果你手上没有现成屏幕用 HDMI 输出替代也可以RV1126 通过转换芯片可以驱动 HDMI但项目复杂度会加一颗芯片调试起来多一层变量。第一次做尽量用 MIPI DSI 屏幕或者 RGB 并口屏幕这样链路短、问题少。2. 硬件准备与sensor镜头选型要点2.1 RV1126主控与显示接口RV1126 是一颗四核 Cortex-A7 RISC-V 协处理器的视觉 SoC最大特点是内置 2TOPS 算力的 NPU可以运行轻量级神经网络模型。视频接口方面它通常支持多路 MIPI CSI 输入和一路 MIPI DSI 显示输出部分板级会引出 RGB 并口或 BT.1120。实际选板子的时候你要确认三件事sensor 接口是不是 MIPI CSI 2-lane/4-lane、屏幕接口是 MIPI DSI 还是 RGB、系统内存多大。内存至少要 256MB 才够跑图像缓冲512MB 更稳。显示接口这块最容易踩坑。RV1126 的显示控制器在硬件上支持 MIPI DSI但不同开发板引出方案不一样。有些板子默认输出到 RGB 并口需要你配置屏幕的时序、像素时钟、DE/Hsync/Vsync 极性有些板子直接走 MIPI DSI需要你在 dts 里配置 lane 数、时钟频率。我建议先看 SDK 里的 defconfig 和 dts确认哪些显示接口已经打开再决定怎么接屏。2.2 sensor 模组选型别让镜头拖后腿sensor 选型看起来很简单买一个 MIPI 接口的摄像头模组就能用但实际上 sensor 和镜头是两个独立的部分。最近“拇指相机”“sensor 镜头选型”这些词很热说明很多人开始关注小体积模组方案。我在实际项目里发现同样一颗 sensor 芯片配不同的镜头画质差异非常大。如果你做的是室内短距离预览比如 1 米以内拍清文档就选 3.6mm 焦距左右的镜头如果做户外广角监控选 2.8mm 甚至更小焦距如果要拍远处细节需要长焦镜头但长焦视角窄、景深小对调焦机构要求高。镜头接口常见 M12、M16 和 micro 镜头座拇指相机因为体积小很多用微型镜头座要确认镜头的像面尺寸能覆盖 sensor 的靶面不然成像边缘会有暗角。sensor 本身的选择则要靠查 datasheet。常见有 1080P 的 IMX335、4K 的 IMX415、性价比高的 GC2053、SC3336。我的经验是优先选择 SDK 里自带驱动适配过的 sensor比如 Rockchip 官方 SDK 的 kernel/drivers/media/i2c 目录下有哪些 sensor 驱动直接选用这些型号能省掉大量移植工作。如果是规格书找来的新 sensor就要自己改 dts、写驱动、调接口工作量翻倍。2.3 接线与供电的常见坑硬件连接这一步很多人觉得无非就是排线插上。但 sensor 上电时序是很敏感的东西尤其是 MIPI 接口。你需要关注 sensor 的 AVDD、DOVDD、DVDD 这几路电源的上电顺序有些 sensor 要求 DOVDD 先上电再上 AVDD如果反过来可能出现 I2C 读不到 ID 或者图像异常。建议在 dts 里配置 regulator 的 supply 顺序或者用板上的固定时序芯片。另外 MIPI 信号线的差分对要注意阻抗匹配开发板一般已经处理好了但你自己做转接板时不要随便飞线。时钟线 MCLK 的频率也关键常见 sensor 需要 24MHz 或 27MHzdts 里要配置对否则 sensor 没有输出。复位脚和 PWDN 脚的电平极性也要查手册有些 sensor 复位是高有效有些是低有效配反了就一直处于复位状态点不亮。3. RK_MPI软件框架与关键对象解构3.1 从MPP到RK_MPIAPI设计思路Rockchip 的媒体处理软件栈分两层底层叫 MPP主要是针对视频编解码的库上层叫 RK_MPI是一套更全面的多媒体编程接口。RK_MPI 借鉴了华为海思的 MPI 接口风格用模块句柄通道号的方式管理硬件资源。初次看 API 文档会觉得函数名很长比如 RK_MPI_VI_EnableChn但其实规律非常强模块前缀 操作 对象。API 的设计思路可以类比成一个“视频管道工”。你负责把 VI入水口、VPSS净水器、VO出水口用管道连接起来每个模块有自己的属性配置每个属性配置背后都对应一组硬件寄存器或者内核驱动行为。RK_MPI 把这种连接抽象成 Bind 操作让数据流自动从上游流向下游不需要你这个“管道工”手动搬水。3.2 VI/VPSS/VO三件套谁负责什么VI 模块是视频输入的入口负责接收 MIPI CSI 或者并口 sensor 数据完成数据格式转换、帧同步、丢帧处理。它把 sensor 的 RAW 数据或者 YUV 数据统一成视频帧输出到内存 buffer。VI 的通道属性里要设置数据格式、分辨率和 buffer 个数。VPSS 模块是图像处理中心既可以做缩放scale也可以做裁剪、旋转、镜像、亮度对比度调整。为什么要过 VPSS因为 sensor 输入的分辨率和屏幕显示的分辨率往往不一致比如 sensor 输出 4000x3000屏幕只有 800x480你不缩放就只能显示局部。VPSS 可以分出多个通道同一路输入同时输出 1080P、720P、小尺寸预览图后续做编码和显示非常方便。VO 模块是显示输出控制器负责把视频帧送给屏幕。VO 里面分设备Device和通道Chn设备对应实际的显示接口类型比如 MIPI DSI、RGB、BT1120通道对应叠加到屏幕上的视频层。VO 的时序参数要和屏幕的 datasheet 严格匹配否则会出现黑屏、闪屏、图像偏移。3.3 通道与绑定关系先画一张关系图在写代码前我强烈建议先在纸上画一下 VI、VPSS、VO 的关系。比如我的项目里是单路 sensor 输入要显示到屏幕最简单的拓扑是VI Chn0 → VPSS Grp0 Chn0 → VO Chn0。这里要注意VI 的通道绑定对象是 VPSS Group不是 VPSS Channel。VPSS Group 是一个处理组一个组有多条通道通道之间共享同一路输入数据可以各自做不同处理。VO 的输入是 VPSS 的某个通道输出绑定后就不用手动搬帧了。RK_MPI 里用 RK_MPI_SYS_Bind 把上游节点和下游节点绑定起来使用完成后用 RK_MPI_SYS_Unbind 解绑。这条关系图里还有一个潜在节点VENC。如果后续要录像就在 VPSS Chn0 上再绑定 VENC 通道但本项目只需要显示所以暂时不涉及。4. 最小可运行程序读取sensor并上屏4.1 初始化与系统绑定写 RK_MPI 程序第一步永远是初始化系统。调用RK_MPI_SYS_Init()这个函数会把 MPP 系统里的各个模块初始化好并建议在程序退出时调用RK_MPI_SYS_Exit()。初始化不算复杂度但一定要检查返回值很多时候 sensor 点不亮就是因为前面还有某个模块初始化失败直接忽略了。RK_S32 ret RK_MPI_SYS_Init(); if (ret ! RK_SUCCESS) { printf(RK_MPI_SYS_Init failed: 0x%X\n, ret); return -1; }初始化之后可以先读取一下系统版本号确认 SDK 编译的 MPP 版本避免 API 不兼容。版本信息用RK_MPI_SYS_GetVersion获取打印出来做个日志。调试嵌入式程序日志越详细越好。4.2 配置VI模块sensor输入参数对齐VI 通道的配置重点是VI_CHN_ATTR_S结构体。你需要把 sensor 的输出格式、分辨率、像素格式、帧率填进去。常见的 MIPI sensor 输出是 RAW10 格式但 VI 模块内部可以配置成 YUV420 或者 YUV422 输出给上层这里我建议在已适配的 SDK 里直接查 sample 用的参数。VI_CHN_ATTR_S viChnAttr; memset(viChnAttr, 0, sizeof(viChnAttr)); viChnAttr.pcVideoNode rkisp_mainpath; // 或者 mipi0具体看平台 viChnAttr.enPixelFormat PIXEL_FORMAT_YUV_SEMIPLANAR_420; viChnAttr.u32Width 1920; viChnAttr.u32Height 1080; viChnAttr.enBufType VI_CHN_BUF_TYPE_MMAP; viChnAttr.u32BufCnt 4; viChnAttr.enWorkMode VI_WORK_MODE_NORMAL;pcVideoNode这个字段要特别注意它对应内核里 sensor pipeline 的节点名不同 SDK 版本不一样。RV1126 常见的节点是rkisp_mainpath、rkisp_selfpath如果填错VI 打开视频节点会失败。你可以先去/dev下看看有哪些 video 节点或者在 dts 里查 isp 的 pipeline。4.3 配置VPSS让图像走一条合理的流水线VPSS 配置比较复杂因为它有 Group 和 Chn 两层。先创建 Group设置 Group 的输入分辨率和像素格式这个输入通常和 VI 输出一致。然后设置 Group 的每个 Chn 输出分辨率、裁剪窗口、像素格式和帧率控制参数。VPSS_GRP vpssGrp 0; VPSS_GRP_ATTR_S grpAttr; memset(grpAttr, 0, sizeof(grpAttr)); grpAttr.enPixelFormat PIXEL_FORMAT_YUV_SEMIPLANAR_420; grpAttr.u32MaxW 1920; grpAttr.u32MaxH 1080; grpAttr.enCompressMode COMPRESS_MODE_NONE; RK_MPI_VPSS_CreateGrp(vpssGrp, grpAttr); RK_MPI_VPSS_StartGrp(vpssGrp);VPSS Group 相当于一个处理池创建的 Chn 才决定输出内容。比如我需要在屏幕上显示全屏 1920x1080同时之后可能想抓一张小图做缩略图就可以创建两个通道VPSS_CHN_ATTR_S chnAttr; memset(chnAttr, 0, sizeof(chnAttr)); chnAttr.enChnMode VPSS_CHN_MODE_USER; chnAttr.enPixelFormat PIXEL_FORMAT_YUV_SEMIPLANAR_420; chnAttr.u32FrameRate 0; // 0 表示不限制帧率 chnAttr.bMirror RK_FALSE; chnAttr.bFlip RK_FALSE; chnAttr.stSize.u32Width 1920; chnAttr.stSize.u32Height 1080; RK_MPI_VPSS_SetChnAttr(vpssGrp, 0, chnAttr); RK_MPI_VPSS_EnableChn(vpssGrp, 0);这里u32FrameRate设置为 0 是代表跟随上游帧率不额外做丢帧。如果你的屏幕刷新率是 60Hz而 sensor 只出 30fps那 VO 会重复显示最新帧不会有问题。但如果 sensor 出 60fps屏幕只支持 40Hz就需要在 VPSS 通道里做帧率控制否则可能显示撕裂。4.4 配置VO模块告诉屏幕要显示什么VO 配置第一件事是设置设备属性。VO_PUB_ATTR_S里要指定显示接口类型、分辨率、刷新率和像素格式。比如 MIPI DSI 屏可能用VO_INTF_MIPI_DSIRGB 屏用VO_INTF_RGB888。具体接口类型要看 SDK 头文件里的枚举定义。VO_PUB_ATTR_S voPubAttr; memset(voPubAttr, 0, sizeof(voPubAttr)); voPubAttr.enIntfType VO_INTF_MIPI_DSI; voPubAttr.enIntfSync VO_OUTPUT_1920x1080_60; voPubAttr.u32BgColor 0x000000; voPubAttr.enPixFormat PIXEL_FORMAT_RGB_888;需要注意的是VO 的像素格式可能和 VPSS 输出的 YUV 不一样需要内部转换。RK_MPI 一般会自动处理但你要确认屏幕是 RGB 接口还是 CPU 接口RGB 888 是常见格式。同步模式VO_OUTPUT_1920x1080_60会决定 dts 里的时序参数如果屏幕实际是 50Hz 刷新率这里也要对应改。创建/获取 VO 设备后使能 VO 通道RK_MPI_VO_SetPubAttr(0, voPubAttr); RK_MPI_VO_Enable(0); RK_MPI_VO_EnableChn(0, 0);如果显示层有多个视频层需要设置通道属性比如VO_CHN_ATTR_S里的位置和大小。默认 0 通道可以全屏显示位置为 (0,0)。如果你的 VPSS 输出了 640x480 但屏幕是 1080P可以设置 VO Chn 属性把显示区域放在屏幕中央或者直接用 VPSS 缩放成全屏。4.5 启动流与帧数据流转模块配置好以后按顺序启动使能 VI 通道RK_MPI_VI_EnableChn(viChn, 0)启动 VPSSRK_MPI_VPSS_StartGrp(vpssGrp)使能 VPSS 通道RK_MPI_VPSS_EnableChn(vpssGrp, 0)使能 VO 通道RK_MPI_VO_EnableChn(0, 0)绑定 VI 到 VPSSRK_MPI_SYS_Bind(viNode, vpssNode)绑定 VPSS 到 VORK_MPI_SYS_Bind(vpssNode, voNode)。顺序不对容易出各种问题我自己习惯先绑定再使能通道因为绑定行为会触发内部 pipeline 重新协商格式。两个节点的命名通过RK_MPI_SYS_GetBindNode或者直接手动构造MPP_CHN_S结构体。MPP_CHN_S stViChn {0}; stViChn.enModId RK_ID_VI; stViChn.s32DevId viDev; stViChn.s32ChnId 0;enModId是模块 IDs32DevId和s32ChnId是对应设备号和通道号。绑定操作本身代码很短但一旦上游或下游配置不一致这里会报错所以前面的属性设置一定要反复检查。4.6 加入显示区域的坐标与裁剪很多场景下sensor 的宽高比和屏幕不一致。比如 sensor 输出 4:3屏幕是 16:9。你可以在 VPSS 通道里设置裁剪区域stCropRect只取中间的 16:9 区域再缩放避免画面拉伸变形。VPSS 的裁剪是在VPSS_CHN_ATTR_S里开启bCropEnable然后配置stCropRect。chnAttr.bCropEnable RK_TRUE; chnAttr.stCropRect.u32Width 1920; chnAttr.stCropRect.u32Height 1080; chnAttr.stCropRect.u32X 0; chnAttr.stCropRect.u32Y 120; // 从 4:3 源图中裁剪中间区域裁剪的原点是输入图像左上角单位是像素。这个功能在调屏幕显示时特别有用不用改 sensor 分辨率就能得到想要的画面。但裁剪区域大小要小于等于输入宽度高度X/Y 加上宽高不能越界否则会返回错误参数。5. 我在调通路上踩过的坑5.1 sensor点不亮供电、时钟、复位顺序这是最常见的问题。开机后 I2C 读不到 sensor ID首先查三样东西电源是否真正供上、MCLK 时钟是否输出、复位脚是否释放。我遇到过 dts 里 MCLK 被其他外设占用sensor 始终没时钟I2C 一直失败。排查时可以用示波器量 MCLK或者看内核日志里 sensor 驱动 probe 是否成功。还有一种情况是 sensor 供电电压不对比如 GC2053 需要 1.2V DVDD如果你的 PMIC 配置输出 1.8Vsensor 可能上电后电流异常甚至发热。用万用表量一下 AVDD、DOVDD、DVDD 的电压值是最直接的办法。5.2 图像花屏或颜色偏绿MIPI lane配置与色彩空间花屏大概率是 MIPI lane 数量和速率配置不对。sensor 输出 4 lane 但 dts 里只配了 2 lane或者反之都会导致数据错乱。另一个容易忽略的是 MIPI 接收端和发送端 clock lane 的非连续时钟设置有些 sensor 支持 non-continuous clockplate 上也要开对应模式。颜色偏绿常见于 sensor 输出 RAW 数据但没有做 ISP 处理或者 VI 的像素格式配置成了 YUV 但实际是 RAW。RV1126 的 ISP 模块负责 RAW 转 RGB/YUV如果你直接用 VI 的rkisp_mainpath一般会自动过 ISP。如果你用了rkisp_bypass或selfpath就要确认 bypass 后是否还能正确转换。5.3 分辨率/帧率不对sensor输出与VI对齐出现画面只有一半或者上下颠倒先看 sensor 输出分辨率配的是不是 1920x1080。很多 sensor 默认输出分辨率在驱动里写死你需要在 dts 的 sensor 节点里配>