
简介面向海思hi35xx平台的索尼IMX307/IMX377传感器驱动源码包适合嵌入式驱动开发者以及安防监控、物联网、智能硬件等需要图像采集的项目参考。这套源码包聚焦索尼IMX307的Exmor R背照式CMOS传感器解决其与海思视频监控SoC平台适配时的驱动开发问题。压缩包整体约54KB仅含4个文件两个C源文件、一个头文件和一个Makefile。其中imx307_cmos.c负责传感器初始化、分辨率与帧率配置、曝光控制等主控逻辑imx307_sensor_ctl.c处理I2C通信、电源管理等底层控制时序imx307_cmos_ex.h则定义数据结构与外部接口Makefile用于编译生成驱动模块。目前已有829人学习下载对于这类精简的驱动源码包而言具有一定的社区认可度可作实战参考。开发者通读源码可知IMX307的寄存器映射、配置流程、接口协议等关键知识点进而在hi35xx平台快速移植或二次开发。包内文件结构清晰便于对照海思SDK文档定位修改点也能辅助理解从传感器到SoC的图像数据通路与控制链路。1. 拿到“sony_imx307_hi35xx”工程名时你要面对的第一块硬骨头用海思hi35xx做IPC或智能相机最绕不开的一步就是点亮sensor。很多工程师第一次拿到SDK解压后看到sony_imx307_sonyimx377_SONYIMX307_hi35xx_COm307这种名字第一反应是“这不就是个驱动目录吗”实际上这串字符背后对应的是一颗Sony IMX307 sensor要接到海思平台先过I2C、再配MIPI、写寄存器表、接ISP、调AE最后还要让WDR、Smart这些功能在正确的位置生效。这篇笔记围绕这个工程名把sensor接入的完整链路、参数设置和踩坑记录讲清楚。适合正在做海思方案移植、sensor驱动适配或者被IMX307/IMX377出图问题卡住的嵌入式工程师。2. 海思hi35xx上的sensor驱动到底是谁在编译谁先看清那串工程名的结构2.1 “sony_imx307_sonyimx377_…”不是乱码是厂商、型号、主控平台的拼接规则海思的MPPMedia Process PlatformSDK里sensor驱动有一套约定俗成的命名方式前缀是sensor厂商名中间是sensor型号后缀是主控芯片平台。比如sony_imx307_hi35xx就是“Sony IMX307接到hi35xx平台上”的意思。为什么工程名里会同时出现imx307和imx377常见的情况是项目在评估阶段同时测两颗sensor或者同一个模组厂默认提供两种sensor选项于是把两颗sensor的驱动放在同一个工程目录里通过chip id来区分。这不是随便起的名字而是sensor驱动在SDK里的身份标识编译时它决定生成哪个libsns_xxx.so烧录时它决定系统启动后在VI层挂载哪一颗sensor。我一般拿到一个陌生的sensor驱动工程第一件事不是看代码而是先看目录结构。典型的结构是mpp/sample/sensor_drv/ ├── sony_imx307_hi35xx │ ├── sensor_cmos.c # sensor驱动主文件里面是寄存器表和回调 │ ├── sensor_cmos.h │ ├── ext_ctl.c # 曝光、增益、WDR等扩展控制 │ ├── ext_ctl.h │ └── Makefile └── sony_imx377_hi35xx └── ...sensor_cmos.c里最关键的两个东西一个是static CMOS_SENSOR_CTRL_S g_stCmosSensorCtrl这个结构体描述sensor的物理参数分辨率、像素格式、帧率、镜像翻转方向另一个是stCmosSensorRegs这是寄存器初始化序列。你后续改分辨率、改帧率、开WDR改的都是这两个区域。2.2 把libsns_xxx编进SDK从sample驱动到VI/ISP的注册链路海思平台不是让应用直接去操作sensor而是通过VIVideo Input模块统一管理。sensor驱动在系统里的角色是向VI提供一颗“可用的sensor”的描述并注册几个回调函数让海思的ISP、AE模块在运行时能调你的代码去写寄存器、读寄存器、设置曝光、设置增益。编译方式各家SDK略有差异常见做法是进入sensor驱动目录用SDK提供的交叉编译链直接编出.so然后手动替换到文件系统里。我给一个典型编译命令# 在sensor驱动目录下执行假设SDK_ROOT是海思SDK解压路径 cd $SDK_ROOT/mpp/sample/sensor_drv/sony_imx307_hi35xx make clean make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- SDK_ROOT$SDK_ROOT编译完成后会生成libsns_imx307.so再放到根文件系统的/lib/modules或/usr/lib下。海思的sample在启动时会通过SENSOR_TYPE枚举去匹配驱动库如果枚举类型和库对不上系统会报“sensor type mismatch”的日志画面就是黑的。再看注册链路。sensor驱动的核心入口是CMOS_GetSnsCtrl和CMOS_GetSnsRegInfo它们被VI层在初始化时调用把CMOS_SENSOR_CTRL_S结构体内容拷贝到MPP内部。常见的注册结构体长这样/* sensor_cmos.c - 向海思MPP注册sensor控制接口 */ static CMOS_SENSOR_CTRL_S g_stCmosSensorCtrl { .pstSnsMbus stMbusInfo, /* 原始RAW输出格式 */ .u8ImgType IMG_TYPE_LINEAR, /* 线性模式HDR时为IMG_TYPE_HDR */ .u8SnsMode SNS_MODE_1920x1080p_30fps, /* 枚举模式 */ .unSnsLaneInfo.as08LaneId[0] 0, /* 回调注册VI层通过下面三个函数操作sensor */ .pfnSnsIfProbe sensor_probe, .pfnSnsIfInit sensor_init, .pfnSnsIfExit sensor_exit, };注意.u8ImgType这个字段IMX307默认是线性模式如果你的项目要做海思的多帧WDR这里要改成HDR同时VI侧也要同步配置否则整条链路状态不一致画面会以非常诡异的方式“翻车”。我经历过一次sensor侧配了HDR、VI侧没开结果画面出现半个脸亮半个脸暗的分屏现象查了大半天才定位到是这个字段不一致。3. 让IMX307按我们想要的方式出图初始化寄存器、I2C与MIPI的最小适配流程3.1 先让I2C读通上电、复位、地址扫描与chip id确认IMX307是Sony的2MP COMS1/2.8英寸1080p60fps是常见工况。它的控制接口是I2C——数据手册里叫CCBCamera Control Bus寄存器是16bit地址、8bit数据。拿到新板子第一步不是去查ISP配置而是用I2C把chip id读出来。常见做法是编译海思的i2c-read/write工具或者在sample里加一段最小I2C读代码。驱动里每个I2C读写动作都很重要我贴一段自己一直在用的读寄存器函数作为起点/* sensor_i2c.c - 读sensor的16bit寄存器返回值0表示成功 */ static int sensor_i2c_read(struct i2c_client *client, unsigned short reg, unsigned char *val) { int ret; unsigned char buf[2] {0}; buf[0] reg 8; /* 高字节在前Sony传感器默认大端 */ buf[1] reg 0xff; struct i2c_msg msgs[2] { { .addr client-addr, .flags 0, .buf buf, .len 2 }, { .addr client-addr, .flags I2C_M_RD, .buf val, .len 1 }, }; ret i2c_transfer(client-adapter, msgs, 2); if (ret 0) { dev_err(client-dev, i2c read failed: reg%04x ret%d\n, reg, ret); return ret; } return 0; }这段代码的逻辑很简单第一个I2C消息写寄存器地址第二个消息读回1字节数据。之所以要关注返回值是因为海思I2C总线偶发异常如果驱动里不判断返回值寄存器表写到一半断掉sensor就会停留在一个不确定状态出图后表现为“一会儿正常一会儿花屏”。拿到I2C读写函数后接着做三件事确认sensor的I2C地址、确认上电时序、读chip id。IMX307的I2C地址不是唯一的通常由sensor的引脚配置决定常见是0x34也有0x1A的版本。我习惯先在原理图上确认地址再用工具扫描。3.2 寄存器表和MIPI参数从数据手册到海思VI配置的落盘sensor的点亮流程中有个常见误区以为I2C读通、chip id读对了sensor就能出图。实际上chip id只代表I2C通信正常离图像输出还差着一整张寄存器表。Sony的sensor寄存器表有几百行其中决定能否出图的包括PLL配置决定输出时钟、图像裁剪决定输出分辨率、MIPI lane分配决定物理链路、输出模式RAW10还是RAW12。驱动里一般会把寄存器表做成数组初始化时逐条写入/* sensor_init.c - IMX307寄存器初始化序列写入函数 */ static int sensor_init_regs(struct i2c_client *client) { int i, ret; static const struct regval_list regs[] { /* 先配PLL再配输出最后配MIPI */ {0x0100, 0x00}, /* 休眠模式此时寄存器可写 */ {0x0301, 0x06}, /* PLL倍频 */ {0x0303, 0x01}, {0x0305, 0x02}, {0x0340, 0x02}, /* 垂直帧尺寸 */ {0x0342, 0x0E}, /* 水平线尺寸 */ {0x0344, 0x00}, /* 裁剪起始X */ {0x0345, 0x00}, {0x0346, 0x00}, /* 裁剪起始Y */ {0x0347, 0x00}, {0x0348, 0x07}, /* 裁剪结束X 1920 */ {0x0349, 0x7F}, {0x034A, 0x04}, /* 裁剪结束Y 1080 */ {0x034B, 0x37}, {0x0100, 0x01}, /* 退出休眠 */ }; for (i 0; i ARRAY_SIZE(regs); i) { ret sensor_i2c_write(client, regs[i].addr, regs[i].val); if (ret 0) { dev_err(client-dev, init fail at reg[%04x]\n, regs[i].addr); return ret; } } return 0; }寄存器表里几个必须核对的参数0x0340是垂直帧尺寸决定帧率的基准0x0342是水平线尺寸0x0344到0x034B是有效输出窗口。这些数值一定要和你的目标分辨率对应如果裁剪结束坐标超出sensor的有效像素区输出画面边缘会出现黑边或者错位。MIPI参数这边海思VI侧需要配置lane数和data rate。计算方法很直接RAW10格式下带宽 宽度 × 高度 × 帧率 × 10bit / lane数。以1080p30为例按4 lane算每个lane约需 1080p30830Mbps左右。实际配置时我会留20%余量把data rate调在1Gbps附近。如果MIPI速度配得太低sensor往DDR里灌数据会丢包画面出现横纹这个现象和寄存器表无关很容易被误判成“寄存器写错”。3.3 用sample拉起第一帧彩色画面时要核对哪几个参数海思的sample程序比如sample_venc或sample_smart IPC工程里对应的sensor初始化接口通过SAMPLE_COMM_VI_StartSensor拉起sensor。不是随便调一下就能出画面要逐个核对下面这几项第一sensor枚举类型。海思VI层用枚举值区分sensor型号比如SNS_TYPE_IMX307在sample_comm.h里定义。如果你驱动里写的枚举值和sample调用时传的枚举值不一致VI层log会显示“invalid sensor type”初始化直接失败。第二MIPI接口配置。海思VI输入的MIPI_LANE_NUM要和sensor输出的lane数一致。IMX307常见有2lane和4lane两种输出配置如果你的硬件PCB走的是4 lane但驱动寄存器表里sensor只开了2lane画面会只有一半数据花屏偏色非常明显。第三ISP处理流程。sensor输出的是RAW Bayer数据海思ISP需要做坏点校正、去噪、彩噪抑制再转RGB输出。如果sample里SAMPLE_COMM_VI_SetParam没有正确配置RAW类型画面上会出现绿色或紫色的整体色偏。这种情况看起来像白平衡问题实际上是你告诉ISP的Bayer排列方式和sensor实际输出不一致。4. 曝光、WDR、Smart到底在sensor驱动里管什么别再被这三个词绕晕4.1 exposure attr积分时间和增益的边界最容易锁不住帧率海思的AE模块属于ISP的3A之一它不直接写sensor寄存器而是通过HI_MPI_ISP_SetExPAttr下发曝光目标再由sensor驱动里的扩展控制函数把“目标曝光时间目标增益”换算成寄存器值。这个换算关系是整个AE环路里最容易被写错的地方。先看sensor侧的两个关键物理概念积分时间exposure time和模拟增益analog gain。IMX307的积分时间通过寄存器写入一个行数实际曝光时间 行数 × 一行的时间。一行的时间又由PLL配置和水平尺寸决定。所以换算函数要同时知道帧率和行周期/* ext_ctl.c - 把AE下发的曝光时间转成sensor行数寄存器值 */ static int sensor_set_exposure(unsigned int exposure_us, unsigned int gain_db) { unsigned int lines; unsigned int max_lines; unsigned long long tmp; /* 计算可用最大行数一帧总行数减去垂直消隐 */ max_lines g_stSnsAttr.u16TotalLines - g_stSnsAttr.u16VBlankLines; /* 曝光时间转行数 */ tmp (unsigned long long)exposure_us * g_stSnsAttr.u32LinePeriodUs; lines (unsigned int)(tmp / 1000); if (lines max_lines) { /* 关键超上限要截断否则sensor寄存器写超范围会导致画面闪烁 */ lines max_lines; } /* 写曝光寄存器 */ sensor_i2c_write(g_client, 0x0202, (lines 8) 0xff); sensor_i2c_write(g_client, 0x0203, lines 0xff); return 0; }注意lines max_lines这个截断逻辑。很多工程师在这里不判断溢出AE一次给到30ms曝光但当前帧周期只有20ms寄存器硬写进去之后sensor实际行为是不可预测的——常见结果是画面亮度周期性波动帧率也跌到三四帧看起来像是“sensor死了”实际是曝光范围没限制住。gain的映射也容易出问题。AE下发的gain单位是db但sensor寄存器里一般放的是线性倍率中间要过gain 10^(db/20)换算。IMX307的增益范围从0dB到约30dB超出范围的部分只能依赖ISP数字增益补救。如果sensor驱动把增益上限写得太小画面在暗光下会一直偏暗AE积压在暗部拉不起来。4.2 WDR多帧曝光拼接sensor、VI和ISP三层都要改海思方案的WDR分两种sensor内WDRDOL模式等通常需要sensor硬件支持和frame WDR多帧合成。IMX307这颗sensor本身不带DOL所以海思平台上的IMX307 WDR常见做法是frame WDR同一帧图像时间内sensor分别用短曝光和长曝光输出两帧由ISP合成出高动态范围图像。这个功能在sensor驱动里要做三件事第一实现短帧曝光控制接口即在原有曝光基础上再写一组更短的曝光寄存器第二把控制结构体的u8ImgType改成IMG_TYPE_HDR第三在VI侧的硬件配置里开启HDR拼接模式。三者同步到位WDR才生效。实际操作中最容易漏的是第二和第三的联动。sensor驱动改好了VI侧没开ISP拿到的还是线性RAW数据出来的画面不会报错只是动态范围没有提升效果“跟没开一样”。还有一种情况是VI侧开了HDR但sensor侧曝光寄存器没配合动静转换处出现拖影感运动物体边缘有鬼影调试难度比线性模式高一个量级。4.3 Smart智能编码和ROI其实是VPSS/编码侧的事别在sensor里找和exposure attr、wdr不同smart在海思体系里基本与sensor驱动无关。海思的“Smart”通常指智能编码包括ROI区域增强、背景帧率控制、智能码率控制等这些功能作用在VPSS和VENC阶段是编码器层面的策略不是sensor输出层面的东西。但热词里为什么总把“exposure attr、wdr、smart”并列因为海思sample的结构体SAMPLE_SMART_ATTR_S把编码参数放在一起新手顺着sample代码往下追容易误以为smart也是sensor驱动里要配置的。实际上调smart只需要动HI_MPI_VENC_SetRoiAttr或HI_MPI_VPSS_SetSmartAttrsensor驱动里没有任何smart开关。所以在这里给出一个明确的边界exposure attr管的是sensor的积分时间和增益作用在AE与sensor驱动之间wdr管的是多帧曝光的组合方式作用在sensor、VI、ISP三层之间smart是编码策略作用在VPSS和VENC之间。三个环节的配置目标完全不同如果项目里发现“开了smart后画面变暗”那不是smart的问题往往是同时开的WDR参数没有调到合适的长短帧比例。5. 海思IMX307适配避坑五条值得写进笔记的踩坑记录5.1 现象I2C总是NACKchip id读不出来新板子第一次用I2C工具读IMX307返回一直是NACKcat /proc/device-tree的信息里也看不到设备节点。最初我怀疑sensor地址写错反复改了七八个地址都无效。原因最终定位在sensor的上电时序。IMX307的上电时序要求先给供电、再拉时钟、最后释放复位信号。如果复位引脚是由GPIO控制的而GPIO在系统启动早期还没初始化sensor就一直处于复位状态I2C总线自然没应答。另一个容易忽略的点是I2C电平IMX307的CCB接口电平是1.8V如果海思那头I2C上拉电阻接到3.3V也会因为电平不匹配导致通信不稳定。解决方式是检查原理图确认供电和复位时序确保系统进入应用层之前把复位GPIO正确拉高。如果在驱动里控制复位建议放在sensor_probe最开头。5.2 现象出图了但花屏偏色颜色像彩虹I2C初始化正常sample能拉起通道但画面出现严重色偏竖条纹或者红色/绿色全屏。这个现象排查起来最耗时间因为问题可能出在MIPI lane分配、ISP的Bayer排列、或者sensor的镜像翻转配置三处之一。按排查顺序先用最简单的方法判断把sensor分辨率降到最低如果花屏变轻通常是MIPI data rate不足或lane数不匹配如果画面固定单一色偏优先怀疑ISP的Bayer排列顺序如果画面有左右镜像错位感再查sensor镜像寄存器。我的一般做法是在海思的serial调试口抓一幅RAW dump用RAW工具直接看Bayer排列比对着画面猜快很多。最后定位到的问题通常是sensor寄存器表里镜像翻转寄存器和VI层的MIPI lane配置不一致导致。5.3 现象AE指令下去曝光量半天不动运行sample后画面亮度不跟随场景变化对着灯箱看画面始终一个亮度或者曝光在某个值附近来回抖动无法稳定。用串口打印AE日志发现有报ISP AE overflow或exposure data abnormal。原因主要有两类一类是sensor驱动里曝光换算函数写错AE目标值到寄存器之间没有正确对应常见是忘记把曝光时间乘上行周期另一类是曝光上限配得太小AE被截断在某个固定区域内。解决时先用海思的ISP工具关闭AE手动往sensor驱动里写一组已知曝光值观察画面亮度是否线性变化。如果手动写也无效重点查曝光寄存器地址是否正确——IMX307的曝光寄存器是0x0202/0x0203不同批次的sensor驱动的寄存器地址可能不一致不要直接搬别的项目。5.4 现象切WDR模式后画面暗下去一截线性模式下画面正常一旦把u8ImgType改成HDR整体亮度明显下降暗部细节也丢了看起来像是曝光没有覆盖到暗场景。这种问题九成出在长短帧曝光配比。海思frame WDR工作时短帧负责亮部细节长帧负责暗部细节ISP拼出来的最终亮度取决于两者的融合权重。如果长帧曝光被限制到很短的范围内暗部亮度自然不足。解决步骤是先把短帧曝光关掉只用长帧曝光看是否恢复正常亮度如果正常再去AE长帧配置里找原因。另一个常见原因是VI侧HDR threshold阈值配置过高导致拼接时大部分区域采了短帧。这个参数通常不是sensor驱动负责的而是ISP侧的HI_MPI_ISP_SetWDRAttr里调。5.5 现象固件烧录完插上镜头就是黑屏代码编译过、I2C配置看起来也都正确但固件烧录进设备后启动日志里找不到sensor相关输出画面始终黑屏。这通常不是代码问题而是固件打包环节漏掉了sensor驱动库。海思方案里编译好的libsns_imx307.so必须被正确打到根文件系统或指定分区里。常见做法是先用SDK的解包打包工具把固件解开检查/lib/modules或/usr/lib下有没有这个文件没有就重新打包烧录。烧录工具一般用官方提供的烧录软件比如设备端烧录工具按分区烧写注意烧录后要确认分区挂载正确、文件权限可读。以后遇到“所有代码都对但设备上不生效”的情况先查根文件系统不要先怀疑驱动。6. 双sensor共存的进阶玩法从IMX307到IMX377的切换与验证工程名里同时出现IMX307和IMX377最常见的使用场景是同一块板子可以选配两种sensor通过软件识别自动加载对应配置。这玩法的核心让sensor驱动的probe函数根据chip id自动选择正确的寄存器表和曝光映射参数。/* sensor_probe.c - 根据chip id选择不同的sensor配置 */ static int sensor_probe(struct i2c_client *client) { unsigned char id_high, id_low; unsigned int chip_id; sensor_i2c_read(client, 0x0000, id_high); /* 厂商ID */ sensor_i2c_read(client, 0x0001, id_low); /* 型号ID */ chip_id (id_high 8) | id_low; if (chip_id IMX307_CHIP_ID) { g_current_profile g_imx307_profile; g_stCmosSensorCtrl.pstSnsMbus stMbus_imx307; } else if (chip_id IMX377_CHIP_ID) { g_current_profile g_imx377_profile; g_stCmosSensorCtrl.pstSnsMbus stMbus_imx377; } else { dev_err(client-dev, unsupported chip id: 0x%04x\n, chip_id); return -ENODEV; } return 0; }这段代码在I2C通信完成后立即执行通过chip id区分两颗sensor再选择不同的参数集。这里要注意IMX307和IMX377的寄存器映射差异IMX377是更高像素的sensor分辨率模式数量更多array大小也不同直接套用307的寄存器表一定会出错。在驱动层维护好两个独立的profile结构体是双sensor支持比较清晰的落地方式。验证是否切换正确我习惯在串口启动日志里打印一行当前选择的sensor型号。比对着画面判断更可靠——因为看画面很难分辨一颗sensor是否跑在最优配置上但日志可以直接告诉我们当前挂载的是谁。这行日志成本很低但在换模组、换整机测试时能省掉很多排查时间。另外切sensor后一定要做一次完整的raw图抓取确认输出分辨率和Bayer排列与ISP配置一致防止“能出图但效果不对”的半成品状态流入后续调画质环节。从IMX307到IMX377再到更多Sony sensor的适配路径都差不多先读chip id确认身份再选对寄存器表最后验证VI/ISP链路参数对齐。我在后面做过的所有sensor适配项目都保留了一个习惯拿到sensor驱动的第一步先改chip id校验和I2C地址点亮出图之后再谈画质画质这种“玄学”问题需要在干净的图像链路上逐步排查而不是在动了一堆参数之后才去调AE。希望这篇笔记能帮你少走几步弯路。本文还有配套的精品资源点击获取