
简介这份资源面向嵌入式Linux驱动开发者与海思平台调试人员提供Sony IMX335图像传感器在Hi3559芯片上的适配驱动代码解决传感器与主控之间接口协议、时序控制、电源管理等兼容性问题适用于安防监控、智能分析等场景下的摄像头模组开发。压缩包共6个文件约362KB包含2个C源文件、2个编译产物.o文件、1个头文件与1个Makefile分别对应传感器控制逻辑、CMOS驱动实现、寄存器配置头文件及编译构建脚本结构紧凑便于直接集成到V4L2框架中。目前已有1072人学习下载。读者可参考其初始化流程、数据传输机制与错误处理思路快速完成IMX335在Hi3559上的驱动移植与功能验证减少重复调试成本。1. 拆开 sony_imx335 driver 适配 hi3559 的源码包它到底能跑通什么手里拿到一个sony_imx335 hi3559.zip解压后是Makefile、imx335_sensor_ctl.o、imx335_cmos.o、imx335_cmos_ex.h、imx335_sensor_ctl.c、imx335_cmos.c这几个文件第一反应往往是「这不就是个 sensor 驱动吗能有多复杂」。但真正在 hi3559 上点过 IMX335 的人都知道从 sensor 上电到 ISP 出第一帧可用图像中间隔着一堆寄存器时序、I2C 地址、MIPI lane 映射和时钟树配置任何一处对不上现象就是黑屏或者花屏而且 log 里往往什么都不报。这份资源的价值就在于它把 IMX335 在 hi3559 平台上的初始化序列、sensor 控制接口和 cmos 参数结构体都落成了可编译的 C 文件省掉了从 datasheet 一行行抠寄存器的时间。它适合两类人一类是正在 hi3559 系列hi3559A / hi3559C / hi3559V200 等上做多路摄像头接入的嵌入式工程师需要一份能直接编进 SDK 的 sensor 驱动参考另一类是想搞清楚海思 sensor 驱动框架里cmos.c和sensor_ctl.c各自职责边界的开发者。需要说明的是这份包给的是驱动源码和编译产物不含完整 SDK也不含 ISP 调优参数它的定位是「让 sensor 能出图」不是「让图好看」。下面按「文件职责 → 编译接入 → 寄存器与时序 → 排错 → 进阶验证」的顺序拆开讲每一步都尽量落到能直接抄的参数和命令上。2. 文件职责与海思 sensor 驱动框架cmos.c 和 sensor_ctl.c 谁管什么2.1 先认清海思 MPP 里 sensor 驱动的分层海思的 sensor 驱动不是一个大文件而是按职责切成两层。上层是cmos层负责把 sensor 注册进 MPP 的 ISP 体系提供sample_comm_isp之类流程需要的回调下层是sensor_ctl层直接跟硬件打交道管 I2C 读写、上电时序、复位、时钟使能。这份包里imx335_cmos.c和imx335_sensor_ctl.c正好对应这两层imx335_cmos_ex.h则是暴露给外部引用的寄存器地址和结构体声明。理解这个分层很关键因为出问题时排查方向完全不同如果系统起来后cat /proc/umap/isp里根本看不到 sensor那是 cmos 层注册没成功如果注册成功但出图异常那多半是 sensor_ctl 层的时序或寄存器序列有问题。很多人一上来就改寄存器结果发现是 cmos 层的enSnsType没对上白折腾半天。常见做法是先把imx335_cmos.c里的sensor_register流程读一遍确认它调用了哪些HI_MPI_ISP_SensorRegCallBack之类的接口再看imx335_sensor_ctl.c里的imx335_init被谁调用、在什么时机调用。这两个文件的调用关系理清了后面改参数才有方向。2.2 逐个文件说明职责与改动入口文件职责常见改动点imx335_cmos.csensor 注册、ISP 回调绑定、sensor 属性填充sensor 类型枚举、分辨率、帧率、WDR 模式imx335_cmos_ex.h寄存器地址宏、结构体、外部声明I2C 地址、寄存器基址、导出函数声明imx335_sensor_ctl.c上电时序、I2C 读写、初始化寄存器序列复位时序、时钟极性、初始化数组imx335_sensor_ctl.o编译产物验证编译是否通过不需要改但可对比重新编译结果imx335_cmos.o编译产物同上Makefile编译规则指定交叉工具链和头文件路径工具链前缀、SDK 头文件路径这张表建议对着实际文件看一遍。imx335_cmos_ex.h里通常会有类似#define IMX335_I2C_ADDR 0x34这样的宏I2C 地址写错是最常见的翻车点之一因为 IMX335 的 7 位地址是 0x1A左移一位后是 0x34但有些代码里直接写 0x1A编译不报错运行时 I2C 通信全失败。2.3 编译接入Makefile 怎么改才能编进 SDK这份包自带的Makefile是独立编译用的真正接入 SDK 时一般不会直接用它的规则而是把.c文件挂到 SDK 的 sensor 编译体系里。但独立编译可以先验证代码本身有没有语法问题。典型做法是改交叉工具链前缀和头文件路径# 交叉工具链按实际 SDK 的路径改 CROSS_COMPILE ? arm-himix200-linux- CC : $(CROSS_COMPILE)gcc # SDK 头文件路径指向 MPP 的 include 目录 MPP_INC : /path/to/hi3559_sdk/mpp/include MPP_LIB : /path/to/hi3559_sdk/mpp/lib CFLAGS : -Wall -O2 -I$(MPP_INC) -I. LDFLAGS : -L$(MPP_LIB) OBJS : imx335_sensor_ctl.o imx335_cmos.o all: $(OBJS) %.o: %.c $(CC) $(CFLAGS) -c $ -o $ clean: rm -f $(OBJS)这里CROSS_COMPILE必须和 SDK 实际使用的工具链一致hi3559 常见的是arm-himix200-linux-或aarch64-himix100-linux-用错工具链编出来的.o链接时会报架构不匹配。MPP_INC要指向 SDK 里mpp/include目录因为imx335_cmos.c会引用hi_comm_isp.h、mpi_isp.h这类头文件。编译通过只说明语法和头文件没问题不代表运行时能跑通真正的验证在板子上。提示如果 SDK 里已经有其他 sensor 驱动比如 imx334、ov4689最省事的接入方式是把这份包的文件名和函数名按 SDK 的命名规范改一遍然后挂到 SDK 的 sensor Makefile 里而不是自己写一套编译规则。3. IMX335 寄存器初始化与时序上电、复位、I2C 三步不能乱3.1 上电时序先电后钟再复位IMX335 对上电顺序有要求datasheet 里写得很清楚先供 AVDD模拟电源典型 2.8V再供 DOVDD数字 IO典型 1.8V最后供 DVDD数字核心典型 1.2V。三路电都稳定后才能给 MCLK最后拉复位。顺序错了sensor 可能直接不响应 I2C或者能读 ID 但出不了图。imx335_sensor_ctl.c里一般会有一个imx335_init或者sensor_init函数里面按顺序调用电源使能、延时、复位释放。常见写法是/* 上电时序延时值按实际硬件调整 */ static void imx335_power_on(void) { /* 1. 使能 AVDD 2.8V */ enable_avdd(); usleep(1000); /* 2. 使能 DOVDD 1.8V */ enable_dovdd(); usleep(1000); /* 3. 使能 DVDD 1.2V */ enable_dvdd(); usleep(5000); /* 等电源稳定datasheet 要求至少 1ms这里给 5ms 余量 */ /* 4. 输出 MCLKIMX335 典型 24MHz 或 27MHz */ enable_mclk(); usleep(1000); /* 5. 释放复位低电平复位则拉高 */ reset_high(); usleep(10000); /* 复位释放后等 sensor 内部初始化 */ }这里的usleep值不是随便写的。电源稳定延时给太短sensor 内部 LDO 还没建立I2C 读出来全是 0xFF复位释放后的延时给太短sensor 还没准备好接收配置第一帧配置可能丢。我一般会在每个延时后面加一句printf打时间戳用示波器抓 MCLK 和复位脚确认实际时序和代码一致。3.2 I2C 读写地址、位宽、寄存器页切换IMX335 的 I2C 从地址是 0x1A7 位写操作时左移一位变 0x34读操作是 0x35。寄存器地址是 16 位数据是 8 位所以一次写寄存器要发 3 个字节地址高 8 位、地址低 8 位、数据。海思 SDK 里一般用HI_MPI_ISP_SensorWrite或者直接调ioctl走 I2C但底层逻辑是一样的。/* I2C 写 16 位寄存器地址8 位数据 */ static int imx335_write_reg(unsigned short reg_addr, unsigned char value) { unsigned char buf[3]; buf[0] (reg_addr 8) 0xFF; /* 地址高字节 */ buf[1] reg_addr 0xFF; /* 地址低字节 */ buf[2] value; /* 数据 */ /* 实际调用 SDK 的 I2C 接口这里用伪代码表示 */ return i2c_write(IMX335_I2C_ADDR, buf, 3); }IMX335 还有「页切换」的概念部分寄存器分布在不同的页里写之前要先写页选择寄存器。如果初始化序列里漏了页切换会出现「寄存器写进去了但没生效」的玄学现象。排查方法很简单写完一个寄存器后立刻读回来如果读回值和写入值不一致先检查页选择。3.3 初始化寄存器序列从 datasheet 到数组imx335_sensor_ctl.c里最长的部分就是初始化寄存器数组通常是一个{addr, value}的二维数组按顺序写下去。这个序列一般来自 Sony 提供的初始化脚本或者参考设计不能随便删改顺序因为有些寄存器有依赖关系比如先配 PLL 再配 MIPI 输出。/* 初始化序列片段完整序列按实际文件为准 */ static const struct imx335_reg imx335_init_seq[] { {0x3000, 0x01}, /* 软件复位 */ {0x3002, 0x00}, /* 解除复位 */ {0x300A, 0x3B}, /* PLL 配置影响 MCLK 到像素时钟的倍频 */ {0x300B, 0x00}, /* ... 中间省略大量寄存器 ... */ {0x3018, 0x04}, /* MIPI lane 数配置4 lane 还是 2 lane */ {0x3019, 0x00}, };改这个序列时最容易踩的坑是 MIPI lane 数配置和硬件实际走线不一致。比如硬件只走了 2 lane代码里配成 4 lane现象是 MIPI 接收端报错或者出图只有一半。另一个坑是 PLL 配置和 MCLK 频率不匹配导致帧率不对或者根本不出图。改完序列后建议先用i2cget之类的工具把关键寄存器读回来对比确认写入生效。4. 适配 hi3559 的避坑与排查黑屏、花屏、I2C 失败怎么定位4.1 现象系统启动后 ISP 里看不到 sensor原因通常是 cmos 层注册失败可能是enSnsType和 SDK 里定义的 sensor 类型枚举对不上或者sensor_register没被调用。解决方法是先确认 SDK 的sample_comm里有没有把这份 sensor 的注册函数挂进去再检查imx335_cmos.c里的 sensor 类型宏是否和 SDK 头文件里的一致。如果 SDK 里没有 IMX335 的枚举需要自己加一个并确保 ISP 初始化时传的是这个新枚举。4.2 现象I2C 通信失败读 sensor ID 返回 0xFF 或 0x00原因有几个I2C 地址写错0x1A 写成 0x34 或反过来、上电时序不对导致 sensor 没起来、I2C 总线被其他设备占用、上拉电阻没焊。解决方法是先用示波器或逻辑分析仪抓 I2C 波形确认有起始信号和 ACK再用万用表量 sensor 的电源脚确认三路电都正常最后检查设备树或板级配置里 I2C 控制器有没有使能。血泪经验是有时候板子上 I2C 上拉电阻是 10K速率跑 400K 时波形上升沿太慢降到 100K 就好了。4.3 现象能出图但花屏、偏色、亮度异常原因多半是 MIPI lane 映射不对、ISP 的 Bayer 顺序配错、或者 WDR 模式不匹配。IMX335 支持多种输出模式线性模式和 WDR 模式的寄存器配置不同如果 sensor 配成 WDR 但 ISP 按线性处理就会出图异常。解决方法是先确认 sensor 输出模式再检查 ISP 的bayer格式和wdr模式是否一致。偏色问题还要看 ISP 的 AWB 有没有跑起来有时候是 AWB 没收敛不是驱动的问题。4.4 现象帧率不对或者跑一段时间后丢帧原因可能是 PLL 配置和 MCLK 不匹配、MIPI 时钟频率超出接收端能力、或者 DDR 带宽不够。hi3559 多路接入时如果每路都跑高分辨率高帧率DDR 带宽容易成为瓶颈。解决方法是先用cat /proc/umap/vi看 VI 的帧率统计确认是 sensor 输出帧率不对还是后端处理丢帧再检查 MIPI 接收配置里的时钟频率是否和 sensor 输出匹配。如果带宽不够降低分辨率或帧率或者减少同时接入的路数。4.5 现象编译通过但链接报 undefined reference原因通常是 Makefile 里漏了 MPP 的库或者函数声明和 SDK 版本不匹配。hi3559 不同 SDK 版本的 MPP 接口可能有差异比如HI_MPI_ISP_SensorRegCallBack的参数个数变了。解决方法是先确认 SDK 版本再对照 SDK 里的头文件改函数签名链接时把-lmpi -lisp之类的库都加上顺序也有讲究被依赖的库放后面。5. 进阶验证用 sensor 读回值和抓帧确认驱动真的跑通了5.1 寄存器读回验证写进去的要能读出来驱动跑通的最低标准不是「编译过了」而是「关键寄存器写进去能读回来」。我一般会写一个小测试函数在初始化完成后把几个关键寄存器读回来打印/* 初始化后读回关键寄存器确认写入生效 */ static void imx335_dump_key_regs(void) { unsigned short regs[] {0x300A, 0x300B, 0x3018, 0x3019}; int i; for (i 0; i sizeof(regs) / sizeof(regs[0]); i) { unsigned char val 0; imx335_read_reg(regs[i], val); printf(reg 0x%04X 0x%02X\n, regs[i], val); } }把打印值和初始化序列里的期望值对比不一致的就要查页切换或者 I2C 时序。这一步能挡掉大部分「配置没生效」的问题。5.2 抓帧验证从 VI 节点拿一帧存成文件寄存器对了不代表出图对最终验证还是要抓帧。海思平台一般用sample_vi或者自己写程序从 VI 节点取帧存成 YUV 文件后用工具看。常见做法是跑 SDK 自带的sample_vi把 sensor 类型改成 IMX335看能不能出图。如果sample_vi能出图说明驱动基本没问题如果不出图看sample_vi的 log 里 VI 和 ISP 的报错信息比盲改寄存器高效得多。5.3 一个我常用的对比技巧手头如果有能正常出图的同平台 sensor 驱动比如 IMX334把两份sensor_ctl.c的初始化序列和时序函数并排对比差异点往往就是问题所在。IMX335 和 IMX334 在寄存器层面有不少相似之处对比着看比从头读 datasheet 快。从那以后我每次适配新 sensor都强制先找一份同平台能跑的驱动做参照再动手改省下来的时间够调好几轮 ISP 了。希望这份拆解能帮到你少走点黑屏的弯路。本文还有配套的精品资源点击获取