☰
IMX335 在 Hi3559 上的驱动适配:从寄存器到 VI 管线
2026/10/7 13:29:52 网站建设 项目流程

简介:这是一份面向嵌入式Linux驱动开发者的Sony IMX335传感器驱动适配资料,针对海思Hi3559平台定制,解决图像传感器在该芯片上无法正常初始化与取流的问题。资源包共6个文件,以C源码与头文件为核心,包含imx335_cmos.c、imx335_sensor_ctl.c及对应头文件,另附Makefile与编译生成的.o目标文件,整体约362KB,结构精简,便于直接编译验证。驱动已针对Hi3559的接口协议、时序控制与电源管理完成适配调试,功能验证无误,可帮助读者理解V4L2框架下传感器注册、寄存器配置、MIPI CSI-2数据传输及中断与帧率控制等关键环节。目前已有1072人学习下载,适合从事安防监控、无人机或工业相机开发、需要快速移植IMX335驱动的工程师参考,也可作为Hi3559平台ISP调试与图像采集链路的实践素材。

1. IMX335 上 Hi3559:一颗 500 万像素 Sensor 的驱动适配到底难在哪

手里有一块 Hi3559A 的开发板,又拿到几片索尼 IMX335 模组,想把它们接起来跑出图——这件事听起来只是「写个驱动」,实际动手才会发现,它横跨了 MIPI 物理层、I2C 寄存器时序、海思 MPP 的 VI 管线配置和 ISP 标定四条链路,任何一环对不上,现象都是黑屏或者花屏,而日志里往往什么都不报。IMX335 是一颗 1/2.8 英寸、最高 2592×1944@30fps 的 CMOS Sensor,走 MIPI CSI-2 接口,I2C 从地址 0x36(8bit 写地址 0x6C);Hi3559 系列则是海思面向智能视觉的 SoC,Sensor 接入靠的是 MPP 框架下的 sensor 驱动 + ISP 库。所谓「适配」,本质是把索尼的寄存器手册翻译成海思 sensor 驱动框架能识别的结构体,再让 VI 能正确采样到 MIPI 数据。这篇笔记面向的是已经能编译海思 SDK、手上有示波器或逻辑分析仪的嵌入式工程师,我会把寄存器配置、驱动注册、VI 绑定和踩坑逐条拆开,新手能照着走,熟手能直接对参数边界。

2. 先搞懂 Hi3559 的 Sensor 驱动框架长什么样

2.1 海思 MPP 里 Sensor 驱动的三层结构

海思的 sensor 适配不是写一个 Linux 字符设备那么简单,它嵌在 MPP(Media Process Platform)里,分三层。最底层是 I2C 读写和上电时序,负责把寄存器值真正写进 Sensor;中间层是sample_comm_isp里注册的 sensor 对象,包含sensor_obj、sensor_exp_func这些回调;最上层是 VI(Video Input)设备,通过HI_MPI_VI_SetDevAttr把 MIPI 参数、时序和 sensor 时钟绑上去。理解这三层,才知道改哪里。很多人一上来就改寄存器表,结果发现连 I2C 都没通,白忙一天。

驱动代码通常放在 SDK 的mpp/component/isp/sensor/sony_imx335/目录下,核心文件是imx335_sensor_ctl.c(寄存器操作)和imx335_cmos.c(回调注册)。imx335_cmos.c里那个sensor_register_callback是入口,它把cmos_init、cmos_exit、cmos_set_image_mode等函数指针交给 ISP 框架。你要做的第一件事,是确认 SDK 里有没有现成的 imx335 目录——有就改,没有就从同系列的 imx290 或 imx415 抄一份骨架,因为索尼这几颗的寄存器风格接近。

2.2 IMX335 的关键寄存器分组

IMX335 的寄存器按功能分成几组,适配时按组推进比逐条查手册高效得多。第一组是系统控制,地址 0x3000 附近,负责 standby 释放、主时钟分频;第二组是 MIPI 输出配置,0x3000~0x30xx 区间,决定 lane 数、输出位宽、Lane 速率;第三组是曝光和增益,0x3500 附近,这是 AE 调用的核心;第四组是帧率和裁剪窗口,0x3700 附近。下面这张表是我实际适配时整理的常用寄存器,值对应 2592×1944@30fps、4 lane、10bit 输出:

寄存器典型值作用备注
0x30000x01standby 控制写 0x01 进 standby,0x00 释放
0x30020x00主时钟分频配合 INCK 频率
0x300C0x3BMIPI 全局配置与 lane 数相关
0x30180x04Lane 模式4 lane 时按手册设
0x30300x00输出位宽10bit 输出
0x30500x00黑电平相关影响暗部
0x35000x00曝光高字节AE 写入
0x37000x00裁剪窗口起始全尺寸时保持默认

提示:寄存器值不要照抄,必须对着你手上那颗 Sensor 的 datasheet 版本核对,索尼不同批次的手册在 0x300C 这类全局配置上会有差异。

2.3 从零建一个 sensor 目录的最小步骤

假设 SDK 里没有 imx335,我一般这样起步。先复制一份 imx290 目录改名,然后改 Makefile 里的编译目标,再逐步替换寄存器表。这一步的关键是让编译先通过,功能后面再调。

# 进入海思 SDK 的 sensor 目录 cd mpp/component/isp/sensor/ # 复制同系列 sensor 作为骨架 cp -r sony_imx290 sony_imx335 cd sony_imx335 # 重命名源文件,保持命名一致 mv imx290_sensor_ctl.c imx335_sensor_ctl.c mv imx290_cmos.c imx335_cmos.c mv imx290_cmos_ex.h imx335_cmos_ex.h

改完文件名,还要改Makefile里的SRCS和INC路径,以及mpp/component/isp/sensor/Makefile里把新目录加进编译列表。逻辑说明:海思的 sensor 编译是遍历目录的,目录名和文件名必须和 Makefile 里的变量对上,否则链接阶段会报undefined reference to sensor_register_callback。参数说明:sony_imx290只是骨架来源,如果你 SDK 里连 imx290 都没有,就找任意一个索尼 sensor 目录,寄存器风格差异靠后续替换解决。

3. 寄存器配置与 I2C 时序:让 Sensor 先出数据

3.1 上电时序和 I2C 地址确认

IMX335 上电有严格顺序:先给 AVDD(2.9V),再给 DOVDD(1.8V),最后给 DVDD(1.2V),复位信号 XCLR 要在电源稳定后拉高,INCK 时钟必须在复位释放前就稳定。顺序错了,Sensor 会锁死,I2C 读出来全是 0xFF 或 0x00。I2C 从地址是 0x36(7bit),换算成海思驱动里常用的 8bit 写地址就是 0x6C。先用i2c_read读一下 0x3000 寄存器,能读到非 0xFF 的值,说明 I2C 通了。

/* imx335_sensor_ctl.c 里的 I2C 地址定义 */ #define IMX335_I2C_ADDR 0x6C /* 8bit 写地址,对应 7bit 0x36 */ #define IMX335_I2C_ADDR_READ 0x6D /* 读地址 */ /* 读一个寄存器,返回 0 表示成功 */ static int imx335_read_reg(unsigned int addr, unsigned char *val) { /* 海思封装好的 I2C 读接口,addr 是 16bit 寄存器地址 */ return sensor_i2c_read(IMX335_I2C_ADDR, addr, val, 1); }

逻辑说明:海思的sensor_i2c_read内部会处理寄存器地址的 16bit 拆分,你只需要给对从地址。参数说明:IMX335_I2C_ADDR必须是 8bit 形式,如果你写成 0x36,读出来会一直失败,这是最常见的翻车点之一。

3.2 初始化寄存器表的写法

初始化表是一个{addr, val}数组,按顺序写下去。海思的sensor_write_reg通常带延时参数,因为有些寄存器写完要等几毫秒才能写下一条。下面是我用的初始化片段,重点是 standby 释放和 MIPI 配置的顺序。

/* 初始化寄存器序列,最后一项用 0xFF 结尾 */ static const struct imx335_reg imx335_init_seq[] = { {0x3000, 0x01}, /* 先进入 standby */ {0x3002, 0x00}, /* 主时钟分频 */ {0x300C, 0x3B}, /* MIPI 全局配置 */ {0x3018, 0x04}, /* 4 lane 模式 */ {0x3030, 0x00}, /* 10bit 输出 */ {0x3050, 0x00}, /* 黑电平 */ {0x3000, 0x00}, /* 释放 standby,开始出图 */ {0xFFFF, 0xFF}, /* 结束标志 */ }; static int imx335_init(void) { int i = 0; while (imx335_init_seq[i].addr != 0xFFFF) { /* 写寄存器,部分寄存器需要延时 */ sensor_write_reg(IMX335_I2C_ADDR, imx335_init_seq[i].addr, imx335_init_seq[i].val); i++; } return 0; }

逻辑说明:先写 0x3000=0x01 进 standby,是为了在配置其他寄存器时 Sensor 不输出乱数据;所有配置写完再写 0x3000=0x00 释放。参数说明:0x3018的 lane 模式值必须和硬件走线一致,4 lane 接成 2 lane 会导致 MIPI 采样错位,现象是花屏。0x3030的位宽要和 VI 里配置的DATA_TYPE对上,10bit 对应 RAW10。

3.3 用逻辑分析仪验证 I2C 和 MIPI

寄存器写完不代表成功,要用逻辑分析仪抓 I2C 波形,确认每条写都有 ACK。如果某条没有 ACK,先查从地址,再查上拉电阻(一般 4.7kΩ)。MIPI 侧用示波器看 CLK lane 有没有差分信号,速率对不对。IMX335 在 2592×1944@30fps、10bit、4 lane 下,Lane 速率大约 594Mbps,这个值要和 VI 里MIPI_RATE配置一致,差太多会丢帧或黑屏。

4. 把 Sensor 挂进 VI 管线:MPP 配置与绑定

4.1 VI 设备属性里的 MIPI 参数

Sensor 出数据只是第一步,Hi3559 的 VI 要正确采样,得配VI_DEV_ATTR_S。这里最容易错的是MIPI_WDR_MODE、LANE_ID和DATA_TYPE。下面是一个 4 lane、RAW10、线性模式的配置示例。

VI_DEV_ATTR_S dev_attr; memset(&dev_attr, 0, sizeof(dev_attr)); dev_attr.enIntfMode = VI_MODE_MIPI; /* MIPI 接口 */ dev_attr.enWorkMode = VI_WORK_MODE_1Multiplex; dev_attr.enScanMode = VI_SCAN_PROGRESSIVE; dev_attr.enDataSeq = VI_DATA_SEQ_YUYV; /* RAW 域下按实际设 */ dev_attr.enInputDataType = DATA_TYPE_RAW_10BIT; /* 和 sensor 输出位宽一致 */ dev_attr.szInputSize.width = 2592; dev_attr.szInputSize.height = 1944; dev_attr.enCompressMode = COMPRESS_MODE_NONE; dev_attr.stDevRect.x = 0; dev_attr.stDevRect.y = 0; dev_attr.stDevRect.width = 2592; dev_attr.stDevRect.height = 1944; /* MIPI 相关 */ dev_attr.enMipiRate = MIPI_RATE_594M; /* 与 sensor lane 速率匹配 */ dev_attr.enLaneNum = 4; /* 4 lane */

逻辑说明:enInputDataType必须和 Sensor 的0x3030输出位宽一致,RAW10 对 RAW10。参数说明:enMipiRate是枚举值,海思 SDK 里通常有 594M、891M 等档位,选最接近实际速率的;enLaneNum要和硬件走线一致,接 4 根就写 4。

4.2 绑定 ISP 和启动 VI 的顺序

VI 配好后,要按VI_CreateDev→VI_SetDevAttr→VI_EnableDev→ 绑定 ISP →VI_StartPipe的顺序调用。顺序错了会返回HI_ERR_VI_NOT_CONFIG之类的错误码。下面这段是典型的启动流程。

/* 创建并配置 VI 设备 */ HI_MPI_VI_SetDevAttr(0, &dev_attr); HI_MPI_VI_EnableDev(0); /* 绑定 ISP,sensor 类型要和注册的一致 */ HI_MPI_ISP_SetSensorType(0, SONY_IMX335); /* 启动 pipe,开始出图 */ HI_MPI_VI_StartPipe(0);

逻辑说明:HI_MPI_VI_EnableDev之后才能绑 ISP,否则 ISP 拿不到设备句柄。参数说明:SONY_IMX335这个枚举要在hi_comm_isp.h里定义,如果你新增了 sensor,记得在枚举里加一项,否则SetSensorType会失败。

4.3 用 sample 程序快速验证出图

海思 SDK 自带sample_vi之类的示例,改一下 sensor 类型就能跑。我一般先用它验证,不要一上来就写自己的应用。跑通后能看到 YUV 文件,用yuvplayer打开,能出正常图像说明驱动链路通了。如果黑屏,先看cat /proc/umap/vi里的帧计数,计数不涨说明 MIPI 没数据;计数涨但图像花,查 DATA_TYPE 和 lane 配置。

5. 适配过程中最容易翻车的几个点

5.1 现象:I2C 读寄存器一直返回 0xFF

原因:从地址写错,或者上电时序不对导致 Sensor 没起来。海思驱动里从地址要 8bit,很多人直接填 7bit 的 0x36。解决:改成 0x6C,再用逻辑分析仪确认电源和 XCLR 时序,XCLR 必须在 INCK 稳定后至少延时 1ms 再拉高。

5.2 现象:VI 帧计数正常但图像全绿或全花

原因:DATA_TYPE 和 Sensor 输出位宽不匹配,或者 MIPI lane 数配错。RAW10 配成 RAW12,解出来的像素就是错的。解决:核对0x3030和enInputDataType,再确认enLaneNum和硬件一致。全绿还可能是 ISP 没加载标定参数,检查isp_calib文件路径。

5.3 现象:图像能出但曝光不生效,画面过亮或过暗

原因:AE 回调没注册,或者曝光寄存器地址写错。IMX335 的曝光是 0x3500 三字节,海思 AE 库会调cmos_set_exposure,如果这个函数没实现或地址错,AE 就失效。解决:在imx335_cmos.c里实现cmos_set_exposure,按手册把 24bit 曝光值拆到 0x3500/0x3501/0x3502。

5.4 现象:编译报 undefined reference to sensor_register_callback

原因:新加的 sensor 目录没进 Makefile,或者文件名和 Makefile 变量不一致。解决:检查mpp/component/isp/sensor/Makefile里的SRCS是否包含新目录,文件名是否和imx335_cmos.c一致。海思的编译脚本对大小写敏感,IMX335和imx335不能混。

5.5 现象:跑一段时间后丢帧或 VI 报超时

原因:MIPI 速率配置偏高或偏低,或者 DDR 带宽不够。2592×1944@30fps 的 RAW10 数据量不小,如果同时跑编码,带宽会紧张。解决:把enMipiRate调到和实际匹配的档位,降低帧率到 25fps 试试,或者检查 VI 的 buffer 数量是否够。

6. 进阶:用寄存器回读和 ISP 标定把画质调到位

驱动跑通只是及格线,真正让 IMX335 在 Hi3559 上出好图,还得做两件事:寄存器回读校验和 ISP 标定。寄存器回读是在初始化后把关键寄存器读回来比对,确认写入生效。我一般写个小函数,把 0x3000、0x300C、0x3018、0x3030 读出来打印,和预期值对不上就说明 I2C 写失败或 Sensor 没接受。

/* 回读关键寄存器,用于调试 */ static void imx335_dump_regs(void) { unsigned char val; unsigned int regs[] = {0x3000, 0x300C, 0x3018, 0x3030}; int i; for (i = 0; i < 4; i++) { if (imx335_read_reg(regs[i], &val) == 0) { printf("reg 0x%04X = 0x%02X\n", regs[i], val); } } }

逻辑说明:回读能区分「写没写进去」和「写进去了但 Sensor 没执行」两种情况。参数说明:回读时要注意有些寄存器是只读的,读出来和写入值不同是正常的,以手册标注为准。

ISP 标定这块,海思提供isp_calib工具,需要暗场、亮场和色卡图。IMX335 的暗电流和镜头 shading 因模组而异,不标定的话暗部噪点和四角偏色会很明显。标定流程是:先拍暗场算 BLC,再拍亮场算 LSC,最后拍色卡算 AWB 和 CCM。标定文件生成后放到/etc/isp/下,VI 启动时加载。这一步没有捷径,环境光要稳定,色卡要正对镜头。

我自己的习惯是,每换一批模组就重新跑一次暗场标定,因为索尼不同批次的 Sensor 暗电流有差异,用旧标定文件会出现固定位置的噪点。另外,调试时把printf打在cmos_init和cmos_set_exposure里,能快速定位是初始化问题还是 AE 问题。这套流程我在几块 Hi3559 板子上都跑过,最花时间的从来不是写代码,而是对着示波器确认 MIPI 那几根差分线有没有虚焊。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询