☰
hi3559平台IMX335驱动适配与调试实战指南
2026/10/7 18:53:05 网站建设 项目流程

简介:这份资源是面向嵌入式Linux驱动开发者的Sony IMX335传感器驱动适配代码,针对海思Hi3559高性能视频处理平台定制,解决IMX335在Hi3559系统上无法正常初始化、取流与图像处理的问题。包内共6个文件,以C源码与头文件为核心,包含imx335_cmos.c、imx335_sensor_ctl.c及对应头文件,另有Makefile构建脚本和两个编译生成的.o目标文件,压缩包约362KB,结构精简,便于直接集成到V4L2驱动框架中编译验证。驱动已针对Hi3559的接口协议、时序控制与电源管理完成适配调试,功能验证无误,可帮助读者理解CMOS传感器驱动的设备初始化、数据传输与错误处理流程,快速完成图像采集链路的搭建与排错。目前已有1072人学习下载,适合从事安防监控、无人机或工业相机开发的中高级工程师参考借鉴。

1. 拿到 sony_imx335 driver 适配 hi3559 的源码包,先别急着 make

如果你正在调 hi3559 平台上的 IMX335 摄像头,大概率经历过这样的场景:sensor 上电了,I2C 能读到 ID,但 /dev/video0 死活出不了图,dmesg 里一堆 i2c timeout 或者 mipi 报错。这时候你需要的不是从头撸一个驱动,而是一份已经在 hi3559 上跑通过的参考实现。手上这个 sony_imx335 hi3559.zip 就是干这个的——它包含 imx335_cmos.c、imx335_sensor_ctl.c、imx335_cmos_ex.h 以及对应的 Makefile,是海思 MPP 框架下 sensor 驱动的标准骨架,编译产物是 imx335_cmos.o 和 imx335_sensor_ctl.o。适合谁用?正在 hi3559 系列(hi3559A / hi3559C / hi3559V200)上做多路摄像头接入的嵌入式工程师,尤其是需要把 IMX335 这颗 500 万像素 sensor 跑起来、但又不想从 sample 代码一行行改的人。这篇笔记就按我实际拆包、编译、上板、出图的顺序,把关键参数和踩过的坑一次讲清。

2. 拆开源码包:hi3559 sensor 驱动的文件分工与调用链

2.1 三个核心文件各自管什么

海思平台的 sensor 驱动不像 Linux 主线那样走 V4L2 完整框架,而是 MPP(Media Process Platform)自己的一套注册机制。你拿到的这几个文件,分工非常明确:

  • imx335_cmos.c:驱动主体。负责 sensor 的注册、初始化、分辨率切换、帧率配置、镜像翻转等。里面会实现一个sensor_obj结构体,挂到 MPP 的 sensor 链表上。
  • imx335_sensor_ctl.c:底层寄存器操作。I2C 读写、上电时序、复位、时钟使能都在这。它不关心上层业务,只负责把寄存器值写进去、读出来。
  • imx335_cmos_ex.h:对外暴露的宏和结构体定义。比如IMX335_I2C_ADDR、IMX335_WIDTH、IMX335_HEIGHT、IMX335_MIPI_ATTR这些,都在这里改。
  • Makefile:交叉编译脚本。指定工具链、内核头文件路径、输出 .o 文件。

调用链是这样的:MPP 启动时通过sensor_register_callback把 imx335 的sensor_obj注册进去,然后 ISP 初始化时调用sensor_init,sensor_init再调imx335_sensor_ctl.c里的 I2C 写序列完成上电和寄存器配置。出图失败时,你要顺着这条链一段段查。

2.2 先确认你的 SDK 版本和 sensor 类型宏

在动任何代码之前,先做一件事:确认你用的 hi3559 SDK 版本。不同版本的 MPP 接口有差异,尤其是ISP_SNS_OBJ_S结构体的成员。常见做法是去 SDK 的mpp/sample/common/下看sample_comm_isp.c里怎么注册 sensor 的,对照你的 imx335_cmos.c 是否匹配。

然后检查imx335_cmos_ex.h里的 sensor 类型宏。hi3559 SDK 通常要求你在sensor_type里选一个,比如:

// imx335_cmos_ex.h 中常见的宏定义 #define IMX335_I2C_ADDR 0x34 // 7bit 地址,实际写寄存器时左移一位 #define IMX335_WIDTH 2592 // 有效像素宽 #define IMX335_HEIGHT 1944 // 有效像素高 #define IMX335_MIPI_LANE 4 // MIPI 通道数,hi3559 常用 4 lane #define IMX335_MIPI_CLK 891 // MIPI 时钟频率,单位 Mbps,按实际晶振算 #define IMX335_FRAME_RATE 30 // 默认帧率

这几个值必须和你的硬件一致。I2C 地址错了,连 ID 都读不到;MIPI lane 数错了,出图花屏或者直接没图;MIPI 时钟错了,图像会撕裂或者 ISP 报错。我一般会先拿示波器量一下 MIPI 时钟引脚,确认实际频率,再反推寄存器配置。

2.3 Makefile 里的交叉编译参数怎么改

Makefile 是这份资源里最容易被忽略、但最容易翻车的地方。它通常长这样:

# 交叉编译工具链,按你的实际路径改 CROSS_COMPILE = arm-himix200-linux- CC = $(CROSS_COMPILE)gcc # 内核头文件路径,必须指向你当前使用的 SDK 内核 KERNEL_DIR = /home/xxx/hi3559_sdk/osdrv/linux-4.9.y # MPP 头文件路径 MPP_DIR = /home/xxx/hi3559_sdk/mpp # 编译选项 CFLAGS = -Wall -O2 -I$(KERNEL_DIR)/include -I$(MPP_DIR)/include # 目标文件 obj-m += imx335_cmos.o obj-m += imx335_sensor_ctl.o all: make -C $(KERNEL_DIR) M=$(PWD) modules clean: make -C $(KERNEL_DIR) M=$(PWD) clean

这里有几个参数必须改:CROSS_COMPILE要换成你 SDK 里的工具链前缀,KERNEL_DIR要指向你实际编译内核时用的源码目录,MPP_DIR要指向 MPP 的 include 目录。改完执行make,如果报xxx.h: No such file or directory,八成是路径没指对。编译成功后会生成imx335_cmos.ko和imx335_sensor_ctl.ko,用insmod加载。

提示:有些 SDK 版本要求把 sensor 驱动直接编进内核,而不是做成 .ko。这时候你需要把 .c 文件放到内核的drivers/media/platform/下,改 Kconfig 和 Makefile。具体看你的 SDK 文档。

3. 上板调试:I2C 通信、MIPI 配置与出图验证

3.1 I2C 地址确认与读写测试

驱动加载后第一件事是确认 I2C 能通。IMX335 的 7bit 地址通常是 0x34,但有些模组会通过 SID 引脚改成 0x35 或 0x36。上板后先跑:

# 查看 i2c 总线上的设备 i2cdetect -y 0 # 读 IMX335 的 Chip ID 寄存器,地址 0x3000 和 0x3001 i2cget -y 0 0x34 0x3000 w

正常应该读到0x33 0x54(IMX335 的 Chip ID)。如果读不到,先查硬件:SID 引脚电平、上拉电阻、电源是否正常。软件层面查imx335_sensor_ctl.c里的imx335_i2c_init函数,确认总线号、地址、速率配置对不对。常见错误是总线号写死成 0,但实际接在 1 上。

3.2 MIPI 参数配置与 ISP 对接

I2C 通了之后,下一步是 MIPI 和 ISP。hi3559 的 MPP 里,sensor 的 MIPI 属性通过combo_dev_attr配置。在imx335_cmos.c的sensor_init里,你会看到类似这样的代码:

// 配置 MIPI 属性 combo_dev_attr_t mipi_attr; mipi_attr.input_mode = INPUT_MODE_MIPI; mipi_attr.data_rate = MIPI_DATA_RATE_X1; mipi_attr.img_rect.width = IMX335_WIDTH; mipi_attr.img_rect.height = IMX335_HEIGHT; mipi_attr.mipi_attr.lane_id[0] = 0; mipi_attr.mipi_attr.lane_id[1] = 1; mipi_attr.mipi_attr.lane_id[2] = 2; mipi_attr.mipi_attr.lane_id[3] = -1; // 4 lane 时最后一 lane 不用 mipi_attr.mipi_attr.output_rect.x = 0; mipi_attr.mipi_attr.output_rect.y = 0; mipi_attr.mipi_attr.output_rect.width = IMX335_WIDTH; mipi_attr.mipi_attr.output_rect.height = IMX335_HEIGHT; mipi_attr.mipi_attr.output_rect.w = IMX335_WIDTH; mipi_attr.mipi_attr.output_rect.h = IMX335_HEIGHT;

这里lane_id的顺序必须和硬件走线一致。我遇到过一块板子,lane 顺序接反了,结果图像上半部分正常、下半部分绿屏。后来把lane_id[0]和lane_id[1]对调才解决。另外data_rate要选对,IMX335 在 4 lane 下通常用 X1,2 lane 下用 X2,选错了 ISP 会报mipi error。

3.3 出图验证:从 /dev/video0 抓一帧

驱动加载、ISP 初始化完成后,用 sample 里的sample_venc或者sample_vio跑一遍。如果一切正常,你会看到类似这样的日志:

[ISP] sensor imx335 init success [ISP] mipi lane 0-3 locked [VPSS] chn 0 start success

然后抓一帧图:

# 用 sample_vio 抓图,输出到 /tmp/out.yuv ./sample_vio 0 1 # 把 YUV 转成 JPG 看效果 ffmpeg -f rawvideo -pix_fmt nv12 -s 2592x1944 -i /tmp/out.yuv -frames:v 1 /tmp/out.jpg

如果图是黑的,先查曝光和增益寄存器;如果图是绿的,查 MIPI lane 顺序和 ISP 的 Bayer 格式配置;如果图有横条纹,查 MIPI 时钟频率和电源纹波。这些在imx335_sensor_ctl.c的寄存器序列里都能找到对应的调整点。

4. 避坑记录:IMX335 在 hi3559 上最容易翻车的五个点

4.1 现象:i2cdetect 能扫到地址,但读 Chip ID 返回 0xFF

原因:I2C 地址是 7bit,但i2cget默认按 8bit 处理,或者 sensor 的 SID 引脚电平不对。IMX335 的地址由 SID 引脚决定,悬空、接地、接 VCC 对应三个不同地址。

解决:先用万用表量 SID 引脚电平,确认实际地址。然后在imx335_sensor_ctl.c里把IMX335_I2C_ADDR改成对应的 7bit 地址。注意海思的 I2C 读写函数通常要求传入 8bit 地址(7bit 左移一位),别搞混。

4.2 现象:驱动加载成功,但 ISP 初始化时报 "sensor not found"

原因:MPP 的 sensor 注册链表里没有你的 imx335,或者注册的 sensor 名字和sensor_type不匹配。hi3559 SDK 里,sensor_register_callback注册时用的名字必须和sample_comm_isp.c里sensor_type枚举一致。

解决:检查imx335_cmos.c里sensor_obj.sensor_name的值,确保和 SDK 里定义的一致。常见做法是直接搜 SDK 里SENSOR_TYPE_IMX335这个宏,看它对应的字符串是什么,然后改成一样的。

4.3 现象:出图花屏,上半部分正常下半部分绿

原因:MIPI lane 顺序和硬件走线不一致。hi3559 的 MIPI 接收器对 lane 顺序敏感,尤其是 4 lane 模式下,lane0 和 lane1 对调会导致数据错位。

解决:在imx335_cmos.c的 MIPI 配置里调整lane_id数组的顺序。如果硬件是 lane0→lane2、lane1→lane3 这种交叉走线,软件里也要对应改。我一般会先按 0,1,2,3 试一遍,不行再试 1,0,3,2,最多试四种组合就能找到对的。

4.4 现象:图像有横条纹或撕裂

原因:MIPI 时钟频率和 sensor 实际输出不匹配,或者电源纹波太大。IMX335 在 4 lane 下 MIPI 时钟通常是 891Mbps,但有些模组因为晶振精度问题会偏。

解决:先用示波器量 MIPI 时钟引脚的实际频率,然后调整imx335_cmos_ex.h里的IMX335_MIPI_CLK值。如果频率没问题,查电源——IMX335 对 AVDD 和 DVDD 的纹波很敏感,建议在电源引脚附近加 10uF 和 0.1uF 电容。

4.5 现象:编译报错 "implicit declaration of function 'sensor_register_callback'"

原因:Makefile 里的 MPP 头文件路径没指对,或者 SDK 版本不匹配。不同版本的 hi3559 SDK,sensor_register_callback的声明位置可能不同。

解决:在 SDK 里搜sensor_register_callback的定义,找到它所在的头文件,然后把该头文件所在目录加到 Makefile 的CFLAGS里。如果 SDK 版本太老没有这个函数,那就得用老的注册方式,具体看 SDK 里的 sample 代码。

5. 进阶技巧:用 sensor_ctl 做寄存器级调试与多路复用

5.1 直接读写寄存器定位问题

imx335_sensor_ctl.c里的 I2C 读写函数不只是初始化时用,调试阶段也可以直接调。我习惯在驱动里加一个 debug 节点,通过ioctl或者proc文件系统暴露寄存器读写接口。比如:

// 在 imx335_sensor_ctl.c 里加一个简单的 debug 函数 int imx335_read_reg(unsigned int reg_addr, unsigned int *value) { unsigned int buf[2]; buf[0] = reg_addr; // 调用海思的 I2C 读接口,具体函数名看 SDK if (hi_i2c_read(IMX335_I2C_ADDR, buf, 2, value, 1) != 0) { printk("imx335 read reg 0x%x failed\n", reg_addr); return -1; } return 0; }

然后通过insmod参数或者proc文件触发。这样当图像异常时,你可以直接读曝光、增益、帧率相关的寄存器,看值对不对。比如读0x3A00(曝光高字节)和0x3A01(曝光低字节),确认自动曝光有没有生效。

5.2 多路 IMX335 复用时的注意事项

hi3559 支持多路 sensor 同时接入,但 IMX335 多路复用时有两个坑:一是 I2C 地址冲突,二是 MIPI lane 资源分配。I2C 地址冲突可以通过 SID 引脚解决,但 MIPI lane 是硬件固定的,不能两路共用同一组 lane。常见做法是:

路数I2C 地址MIPI lane备注
路 10x34lane 0-3主摄
路 20x35lane 4-7副摄,需确认 hi3559 是否支持
路 30x36lane 8-11部分型号支持

如果你的 hi3559 型号不支持这么多 lane,那就只能分时复用,或者降低分辨率用 2 lane 模式。具体看芯片手册的 MIPI 通道数。

5.3 验证驱动稳定性的一个习惯

从那以后我每次调完 sensor 驱动,都会强制走一遍 24 小时烤机:连续抓图、切换分辨率、开关流,看有没有内存泄漏或者 I2C 超时。具体命令是:

# 循环抓图 1000 次,每次间隔 1 秒 for i in $(seq 1 1000); do ./sample_vio 0 1 sleep 1 done # 同时监控 dmesg 里的错误 dmesg -w | grep -i "error\|timeout\|fail"

如果 1000 次里有一次失败,就说明驱动有稳定性问题,通常是 I2C 锁没释放或者 MIPI 中断没清。这时候回去查imx335_sensor_ctl.c里的错误处理分支,看有没有漏掉mutex_unlock或者free_irq。

希望帮到你。

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

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

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

立即咨询