☰
IMX335 在 Hi3559 上的驱动适配与出图实战
2026/10/9 14:53:09 网站建设 项目流程

简介:本资源面向嵌入式Linux驱动开发者与海思平台音视频工程师,提供Sony IMX335 CMOS图像传感器在Hi3559平台上的适配驱动代码,解决传感器与主控芯片之间接口协议、时序控制、电源管理等兼容性问题,适用于安防监控、无人机、工业相机等场景的底层开发与调试。压缩包共6个文件,约362KB,包含2个C源文件、2个编译生成的.o目标文件、1个Makefile构建脚本和1个头文件,覆盖传感器寄存器配置、CMOS驱动逻辑及编译规则,结构紧凑便于直接集成到V4L2框架工程中。目前已有1072人学习下载,说明该适配方案在社区中具备一定参考价值。读者可据此了解IMX335在Hi3559上的初始化流程、MIPI CSI-2数据传输配置与中断处理思路,并借助已验证的驱动代码快速完成功能验证与二次开发。

1. IMX335 上 Hi3559:从“点不亮”到“出图”的完整适配路径

手里有一块 Hi3559A 的开发板,又拿到几片索尼 IMX335 的 sensor,想把它们凑成一套能出图的采集系统——这个场景在安防、机器视觉和嵌入式视觉方案里非常常见。IMX335 是一颗 1/2.8 英寸、最高 5MP(2592×1944)的 CMOS 图像传感器,MIPI CSI-2 输出,支持 2×2 binning 和多种分辨率模式;Hi3559A 则是海思面向高端 IPC 和 AI 视觉的 SoC,内置 ISP、MIPI 接收控制器和 VI 采集通路。把这两者接起来,核心工作就是写一份符合海思 MPI 框架的 sensor 驱动,让 ISP 能正确识别 sensor、配置时序、接收 RAW 数据并最终出图。

这件事适合谁?适合已经能编译海思 SDK、跑通过 sample_venc 或 sample_vio 的嵌入式工程师;也适合从其他平台(比如瑞芯微、全志)转过来、第一次接触海思 sensor 层的人。它解决的不是“写个 I2C 读写”这么简单的问题,而是要让 sensor 驱动、MIPI 配置、ISP 初始化三者对齐,任何一处参数错位都会导致花屏、绿屏甚至完全无中断。下面按“先理解框架、再动手写、最后排坑”的顺序展开。

2. 先搞懂 Hi3559 的 sensor 驱动框架:为什么不能只写 I2C

2.1 海思 MPI 里 sensor 驱动到底承担什么角色

很多人第一次接触海思平台,会以为 sensor 驱动就是“初始化寄存器 + 读写 I2C”。如果只做到这一步,你会发现系统能识别到 I2C 设备,但 VI 通路拿不到数据,或者 ISP 报“sensor not ready”。原因在于海思的 sensor 驱动是一个注册到 MPI 框架里的回调集合,它需要向 ISP 和 VI 提供一整套描述信息。

具体来说,一份完整的 IMX335 驱动要提供这些内容:

  • sensor 基本信息:名字、I2C 地址、输出接口类型(MIPI)、分辨率能力。
  • 总线回调:sensor_i2c_init、sensor_i2c_deinit、sensor_i2c_read、sensor_i2c_write,让上层通过统一接口访问寄存器。
  • 寄存器操作回调:sensor_reg_init、sensor_reg_exit,负责上电后的初始化序列和退出时的清理。
  • 模式与时序:sensor_set_image_mode、sensor_get_image_mode,以及每种模式对应的分辨率、帧率、MIPI 时钟、行场时序。
  • ISP 交互:把 sensor 的曝光、增益、白平衡等能力通过ISP_SENSOR_REGISTER_S结构体注册给 ISP,让 3A 算法能控制它。

换句话说,驱动是 sensor 和 ISP 之间的“翻译层”。你写的每一个回调,最终都会被mpi_isp或mpi_vi在合适的时机调用。理解这一点,后面写代码时才知道每个函数该填什么。

2.2 IMX335 的关键规格与 Hi3559 的匹配点

在动手前,先把 IMX335 的几组关键参数和 Hi3559 的接收能力对齐,否则后面调 MIPI 会非常痛苦。

项目IMX335 典型值Hi3559 侧关注点
最大分辨率2592×1944(5MP)VI 最大宽高、ISP 处理能力
MIPI 通道2 lane / 4 lane 可选MIPI RX 配置的 lane 数必须一致
输出格式RAW10 / RAW12ISP 的 bayer 格式配置
主时钟24MHz 或 27MHz开发板晶振供给,需与驱动一致
I2C 地址0x1A(7bit)总线号、地址要写对
帧率全尺寸约 30fps取决于 MIPI 时钟和 VI 带宽

这里最容易翻车的是MIPI lane 数和主时钟频率。IMX335 支持 2 lane 和 4 lane,但很多开发板默认走 2 lane;如果你驱动里按 4 lane 配,MIPI 接收端会一直报错。主时钟同理,24MHz 和 27MHz 对应的 PLL 配置不同,寄存器序列不能混用。

2.3 驱动文件在 SDK 里的位置与编译方式

海思 SDK 通常把 sensor 驱动放在类似smp/a55_linux/mpp/sample/或mpp/component/isp/sensor/的目录下。以常见的 Hi3559A SDK 为例,sensor 驱动一般位于:

# 进入 SDK 的 sensor 目录(不同 SDK 版本路径略有差异) cd Hi3559A_SDK/mpp/component/isp/sensor ls # 你会看到 sony_imx335、sony_imx290、ov_os05a 等目录

每个 sensor 一个目录,里面通常包含:

  • imx335_cmos.c:主驱动文件,实现所有回调。
  • imx335_cmos.h:寄存器定义、结构体声明。
  • imx335_sensor_ctl.c:I2C 读写和上电控制。
  • Makefile:编译规则。

编译时,SDK 的顶层 Makefile 会根据配置决定是否把该 sensor 编进库。你需要确认mpp/component/isp/sensor/Makefile或对应的.config里打开了 IMX335 的编译开关。常见做法是:

# 在 SDK 根目录执行整体编译 make clean make -j8 # 编译完成后,检查生成的库是否包含 imx335 符号 nm -D mpp/lib/libisp.so | grep imx335

如果nm看不到 imx335 相关符号,说明编译开关没打开,或者 Makefile 里没把imx335_cmos.o加进去。这一步不通过,后面 sample 里调用SAMPLE_COMM_ISP_SensorInit时会直接返回失败。

3. 写一份能跑的 IMX335 驱动:从 I2C 到 ISP 注册

3.1 I2C 读写与上电时序:最基础也最容易错

IMX335 的 I2C 是 16 位寄存器地址、8 位数据。海思的 sensor 框架要求你实现sensor_i2c_read和sensor_i2c_write,内部通常调用HI_MPI_I2C_Read或直接操作/dev/i2c-X。下面是一个典型的写寄存器实现:

/* imx335_sensor_ctl.c */ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> static int g_i2c_fd = -1; /* 打开 I2C 总线,bus_num 由开发板决定,常见是 0 或 1 */ int imx335_i2c_init(int bus_num) { char dev_name[32]; snprintf(dev_name, sizeof(dev_name), "/dev/i2c-%d", bus_num); g_i2c_fd = open(dev_name, O_RDWR); if (g_i2c_fd < 0) { perror("open i2c"); return -1; } return 0; } /* 写 16bit 寄存器地址 + 8bit 数据 */ int imx335_write_reg(unsigned int addr, unsigned int data) { unsigned char buf[3]; struct i2c_rdwr_ioctl_data packets; struct i2c_msg msgs[1]; buf[0] = (addr >> 8) & 0xFF; /* 寄存器高字节 */ buf[1] = addr & 0xFF; /* 寄存器低字节 */ buf[2] = data & 0xFF; /* 数据 */ msgs[0].addr = 0x1A; /* IMX335 7bit 地址 */ msgs[0].flags = 0; /* 写 */ msgs[0].len = 3; msgs[0].buf = buf; packets.msgs = msgs; packets.nmsgs = 1; if (ioctl(g_i2c_fd, I2C_RDWR, &packets) < 0) { perror("i2c write"); return -1; } return 0; }

逻辑说明:这里用I2C_RDWR一次性发送 3 个字节,前两字节是寄存器地址,第三字节是数据。参数上,0x1A是 IMX335 的 7bit 地址,如果你的硬件把 SID 引脚拉高,地址可能变成0x1B,必须对照原理图确认。bus_num也要和实际接线一致,接错总线会一直open失败或读写超时。

上电时序同样关键。IMX335 一般需要先给 AVDD、DVDD、DOVDD,再释放复位,最后给 MCLK。海思 SDK 里通常用 GPIO 控制复位和电源,代码里会调用HI_MPI_SYS_SetReg或直接操作/sys/class/gpio。如果上电顺序错,sensor 可能不响应 I2C,表现为读0x0000或0xFFFF。

3.2 寄存器初始化序列:别直接抄,要按模式改

IMX335 的初始化序列很长,通常有几百个寄存器。网上能找到的序列大多是全尺寸 2592×1944、2 lane、24MHz 的配置。如果你用的是其他模式,必须改对应的 PLL、裁剪窗口和 MIPI 输出设置。下面是一个初始化函数的骨架:

/* 初始化序列,按 2592x1944@30fps, 2lane, 24MHz 整理 */ static const struct imx335_reg imx335_init_seq[] = { {0x3000, 0x01}, /* 进入 standby */ {0x3002, 0x00}, /* 主模式设置 */ /* ... 省略中间大量 PLL、时序寄存器 ... */ {0x3000, 0x00}, /* 退出 standby,开始出图 */ }; int imx335_reg_init(void) { int i; for (i = 0; i < sizeof(imx335_init_seq)/sizeof(imx335_init_seq[0]); i++) { if (imx335_write_reg(imx335_init_seq[i].addr, imx335_init_seq[i].data) < 0) { printf("init seq fail at index %d\n", i); return -1; } } return 0; }

逻辑说明:数组里每个元素是“地址 + 数据”。实际项目中,这个数组会非常长,建议按功能分组注释,比如“PLL 配置”“MIPI 输出”“曝光默认值”。参数上,0x3000是 standby 控制寄存器,写0x01进入待机、写0x00退出,这个顺序不能反。如果你发现 I2C 写成功但不出图,优先检查 standby 是否真的退出了。

提示:不同批次的 IMX335 可能在个别寄存器上有差异,尤其是 PLL 相关的。如果全尺寸能出图但小分辨率不行,多半是裁剪窗口或 binning 配置没改对。

3.3 把驱动注册进 ISP:结构体填错就前功尽弃

海思 ISP 通过ISP_SENSOR_REGISTER_S结构体来识别 sensor。这个结构体里包含 sensor 名称、I2C 地址、模式数量、每种模式的描述,以及一堆回调函数指针。下面是一个简化示例:

/* imx335_cmos.c */ static ISP_SENSOR_REGISTER_S g_imx335_reg = { .pfn_sensor_init = imx335_reg_init, .pfn_sensor_exit = imx335_reg_exit, .pfn_sensor_set_mode = imx335_set_image_mode, .pfn_sensor_get_mode = imx335_get_image_mode, .pfn_sensor_i2c_init = imx335_i2c_init, .pfn_sensor_i2c_read = imx335_read_reg, .pfn_sensor_i2c_write = imx335_write_reg, .sensor_name = "imx335", .i2c_addr = 0x1A, .mode_num = 1, .mode = { { .width = 2592, .height = 1944, .fps = 30, .mipi_lane = 2, .bayer = BAYER_RGGB, .mipi_clk = 594, /* Mbps,按实际 PLL 计算 */ }, }, }; /* 注册函数,sample 里会调用 */ int imx335_register(void) { return HI_MPI_ISP_SensorRegCallBack(&g_imx335_reg); }

逻辑说明:pfn_sensor_init指向初始化序列,pfn_sensor_set_mode负责切换分辨率。mode数组里每一项描述一种输出模式,mipi_clk要和 sensor 实际输出的 MIPI 时钟一致,否则 VI 会报“MIPI 接收错误”。bayer格式也要和 ISP 配置一致,IMX335 常见是 RGGB,写错会导致颜色完全不对。

参数上,i2c_addr必须和硬件一致;mode_num如果写 1 但实际想切多种分辨率,后面调用set_mode会失败。注册成功后,sample 里通过SAMPLE_COMM_ISP_SensorInit就能找到这个 sensor。

3.4 在 sample 里跑通出图:最小验证命令

驱动编译进库后,用海思自带的 sample 验证是最快的方式。通常sample_vio或sample_venc会调用 sensor 初始化。你需要确认 sample 的配置文件里 sensor 类型选的是 imx335。

# 进入 sample 目录 cd Hi3559A_SDK/mpp/sample/vio # 修改 Makefile 或配置文件,确保 sensor 类型为 imx335 # 编译 make clean && make # 运行,注意可能需要 root 权限 ./sample_vio 0

如果一切正常,你会看到 VI 通道有帧输出,接上屏幕或通过 VENC 推流能看到画面。如果报错,常见的有:

  • sensor not found:注册失败或 I2C 不通。
  • MIPI rx error:lane 数、时钟或时序不对。
  • ISP init fail:bayer 格式或分辨率不匹配。

注意:sample 运行前要确保没有其他进程占用 I2C 或 MIPI 资源,否则会出现“设备忙”的错误。

4. 适配过程中最容易翻车的 5 个坑

4.1 现象:I2C 能读到 ID,但 VI 始终无中断

原因:MIPI lane 数配置和硬件不一致。IMX335 支持 2 lane 和 4 lane,但驱动里mipi_lane写的是 4,而开发板只接了 2 lane。MIPI 接收端检测不到完整的 lane 信号,自然不会产生中断。

解决:对照原理图确认实际 lane 数,把驱动结构体里的mipi_lane改成一致的值。同时检查 MIPI 时钟频率,2 lane 和 4 lane 的时钟计算方式不同,不能直接套用。

4.2 现象:出图但颜色严重偏绿或偏紫

原因:bayer 格式配错。IMX335 默认输出 RGGB,但 ISP 里如果配成 BGGR 或 GRBG,颜色就会完全错乱。另一种可能是白平衡没校准,但那种偏色通常比较轻微。

解决:先确认驱动里bayer字段和 ISP 配置一致。海思 ISP 通常有ISP_BAYER_RGGB等枚举,两边要对齐。如果格式确认无误,再检查是否加载了正确的 ISP 参数文件(isp_param)。

4.3 现象:全尺寸能出图,切到 1080p 就花屏

原因:裁剪窗口或 binning 配置没改。IMX335 在不同分辨率下,输出窗口和 PLL 可能不同。如果只改了驱动里的width/height,但初始化序列还是全尺寸的,sensor 实际输出的数据量和 VI 预期不一致,就会花屏。

解决:为每种分辨率准备独立的初始化序列,或者在set_image_mode里根据模式重新写关键寄存器。不要指望一套序列通吃所有分辨率。

4.4 现象:驱动编译通过,但 sample 运行时找不到 sensor

原因:注册函数没被调用,或者 Makefile 没把驱动编进库。海思 SDK 里 sensor 注册通常需要在sensor_register.c或类似文件里显式调用imx335_register()。如果只写了驱动文件但没注册,ISP 自然找不到。

解决:检查 SDK 的 sensor 注册列表,确认 imx335 被加进去了。同时用nm检查库文件里是否有imx335_register符号。

4.5 现象:I2C 读写偶尔失败,重启后又正常

原因:上电时序不稳定,或者 I2C 总线上有其他设备干扰。IMX335 对复位和时钟的时序有要求,如果复位释放太早或 MCLK 不稳定,I2C 可能间歇性无响应。

解决:用示波器抓一下复位、MCLK 和 I2C 波形,确认时序符合 datasheet。软件上可以在 I2C 操作前加延时,或者重试机制。如果总线上挂了多个设备,检查地址是否冲突。

5. 进阶:用 ISP 调试工具验证驱动质量与参数边界

驱动能出图只是第一步,真正决定画面质量的是 ISP 参数和 3A 算法。海思提供了 ISP 调试工具(常见的是hi_isp_debug或 PC 端的ISP_Calibration工具),可以通过网口或串口连接板端,实时查看和调整曝光、增益、白平衡、降噪等参数。

一个实用的验证方法是:在固定光源下,用 IMX335 采集标准色卡,然后通过 ISP 工具观察 RGB 曲线的偏差。如果驱动里的曝光和增益回调实现正确,3A 算法应该能自动收敛到合理值。如果画面一直过曝或过暗,说明ISP_SENSOR_REGISTER_S里的曝光能力描述不对,比如最大曝光行数、增益范围写错了。

另一个边界是帧率与带宽。Hi3559 的 VI 和 ISP 有带宽上限,如果你把 IMX335 配到全尺寸 30fps 同时开多路,可能会出现丢帧。这时候需要降低帧率或分辨率,或者检查 MIPI 时钟是否足够。我一般会在驱动里把每种模式的mipi_clk算准,然后用cat /proc/umap/mipi_rx查看实际接收状态,确认没有 CRC 错误。

最后说个血泪经验:IMX335 的寄存器手册里有些保留位,写错可能不会立即报错,但会在特定温度或电压下出问题。我习惯在初始化序列里只写 datasheet 明确要求的位,保留位保持默认,不随意发挥。调试时先用最小系统跑通,再逐步加功能,这样出了问题也容易定位。

希望帮到你。

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

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

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

立即咨询