ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

嵌入式Linux下OV9650摄像头驱动开发与黑屏排查实战

嵌入式Linux下OV9650摄像头驱动开发与黑屏排查实战 简介面向嵌入式Linux开发者的OV9650摄像头驱动与测试工程适用于S3C2410、S3C2440、S3C6410及A8等ARM平台可满足嵌入式Linux驱动开发与ARM平台外设接入需求。驱动基于V4L2架构实现已在tiny210开发板上调通内核版本不同或硬件连接相近时只需少量改动即可复用由于不直接操作摄像头控制寄存器也降低了移植门槛。压缩包共288个文件、大小约2.29MB核心包含C驱动源码、头文件和Makefile/Kconfig等工程文件另含HTML、CSS、JavaScript、图片、PDF、DOCX等多种格式覆盖界面资源与说明文档便于对照学习V4L2驱动的组织方式。测试程序基于OpenCV与Qt4.7开发可实际采集摄像头画面并验证驱动工作状态帮助读者梳理从V4L2驱动注册到上层应用调用的完整流程。作者根据真实项目整理对需要快速在ARM平台接入OV9650的开发者有直接参考价值当前已有484人学习浏览适合嵌入式驱动开发人员及项目参考。1. 为什么还在自己写 OV9650 驱动1.1 这套平台组合的尴尬内核自带的驱动指望不上OV9650 这颗传感器放在今天参数一点都不亮眼但它便宜、模组多、资料全配上 2410、2440 这类 ARM9 老平台或者 Cortex-A8 系列依然是很多人学习摄像头驱动入门的首选组合。我当初做这套驱动的时候板子跑的内核是 2.6.24 到 2.6.39那年代的内核里 OV9650 的 V4L2 subdev 驱动远没有现在完善三星 CAMIF 对应的 platform driver 又和 board 文件里的配置绑得很死换个板子就要大改。与其在一堆补丁里找答案不如自己从零写一遍把“传感器怎么配”和“图像怎么搬到内存”这两件事彻底搞清楚。另一个原因是学习价值。摄像头驱动是嵌入式 Linux 里最能串起 I2C、中断、DMA、内存映射、V4L2 框架的一条线。你跟着厂商的 demo 改一改和从头自己写一遍理解深度完全不一样。我做完这套之后后面再碰别的传感器基本就是换一张寄存器表的事。1.2 摄像头数据链路先认清信号流再动手OV9650 输出的是一组并行信号8 位像素数据 D[7:0]加上 PCLK像素时钟、HREF行有效、VSYNC帧同步。这组信号直接接到处理器的 camera interface。在 2410/2440 上叫 CAMIF在 S5PV210 上叫 FIMC在 AM335x 上叫 VPFE名字不同干的活一样按 PCLK 采样数据按 HREF/VSYNC 划出图像边界把数据通过 DMA 搬进 SDRAM再由 LCD 控制器或者应用程序显示出来。现在把整个驱动拆成两半来看一半是 I2C 控制 OV9650 的寄存器决定它输出什么格式、什么分辨率另一半是控制 CAMIF 和 DMA决定数据往哪搬、怎么通知应用层。这个划分一旦在脑子里立住后面写代码、调问题都会顺很多。黑屏这种问题之所以难搞恰恰是因为大家总把这两半混在一起想最后分不清是传感器没出图还是数据没搬对。2. 驱动架构拆解先把传感器控制和采集通道分开2.1 I2C 读写通道摄像头寄存器的唯一入口OV9650 支持 SCCB 协议和 I2C 高度兼容在 Linux 里直接注册一个 i2c_client 就能读写。7 位地址一般是 0x30具体看模组上 ADID 引脚的电平有些板子上是 0x0A这个坑我先放这里后面排查黑屏时会再次遇到。先封装最底层的读写函数后面所有寄存器操作都走这两个函数。写的时候注意 SCCB 的格式是“寄存器地址 数据”两个字节一起发不能像有些 I2C 器件那样分两次传输。static int ov9650_write(struct i2c_client *client, u8 reg, u8 val) { u8 buf[2] {reg, val}; struct i2c_msg msg { .addr client-addr, .flags 0, .len 2, .buf buf, }; return i2c_transfer(client-adapter, msg, 1) 1 ? 0 : -EIO; }写完寄存器我习惯回读一遍确认尤其当怀疑 I2C 时序有问题的时候。回读结果也是判断传感器是否真正在线的最直接依据。你可以在驱动里加一个 debugfs 入口手动读写任意寄存器调试阶段会非常省事。2.2 采集链路CAMIF、DMA 和 V4L2 的配合采集部分的思路是初始化 CAMIF 的源窗口和目标格式配好 DMA 目的地址然后启动传输。三星 CAMIF 的寄存器不算复杂核心是 CISRCFMT、CITRGFMT 和 CIGCTRL。CIGCTRL 里有一组极性控制位 INV_PCLK、INV_HREF、INV_VSYNC这段代码在后面排黑屏时会反复改。void camif_set_polarity(void __iomem *base, int inv_pclk, int inv_href, int inv_vsync) { u32 v readl(base 0x10); /* CIGCTRL偏移以具体手册为准 */ if (inv_pclk) v | (1 3); if (inv_href) v | (1 2); if (inv_vsync) v | (1 1); writel(v, base 0x10); }DMA 缓冲一定要用 dma_alloc_coherent 分配这样能保证物理地址连续同时避免 cache 一致性问题。每个缓冲还要建立 V4L2 的 mmap 映射关系应用层才能直接拿到数据。老内核上建议先用 videobuf 这套机制因为 2.6.x 年代 vb2 还不成熟videobuf 已经把缓冲队列和 mmap 的边界情况处理好了比自己裸写 file_operations 里的 mmap 要稳。2.3 对应用层暴露的 V4L2 接口驱动对外我采用 V4L2 接口这样测试程序可以用标准的 open/ioctl/mmap 流程也方便以后换平台。核心 ioctl 就那几个VIDIOC_S_FMT 设置采集格式VIDIOC_REQBUFS 申请缓冲VIDIOC_QBUF/DQBUF 管理缓冲队列VIDIOC_STREAMON/OFF 控制启停。实现 file_operations 时最容易被忽略的是 unlock_ioctl 和 compat_ioctl 都要注册否则 32 位应用在 32 位内核上没问题但交叉编译环境里经常会踩到。另外一个细节是 DQBUF 在没有数据时应该阻塞等待用等待队列配合中断处理函数唤醒别用忙轮询不然 CPU 占用率直接拉满。3. OV9650 初始化寄存器照着填没错但要知道每项在干嘛3.1 上电与复位时序OV9650 初始化不是上来就写寄存器。先保证供电正常然后把 PWDN 拉低让芯片退出掉电模式RESETB 至少拉低再拉高中间留出足够延时。这个时序经常被人忽略导致传感器根本没醒过来后面写再多寄存器都白搭。我见过一个案例模组的 PWDN 引脚被上拉到高电平驱动加载、I2C 初始化全都正常就是不出图最后发现芯片一直处于掉电状态。复位之后第一件事是读芯片 ID。0x0A 和 0x0B 分别存厂商 ID 和版本号正常 OV9650 读回来是 0x7F 和 0xA2。读不到这个值直接查 I2C 地址、电源和 MCLK 时钟不用往下调。这里插一句网上很多初始化表是从 OV9655 抄来的读回来的 ID 可能是 0x7F 0xA5寄存器虽然大体兼容但细节有差异务必以自己手上这颗芯片读回的 ID 为准。3.2 关键寄存器分组初始化表通常有上百行网上能搜到一整套但那些值不是让你无脑抄的。按功能分组理解调试时才不会懵。我这里列出几个最关键的寄存器寄存器作用说明0x12 COM7软件复位、格式切换、彩条模式bit0 软复位bit2 选 VGA/CIFbit1 开彩条0x11 CLKRC内部时钟分频直接决定 PCLK 频率分频错了帧率会异常0x13 COM8自动曝光/增益/白平衡开关暗处全黑时优先检查这里0x40 COM15输出格式选择YUV422 还是 RGB565必须和 CAMIF 目标格式对齐0x15 COM10PCLK/HREF/VSYNC 极性和后端 CIGCTRL 极性位是同一件事的两端static const struct ov9650_reg ov9650_vga_yuv[] { {0x12, 0x80}, /* 软复位 */ {0x11, 0x81}, /* 时钟分频 */ {0x12, 0x04}, /* VGA 模式 */ {0x13, 0xBB}, /* 打开 AGC/AEC */ {0x15, 0x00}, /* 极性默认 */ {0x40, 0x40}, /* YUV422 输出 */ /* 完整表按厂商 datasheet 补充 */ };我特别强调 0x40 这个寄存器如果 CAMIF 配的是 RGB565传感器却还是 YUV 输出画面不会正常最常见的表现就是颜色发绿或者整体偏色反过来传感器输出 RGB565、CAMIF 按 YUV 解画面直接是花的。格式不匹配在黑屏问题里占比很高。3.3 初始化函数的组织方式我习惯把初始化表做成静态表用循环逐条写入。每条写入后做一次回读校验失败就把寄存器地址和期望值打出来配合 I2C 总线上的波形问题定位非常快。这个函数放在驱动里只调用一次但 mcu 侧烧录、产线调试时也经常要单独跑它所以最好把它导出成一个独立的接口别和驱动 probe 逻辑揉在一起。彩条模式寄存器值得单独说。COM7 的 bit1 置 1 后传感器不再输出图像数据而是输出一组内置的彩色条纹。这个功能在调试链路时是杀手锏后面排查黑屏的章节会重点用到它。4. 测试程序从摄像头到 LCD 的最后一公里4.1 采集流程骨架驱动写完测试程序就是快速验证手段。流程固定open /dev/video0设置 640x480 UYVY 格式申请 4 个 mmap 缓冲全部入队STREAMON然后循环 DQBUF 取帧、处理完再 QBUF 还回去。int fd open(/dev/video0, O_RDWR); struct v4l2_format fmt {0}; fmt.type V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width 640; fmt.fmt.pix.height 480; fmt.fmt.pix.pixelformat V4L2_PIX_FMT_UYVY; ioctl(fd, VIDIOC_S_FMT, fmt); struct v4l2_requestbuffers req {0}; req.count 4; req.type V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory V4L2_MEMORY_MMAP; ioctl(fd, VIDIOC_REQBUFS, req); /* 循环 mmap、QBUF、STREAMON、DQBUF */一个小提醒缓冲区数量至少 4 个太少容易丢帧取帧和处理最好分开否则 DQBUF 会阻塞住整个流程。我在 2440 上实测3 个缓冲在 640x480 下偶尔会出撕裂加到 4 个就稳定了。4.2 YUV 转 RGB 和 framebuffer 显示OV9650 在 YUV422 模式下每个像素 16 位顺序可能是 UYVY 也可能是 YUYV取决于 0x3A 到 0x3E 那组寄存器。拿到一帧后我通常做两件事一是直接存成图片文件方便对比二是转成 RGB565 显示到 framebuffer。static inline void uyvy_to_rgb565(const unsigned char *src, unsigned short *dst, int pixels) { for (int i 0; i pixels; i 2) { int u src[0] - 128, y0 src[1] - 16; int v src[2] - 128, y1 src[3] - 16; int r (298 * y0 409 * v 128) 8; int g (298 * y0 - 100 * u - 208 * v 128) 8; int b (298 * y0 516 * u 128) 8; dst[0] ((r 3) 11) | ((g 2) 5) | (b 3); src 4; dst 2; } }显示前先确认 framebuffer 的颜色格式。好多板子的 LCD 是 RGB565也有少见的 BGR565转换后颜色反过来是这一层的问题不是驱动的锅。判断方法很简单先往 framebuffer 里填一块纯红屏幕上是红色说明颜色顺序没问题是偏蓝或者其他颜色就调整高低字节的顺序。4.3 验证驱动好坏的几个观察点测试程序跑通后重点看三件事帧率是否接近设定值、图像有没有撕裂、颜色是否正确。帧率偏低说明 DMA 中断处理或者 PCLK 分频有问题撕裂和缓冲数量、DQBUF 时机有关颜色不对优先查传感器输出格式和 CAMIF 目标格式的匹配关系。另外如果画面是静止的但偶尔跳一帧多半是中断处理里没清标志位导致丢失帧同步中断。5. 驱动正常但黑屏完整排查链路5.1 第一阶段确认传感器真的在出数据先别急着怀疑驱动。驱动加载正常、ioctl 全部返回成功只能说明软件层面没问题传感器那边可能根本没出图。拿示波器测 OV9650 的 PCLK如果这个脚没有时钟翻转问题在传感器本身MCLK 没给上、PWDN 没拉低、复位没做彻底或者初始化表里时钟分频配错了。我遇到过一次 MCLK 被 GPIO 复用配置占了示波器一测才发现时钟根本没进来。如果 PCLK 有翻转再看 HREF 和 VSYNC。两个信号都有周期性脉冲说明传感器已经按帧输出问题就转移到 CAMIF 这一侧了。这一步还有一个快速验证手段读芯片 ID。驱动 probe 时把 0x0A、0x0B 读回来打日志确认是 0x7FA2。I2C 地址错了读不到 ID芯片没上电也读不到这个检查比任何分析都快。5.2 第二阶段用彩条模式做二分定位确认传感器出数据后立刻打开 OV9650 的彩条模式。COM7 的 bit1 置 1传感器不再输出图像数据而是输出一组内置彩色条纹。如果彩条能正常显示说明从传感器输出到 CAMIF 再到 DMA 再到显示的整条链路是通的问题在传感器配置或者镜头、曝光上。如果彩条也是黑的问题就锁定在这条链路里。这个二分定位能帮你把排查范围直接砍半是我在所有平台上的首选操作。很多同事 debug 时喜欢盯着寄存器一行行看效率其实很低。先确认链路通不通再谈细节参数。5.3 第三阶段CAMIF 极性、窗口和 DMA确认链路基本通的情况下黑屏最常见的原因是极性不匹配。OV9650 的 PCLK、HREF、VSYNC 都有可能在模组上被反相处理CAMIF 的 CIGCTRL 提供对应的补偿位。默认配置采样边沿如果和传感器输出对不上采到的全是无效数据表现就是黑屏或花屏。正确的姿势是一次只翻转一位采集一帧看效果不要三个位一起改。我自己遇到过 PCLK 极性反了导致整帧偏黑的案例单独翻转 INV_PCLK 之后立刻正常。HREF 反了通常表现是画面上下颠倒或者大片黑边VSYNC 反了则可能完全不触发帧中断。还要确认 CAMIF 的源窗口配置和实际分辨率一致。OV9650 输出 VGA 640x480CISRCFMT 里如果写成了 352x288DMA 按错误的宽高搬运数据画面不是黑屏就是严重错位。我把源窗口打印出来和传感器配置对比这个问题基本一眼就能看出来。如果以上都对检查 DMA第一是缓冲物理地址的对齐要求三星 CAMIF 对 DMA 地址有对齐限制不对齐会采不到数据第二是 cache 问题用普通 kmalloc 分配内存时CPU 和 DMA 之间没有同步CPU 读到的可能是旧数据看起来就是黑屏。用 dma_alloc_coherent 或者采集后做 cache 无效化都能解决。5.4 颜色格式和暗光环境的隐藏坑颜色格式不匹配也会表现为黑屏或者全绿画面。传感器配 YUV422CAMIF 目标却设成 RGB565或者 UYVY 和 YUYV 顺序搞反画面上经常是全黑、全绿或者严重偏色。遇到这种情况把传感器输出格式固定成一种CAMIF 完全对齐再逐个排查。另一个隐藏坑是暗光环境。COM8 里的 AGC/AEC 如果没打开室内光线不足时传感器输出接近全黑的数据看起来就像黑屏。不要只盯着信号链路先拿手电筒照一下镜头如果画面立刻变亮就是曝光配置的问题。我在 2440 上第一次遇到这个问题时还以为是 DMA 坏了折腾了半天才发现是光线不够。5.5 固定排查顺序我把整套排查顺序固定下来踩坑效率高很多读芯片 ID确认 I2C 通路和传感器上电状态。示波器确认 MCLK 存在、PCLK 有翻转。打开彩条模式验证整条链路是否通。逐一尝试 INV_PCLK、INV_HREF、INV_VSYNC 极性组合。核对 CAMIF 源窗口、目标格式和传感器两端配置。检查 DMA 地址对齐、cache 同步和中断触发。哪一步不过就卡在哪一步不要跳。整套走下来绝大多数黑屏问题半小时内能定位。6. 从 2410 到 A8 的移植改动量比想象中小6.1 哪些代码原封不动驱动里和传感器相关的部分完全可以复用I2C 读写封装、OV9650 初始化表、芯片 ID 校验、V4L2 的 file_operations 方法。这一层只依赖 i2c_client 和标准 V4L2 接口不依赖具体 SoC。测试程序更不用说应用层只认 /dev/video0 和 ioctl从 2410 换到 A8 几乎零改动。我当初把同一套代码从 2440 搬到 S5PV210 上传感器相关文件一行没改只重写了采集控制器那一个文件。6.2 需要重写的平台相关部分真正要改的是采集控制器封装。从 2440 的 CAMIF 换成 S5PV210 的 FIMC 或者 AM335x 的 VPFE寄存器基址、寄存器位定义、DMA 分配和对齐要求、中断号、时钟使能这几个全部要重写。但它们干的活完全一样配置输入格式、配置输出格式、启动 DMA、等待中断。把这一层抽象成统一接口平台差异就被隔离在一个文件里。A8 平台的内核一般比较新可以直接用 vb2 框架缓冲管理、队列调度都不用自己写反而比老内核上简单。老内核的 videobuf 在 DQBUF 的阻塞唤醒上有不少坑新框架成熟很多。6.3 移植时最容易漏掉的地方GPIO 复用和时钟门控是最容易漏的。2410 上摄像头接口的引脚基本固定A8 平台经常要配 PINMUX不配就永远采不到数据。CAMIF 模块的时钟也要单独使能漏了的话寄存器写了没反应或者 PCLK 完全没输入。我见过别人改完寄存器表一直黑屏最后发现是 PINMUX 没配置这种问题示波器都看不出来只能逐个引脚核对。最后分享一个我在三块板子上反复用的经验调试阶段永远先开彩条再谈出图。彩条能帮你省下至少一个下午。另外别迷信网上抄来的寄存器表不同批次模组对同一组数值的反应不完全一样重点核对读回的芯片 ID以及传感器输出格式和 CAMIF 目标格式这对组合。OV9650 这套折腾下来你对 V4L2、DMA、I2C 的理解会比看十篇文档都深。本文还有配套的精品资源点击获取
返回列表