news 2026/10/1 3:20:33

OV7725老传感器新平台移植:V4L2驱动与DTS配置实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OV7725老传感器新平台移植:V4L2驱动与DTS配置实战

简介:OV7725是OmniVision公司生产的高分辨率、低功耗CMOS图像传感器,广泛用于数字摄像头与手机成像模组。这份Linux驱动源码包面向嵌入式Linux开发者与驱动调试工程师,解决向系统接入OV7725摄像头并纳入V4L2视频框架的常见需求,也适合作为学习视频设备驱动的参考样例。压缩包体积仅7KB,包含2个文件:ov7725.c为驱动主体,实现设备探测(probe)、视频流初始化、I2C寄存器读写以及YUV/RGB图像格式配置;ov7725.h提供数据结构定义与函数原型,方便快速移植到不同内核或硬件平台。目前已有358人学习/下载,通过这份精简源码,读者可以系统理解CMOS传感器驱动在V4L2框架下的注册、控制和数据采集流程,掌握曝光、增益、帧率等关键参数调节方法,为后续自研摄像头驱动或图像质量优化提供起点,同时代码量不大,适合逐行分析并结合实际硬件验证,对从零开始编写类似传感器驱动具有直接参考价值。

1. 一个 ov7725.rar 引发的老 sensor 新平台移植问题

做嵌入式 Linux 摄像头项目时,你很容易在某个 BSP 压缩包里翻到ov7725.rar,里面是一颗 30 万像素 CMOS 传感器 OV7725 的驱动源码。这颗传感器本身不新,却因为成本低、功耗小,还在很多工业检测、教育机器人、门禁和医疗设备里服役。问题在于:老 BSP 给的驱动多半是基于某个 3.x 内核写的,拿到的同学往往直接往里拷贝,结果编译不过、probe 失败、/dev/video0出不来,最后连图像流都看不见。

这篇文章就是解决这条落地路径的。我会从 V4L2 驱动框架讲起,说清 OV7725 在框架里到底是 subdev 还是 video_device,然后给出把源码包塞进内核、配设备树、用 V4L2 采图的具体步骤,最后把 DVP 花屏、I2C 不通、老框架迁移这些常见坑写透。适合正在做 Linux 驱动开发、手里有 OV7725 模组、却不知道源码该往哪放的工程师;也适合想快速验证一颗老 sensor 还能不能在新的内核上出图的硬件/软件同事。

2. 在 Linux 驱动栈里找 OV7725 的位置:SCCB 之外还有 V4L2 subdev

别急着编译源码。OV7725 是颗典型的并行 DVP 摄像头,和现在常见的 MIPI-CSI 模组完全不同;它在 V4L2 框架里也不是一个独立/dev/videoX,而是挂在 camera host 下游的 subdev。先把这个位置想清楚,后面的 Kconfig、DTS、media-ctl 才不会配错。

2.1 硬件侧:OV7725 是 DVP 并行 CMOS sensor 不是 MIPI

OV7725 的分辨率是 640x480,输出格式常见为 YUV422、RGB565 和 RAW Bayer,通过一组 8 位并行数据线 D0-D7 送出。它没有 MIPI CSI-2 的差分 lane,硬件接口是 PCLK、VSYNC、HREF 加上 8 根数据线,属于 DVP(Digital Video Port)并行接口。主控 SoC 那边必须有 parallel CSI 控制器,或者把普通 GPIO 复用在 CSI 功能上,否则驱动写得再好也没有数据通路。

Configuration 总线是 SCCB,也就是 OmniVision 自己的串行控制协议,引脚叫 SIO_C 和 SIO_D,电气上和 I2C 高度相似。多数 Linux I2C controller 可以直接拿来操作,默认 7 位地址是 0x21。板上通常还会拉出 reset 和 pwdn(power down)两个 GPIO,用来做上电时序。

如果项目里已经有 ov7725 驱动源码,先打开头文件看寄存器表,你会看到大量像COM3、COM7、COM8这样的小写寄存器定义。COM7 决定输出格式,COM8 控制自动曝光和自动增益,这些寄存器最终会通过 SCCB 写入。

2.2 软件侧:v4l2 驱动框架里,OV7725 是 subdev 而不是 video_device

很多新手以为摄像头驱动就是写个字符设备驱动框架,注册file_operations,让 app 能 read。实际上现代 Linux 摄像头走了 V4L2 media controller 那套:OV7725 驱动注册的是v4l2_subdev,它只负责管理 sensor 本身;/dev/video0这个video_device由 SoC 的 CSI/ISP 驱动创建。用户空间通过open("/dev/video0")拿到帧,而 video0 的 driver 在启动 stream 时,会回调到 subdev 的s_stream,让 OV7725 真正开始输出图像。

这也是标题里 “ov7725 v4l2 驱动” 的真正含义:你需要的不是一套 V4L2 主设备驱动,而是一个能被 async subdev 框架找到的 sensor 驱动。典型的 subdev ops 长这样:

static const struct v4l2_subdev_core_ops ov7725_core_ops = { .s_power = ov7725_s_power, }; static const struct v4l2_subdev_video_ops ov7725_video_ops = { .s_stream = ov7725_s_stream, .g_frame_interval = ov7725_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov7725_pad_ops = { .init_cfg = ov7725_init_cfg, .get_fmt = ov7725_get_fmt, .set_fmt = ov7725_set_fmt, }; static const struct v4l2_subdev_ops ov7725_subdev_ops = { .core = &ov7725_core_ops, .video = &ov7725_video_ops, .pad = &ov7725_pad_ops, };

这套回调里,s_power控制 sensor 上电/掉电,s_stream是拉流开关,set_fmt设置输出到 CSI 的 bus format。老内核里没有pad_ops,而是video_ops里的s_fmt和g_fmt;从 4.x 开始 media controller 重构后,基本都改走 pad ops。如果你手里的 rar 包还在用soc_camera那套soc_camera_link,就是要做框架迁移的重点对象。

2.3 先用 i2cget 读 PID/VID:SCCB 通没通一测便知

软件栈再复杂,第一步永远是确认 SCCB 能读到 sensor 的芯片 ID。OV7725 的 PID 在寄存器 0x0b,版本号在 0x0c,如果读出来 PID 是 0x77,说明 sensor 活着,驱动只是匹配或框架的问题。先用 linux 常用命令验证:

i2cdetect -y -r 1 i2cget -y 1 0x21 0x0b i2cget -y 1 0x21 0x0c

-y表示跳过交互确认,-r让工具强制用 read 方式探测,兼容 SCCB;地址 1 表示是哪条 I2C 总线,具体要看你 DTS 里 sensor 挂在哪。读不到 0x77 时,不要怀疑驱动,先查上电、reset GPIO、XCLK 时钟有没有到,这步是后面所有调试的地基。

3. 把 ov7725.rar 放进内核编译:Kconfig、Makefile 和 DTS 一个都不能少

源码包解压出来以后,最忌讳的就是把整个目录拖到drivers/media/i2c/下面然后直接 make。老平台的内核目录结构、Kconfig 依赖和 DTS 语法都变过,得一步一步来。

3.1 解包后先判断驱动是哪个框架

在 Linux 下解压ov7725.rar,常用的是unrar x,如果没有装可以先装 unrar,再执行:

mkdir -p ov7725 unrar x ov7725.rar -d ov7725 grep -rn "soc_camera\|v4l2_subdev\|v4l2_async" ov7725 | head -50

grep的结果能很快告诉你源码属于哪个时代。如果搜到soc_camera和soc_camera_device,这是老内核的平台驱动写法,不建议直接编进 4.19 以上的内核;如果搜到v4l2_subdev_init、v4l2_async_register_subdev,说明驱动已经接近现代框架,移植工作量小很多。

我一般还会看它有没有of_match_table。有of_match_table的驱动支持设备树匹配,没有的话通常要 i2c_board_info 或 platform_data 创建设备,这种驱动放到 DTS 平台上会很别扭,宁可改成 device tree compatible 匹配。

3.2 Kconfig 和 Makefile:让内核菜单里能选中 OV7725

确认驱动源文件是ov7725.c或ov772x.c后,把它放到当前内核的drivers/media/i2c/目录,再给 Kconfig 和 Makefile 加条目。Kconfig 片段大概是这样的:

config VIDEO_OV7725 tristate "OmniVision OV7725/OV7720 sensor support" depends on I2C && VIDEO_DEV && MEDIA_CONTROLLER help V4L2 sub-device driver for the OmniVision OV7725 CMOS image sensor. Supports 640x480 and lower resolutions via parallel DVP interface.

Makefile 里加一行:

obj-$(CONFIG_VIDEO_OV7725) += ov7725.o

注意,ov7725.o的拼写必须和实际.c文件名一致。如果 rar 包里的文件名是ov772x.c,这里的 object 就是ov772x.o。depends on I2C不能删,因为 sensor 驱动全靠 SCCB/I2C 读写寄存器;VIDEO_DEV是 V4L2 框架基础。老的 soc_camera 驱动还会依赖VIDEO_SOC_CAMERA,这个在 4.19 之后基本被移除,碰到就说明要迁移。

3.3 DTS:让 I2C controller 找到这颗 sensor

现代内核里,subdev 驱动和设备树节点是一一对应的。OV7725 挂在 SoC 的 I2C 总线上,同时在 CSI 端口上要把并行时序写对。常见 DTS 写法如下:

&i2c1 { ov7725: ov7725@21 { compatible = "ovti,ov7725"; reg = <0x21>; clocks = <&clk24m>; clock-names = "xclk"; assigned-clocks = <&clk24m>; assigned-clock-rates = <24000000>; pwdn-gpios = <&pio 4 13 GPIO_ACTIVE_HIGH>; reset-gpios = <&pio 4 15 GPIO_ACTIVE_LOW>; port { ov7725_ep: endpoint { remote-endpoint = <&csi0_ep>; bus-width = <8>; hsync-active = <1>; vsync-active = <1>; pclk-sample = <1>; }; }; }; }; &csi0 { status = "okay"; port { csi0_ep: endpoint { remote-endpoint = <&ov7725_ep>; }; }; };

reg = <0x21>是 sensor 的 7 位 SCCB 地址。clocks给 sensor 提供 master clock OV7725 一般用 24MHz,assigned-clock-rates最好显式写上,否则 bootloader 可能把时钟设成 25MHz 或默认值,导致出图帧率不对。bus-width、hsync-active、vsync-active、pclk-sample是 DVP 时序极性,这四个参数一旦和 SoC CSI controller 不匹配,图像就会花屏。

注意:compatible必须以源码里of_match_table的字符串为准,不同内核版本可能是ovti,ov7725,也可能是ovti,ov772x。配 DTS 前先grep -rn "compatible" drivers/media/i2c/ov7725.c确认。

如果平台没有设备树,老方式是在 board.c 里用i2c_board_info注册:

static struct i2c_board_info __initdata ov7725_info = { .type = "ov7725", .addr = 0x21, }; i2c_register_board_info(1, &ov7725_info, 1);

这种写法在支持 DTS 的新内核里也能用,但会和你板子的其他 I2C 设备管理方式不一致,我建议优先 DTS,只有实在是老 BSP 才用 board_info。

4. 编译、加载和采集:把 OV7725 真正变成 /dev/videoX

Kconfig、Makefile、DTS 都改完后,进入验证闭环。先编译,再加载,用 V4L2 工具确认传感器能被 media controller 识别,最后写个最简采集程序抓一帧 YUYV 图像下来。这一章是把黑匣子变成可见数据的关键。

4.1 编译与加载:模块化比较好调试

第一次移植不要直接编进内核,先编成模块,方便改参数后单独重编。交叉编译环境下执行:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig # Device Drivers -> Multimedia support -> Sensors -> OmniVision OV7725 # 先选 <M> make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules dtbs

编译通过后,把ov7725.ko拷到板子的/lib/modules/$(uname -r)/extra/,执行depmod -a,再modprobe ov7725。如果用insmod,记得按依赖顺序手动加载videodev、v4l2-async等模块,比较麻烦。

加载完看 dmesg 是最直接的:

dmesg | grep -i ov7725

如果驱动 probe 成功,你会看到类似 “ov7725 1-0021: chip found ...” 或 “ov7725: probing ...” 的日志。如果看到-EPROBE_DEFER,多半是 DTS 的时钟、电源或 media link 还没准备好,先不急着骂驱动。

4.2 media-ctl 管线配置:不打通 link 是不会有图像的

现代内核里,即使 subdev 注册成功,video0 也不一定出图。你要先把 media controller 的 pipeline link 起来。先看拓扑:

media-ctl -d /dev/media0 -p

输出里会列出 sensor 实体、CSI 实体和它们之间的 pad。通常 sensor 是ov7725 1-0021,pad 0 输出;CSI 端有一个 sink pad。打通 link 并设置 bus format 的命令如下:

media-ctl -d /dev/media0 -r media-ctl -d /dev/media0 -l "'ov7725 1-0021':0 -> 'csi0':0 [1]" media-ctl -d /dev/media0 -V "'ov7725 1-0021':0[fmt:UYVY8_2X8/640x480]"

这里的fmt:UYVY8_2X8/640x480是 media bus format,前面的UYVY8_2X8表示 8 位并行总线,每像素 16bit,UYVY 顺序。OV7725 如果配置成 YUYV 输出,这里也可能是YUYV8_2X8,以驱动set_fmt支持的数组为准。顺序不对时,图像会出现 R/B 通道互换,这在应用层很容易误判成白平衡问题。

然后可以用 v4l2-ctl 设置主设备视频格式:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480,pixelformat=YUYV v4l2-ctl -d /dev/video0 --list-formats-ext

pixelformat=YUYV是用户在/dev/video0上看到的四字符码,和 media bus format 是两个维度,经常有人把这里写错。--list-formats-ext能看到驱动实际支持的像素格式和分辨率。

4.3 一个最少 mmap 采集程序:一帧 640x480 YUYV 落盘

用命令行工具验证完格式,还得确认真正能取到帧。写个最小 C 程序,用 MMAP 方式抓一帧保存成frame.yuv:

#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <unistd.h> #include <linux/videodev2.h> int main(void) { int fd = open("/dev/video0", O_RDWR); if (fd < 0) { perror("open /dev/video0"); return -1; } struct v4l2_format fmt; memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 480; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { perror("VIDIOC_S_FMT"); return -1; } struct v4l2_requestbuffers req; memset(&req, 0, sizeof(req)); req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) { perror("VIDIOC_REQBUFS"); return -1; } void *map[4] = {0}; for (int i = 0; i < 4; i++) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl(fd, VIDIOC_QUERYBUF, &buf); map[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); ioctl(fd, VIDIOC_QBUF, &buf); } int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd, VIDIOC_STREAMON, &type); struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { perror("VIDIOC_DQBUF"); return -1; } FILE *fp = fopen("frame.yuv", "wb"); fwrite(map[buf.index], 1, buf.bytesused, fp); fclose(fp); ioctl(fd, VIDIOC_QBUF, &buf); ioctl(fd, VIDIOC_STREAMOFF, &type); close(fd); return 0; }

这个程序有几个关键点:VIDIOC_S_FMT必须在VIDIOC_REQBUFS之前,V4L2 驱动大多在申请 buffer 时锁定格式;所有 buffer 都要VIDIOC_QBUF放回队列,再VIDIOC_STREAMON,否则驱动找不到可用的 buffer 会一直等。VIDIOC_DQBUF是阻塞调用,如果 OV7725 的 media link 没打通、CSI 没有时钟,它会一直卡在那里,这不是程序 bug,是驱动链路问题。

抓下来的frame.yuv是裸 YUYV 数据,可以用 7yuv、ffplay 或 Python + numpy 打开。看到画面颜色不对,先回 media-ctl 检查 bus format;看到完全花屏,去查 DTS 里的 hsync/vsync/pclk 极性。

5. 驱动移植与调不到的坑:I2C 不通、花屏和框架变换

OV7725 驱动本身不复杂,但老 sensor 移植到新内核的路上坑不少。下面几条是我实际踩过、也帮别人排查过的典型问题,按“现象 -> 原因 -> 解决”写出来,能省你半天时间。

5.1 i2cdetect 看到 0x21,驱动却报 chip_id 错误

现象:i2cdetect明明在 0x21 上看到了设备,但modprobe后 dmesg 显示chip found failed,或者read pid error。

原因:SCCB 读时序和标准 I2C 有细微差别,很多 I2C controller 在快速读写时会少一个 ACK 周期;另外如果 reset GPIO 极性写反,sensor 根本没从复位状态走出来,只能响应部分寄存器。最容易被忽略的是 XCLK:OV7725 的 SCCB 能响应不代表内部逻辑已起来,没有主时钟时读 ID 会随机失败。

解决:先在应用层反复读 PID 确认稳定性:

for i in $(seq 1 10); do i2cget -y 1 0x21 0x0b; done

如果结果忽有忽无,优先查 24MHz 时钟和复位时序。驱动里常见做法是先拉低 reset,延时 20ms,再拉高,再延时 20ms:

gpiod_set_value_cansleep(ov7725->reset_gpio, 0); usleep_range(20000, 50000); gpiod_set_value_cansleep(ov7725->reset_gpio, 1); usleep_range(20000, 50000);

有些模组把 PWDN 引脚默认拉高,sensor 一直处于 power down,也会出现“probe 时失败、但 i2cdetect 有设备”的假象。查硬件原理图时,把 reset 和 pwdn 两个 GPIO 的状态都量一遍。

5.2 MCLK 没起来或时钟频率不对:probe 返回 -EPROBE_DEFER

现象:驱动日志没有任何 ov7725 的错误,只有probe of ov7725 failed with error -517,设备树里 sensor 节点看起来没问题。

原因:-517就是-EPROBE_DEFER,通常是驱动请求xclk时钟时,时钟 provider 还没就绪,或者assigned-clock-rates没生效。OV7725 对 master clock 宽容度还行,但频率突变会直接影响帧率。

解决:在 DTS 里显式固定频率:

assigned-clocks = <&clk24m>; assigned-clock-rates = <24000000>;

上板后查看时钟是否真的跑起来:

cat /sys/kernel/debug/clk/clk_summary | grep -E "clk24m|xclk"

没有 debugfs 时,就用示波器量 sensor XCLK 引脚,看到波形再继续。这个坑很像玄学,但九成是 bootloader 把时钟树重配了。

5.3 图像花屏、绿屏、左右错位:DVP 极性参数是第一嫌疑

现象:v4l2-ctl 能列出格式,采集程序也能 DQBUF 出帧,但打开 YUYV 文件是绿紫相间的斜条纹,或者图像像被水平切割错位。

原因:CSI controller 和 OV7725 在 PCLK 采样沿、HSYNC/VSYNC 有效电平上不一致。DVP 并行接口没有自动协商机制,双方必须约定同一套极性。pclk-sample = <1>表示在 PCLK 上升沿采样,= <0>表示下降沿;hsync-active和vsync-active同理。

解决:改 DTS endpoint 后重新编译 dtbs,重启板子再试。常见组合参考:

参数值说明
bus-width8OV7725 输出 8 位并行数据
pclk-sample1 / 0数据在 PCLK 上升沿/下降沿有效
hsync-active1 / 0HSYNC 高电平/低电平有效
vsync-active1 / 0VSYNC 高电平/低电平有效

如果极性看起来都对了还是花屏,再把 sensor 输出格式从 YUV422 临时切成 RGB565 验证一下。颜色空间转换错误和极性错误在画面上表现不一样,前者是固定色偏,后者是水平撕裂或雪花状条纹。

5.4 老源码在新内核编译不过:soc_camera 到 v4l2_subdev 的迁移

现象:编译时报soc_camera.h: No such file or directory,或者unknown type name 'v4l2_subdev_ops';有些还会报implicit declaration of function 'v4l2_async_register_subdev'。

原因:内核删掉了旧的 soc_camera 平台框架,subdev API 也改过参数。比如早期驱动注册 subdev 时直接v4l2_i2c_subdev_init(&sd, client, ops),新内核要求v4l2_subdev_init之后填充sd->dev,最后必须调v4l2_async_register_subdev。

解决:做最小迁移,把 probe 尾部改成:

v4l2_i2c_subdev_init(&ov7725->sd, client, &ov7725_subdev_ops); v4l2_async_register_subdev(&ov7725->sd);

remove 函数里加上v4l2_async_unregister_subdev(&ov7725->sd)。同时把struct soc_camera_link对应的 platform_data 改到 DTS/device property 里。老的s_fmt/g_fmt如果报错,搬到 pad ops 的set_fmt/get_fmt下。这一步没有统一模板,必须逐个看编译错误,但方向是明确的:让驱动变成一个“能在 async subdev 框架里被 probe 的 I2C client”。

5.5 VIDIOC_DQBUF 一直阻塞:link 没打通或 buffer 没排满

现象:上面给的采集程序运行后卡在VIDIOC_DQBUF,按 Ctrl+C 才退出。media-ctl -p看实体都在,但不出帧。

原因:三个原因按概率排序:media link 没有 enable、sensor 的s_stream没有被调用、buffer 队列里有 buffer 没 QBUF。其中 link 没 enable 最常见,因为很多 SoC 上 CSI 和 sensor 是两个独立 driver,video0 是 video_device 但不清楚下游 link。

解决:确认 link 状态:

media-ctl -d /dev/media0 -p

看ov7725 1-0021的 pad 到 csi0 的 link 是否带[1]。然后查看有没有 sensor 启动日志:

dmesg | tail -50

如果驱动里有s_stream的 debug 打印,能看到stream on。另外检查采集程序是否把所有 buffer 都 QBUF 了,少了任何一个,DMA 环都可能停住。

6. 让 OV7725 长期稳定出图:用 100 帧压力测试和帧时间戳说话

驱动能抓到一帧只是开始,真正要投入生产,得做稳定性验证。我最常用的验证方式是连续抓 100 帧裸数据,检查文件大小和帧时间戳是否正常。

v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=100 --stream-to=stress.yuv ls -al stress.yuv

如果格式是 640x480 YUYV,每帧 614400 字节,100 帧文件大小正好 61440000 字节。少任何一截都说明有丢帧,丢帧基本来自 CSI 带宽、DMA 优先级或电源抖动。不要只看文件大小,还要看v4l2_buffer.sequence和timestamp。在采集程序里打印这两项:

printf("seq=%u ts=%lld.%09ld\n", buf.sequence, (long long)buf.timestamp.tv_sec, buf.timestamp.tv_nsec);

连续帧的 timestamp 间隔应该和你设置的帧率一致,比如 30fps 就是约 33.33ms。如果序列跳号,说明有帧没被 DQBUF 到应用层;如果时间戳抖动超过 10%,多半是 sensor 的 24MHz 时钟不稳,或者上游摄像头用 GPIO 做了非精确的帧同步。

我自己的习惯是:无论从哪里拿到 ov7725.rar,先花十分钟做三件事——确认它是不是 v4l2_subdev 驱动、确认 DTS 时钟和极性、确认 i2cget 能读到 0x77。这三件事不翻车,后面编译、采集、压力测试都能按部就班;翻车了就直接往电源、时钟、GPIO 复位这三处找问题。OV7725 老,但不难伺候,只要把 DVP 时序和 V4L2 框架位置摆正,它能在新内核上再稳定跑很多年。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 3:19:48

SpringBoot+Vue+SpringCloud微服务分布式求职招聘系统实战全解析

先交代一下背景。这个项目完整做下来&#xff0c;前前后后花了将近两个月时间&#xff0c;从最开始的一堆需求点子&#xff0c;到最终跑通“求职者投简历、企业筛简历、平台做推荐”的完整闭环。名字就叫“个人简历求职招聘系统”&#xff0c;技术栈是SpringBoot Vue SpringC…

作者头像 李华
网站建设 2026/10/1 3:18:42

Python+Pillow实现横向年度日历:从日期数据处理到像素级排版

你有没有想过&#xff0c;把一整年的日子摊开成一张横图&#xff0c;会是什么效果&#xff1f;我最近用Python写了个小工具&#xff0c;输入年份&#xff0c;自动生成一张横向年度日历图。所谓横向&#xff0c;不是传统挂历那样一月一页&#xff0c;而是把12个月全部铺在同一张…

作者头像 李华
网站建设 2026/10/1 3:17:42

从模糊到量化:构建可落地的优化方法论与性能调优实践

1. 从“更好的优化”这个标题说起“更好的优化”这四个字&#xff0c;看起来像是一句正确的废话&#xff0c;但恰恰是这种模糊的表述&#xff0c;暴露了一个非常普遍的问题&#xff1a;绝大多数人在说“优化”的时候&#xff0c;根本不知道自己在优化什么。我见过太多项目复盘会…

作者头像 李华
网站建设 2026/10/1 3:17:34

多Agent协作架构实战:从单轮调用到层级编排的演进与落地

1. 从单轮到多 Agent&#xff1a;为什么架构必须演进1.1 单轮调用的本质与天花板很多人第一次接触 Agent&#xff0c;都是从“给大模型一个工具&#xff0c;让它自己决定调不调”开始的。这其实就是最原始的单轮调用形态&#xff1a;用户输入一句话&#xff0c;模型判断是否需要…

作者头像 李华
网站建设 2026/10/1 3:17:07

数组元素按出现次数筛选并升序输出:四种语言实现与工程实践

这道题看起来简单&#xff0c;但我在实际处理业务数据时经常碰到它的变体&#xff1a;比如从订单记录里找出恰好被下单3次的商品编号、从访问日志里筛选出访问了指定次数的用户IP&#xff0c;又或者从传感器数据中挑出异常频次的设备ID。核心无外乎四个动作——数组遍历计数、按…

作者头像 李华
网站建设 2026/10/1 3:17:07

亚马逊Climate Pledge Friendly绿标认证实操:流量红利与申请全攻略

1. 为什么一夜之间&#xff0c;跨境卖家都在追这抹绿亚马逊前台那个墨绿色的小叶子标识&#xff0c;这两年出现的频率越来越高。做跨境电商的朋友应该都有印象&#xff0c;搜索页面里部分产品标题下方会多一行“Climate Pledge Friendly”的小字&#xff0c;配一片叶子图标。点…

作者头像 李华