
简介这份资源是针对联发科MT623611A平台HI253 CMOS传感器驱动的完整源码包面向嵌入式驱动开发者、手机摄像头调试人员及物联网视觉方案研究者。涵盖传感器驱动主实现、USB视频设备属性配置及驱动头文件三个模块以2个C源文件和1个头文件构成压缩包整体仅16KB结构精简便于快速查阅与集成。目前已有一百余人关注学习。通过阅读源码可理解HI253传感器在MTK平台下的寄存器初始化、图像采集流程、I2C/SPI接口交互方式以及USB视频类设备的参数属性处理逻辑适合具备一定嵌入式基础并希望移植或定制CMOS驱动的开发者参考。1. 这份 V1.2 的 HI253 sensor 驱动源码解决的是 MT6236 平台上一件具体的事把文件名拆开信息其实全在标题里2011 年 9 月 15 日归档MTK MT6236(11A) 平台海思 HI253 这颗 200 万像素 CMOS sensorJerry 维护的 V1.2 版本hi253sensordriver 源代码。在 2G 功能机走量的年代MT6236 是出货量很大的相机平台HI253 是常见的低端 sensor把两者接起来的工作就叫 sensor 驱动移植。它解决的从来不是拍照好不好看而是最底层的一件事主控能不能通过 I2C 找到这颗 sensor能不能让它在预览、拍照模式之间正常切换。适合的人群很窄但诉求明确老平台驱动维护者、模组厂 FAE、方案公司里被一张黑屏拖住进度的工程师。看到这个标题我第一反应不是代码量而是排错时那些像玄学一样的坑。2. MT6236(11A) 平台怎么把 HI253 挂进系统从 sensorlist 到 power_on 的框架2.1 sensor 探测链路的三个环节注册表、SensorInit 与 ID 匹配MT6236(11A) 这类功能机平台对 sensor 驱动的管理有一条固定的套路所有受支持的 sensor 都挂在一张注册表里。常见文件名叫 sensorlist.c也有叫 imgsensor_list.c 的不同 BSP 版本名字有差异但作用一样。表里是一串 sensor 描述结构包含驱动名、ID 值和驱动函数入口。系统启动后摄像头服务会遍历这张表逐个调用驱动的初始化函数和 ID 读取函数只要返回的 ID 和表里登记的一致这颗 sensor 就被判定为“在板”后续的预览、拍照流程才会继续走。/* sensorlist.c - 把 HI253 登记进平台的 sensor 注册表 */ #include hi253_Sensor.h static const struct imgsensor_list_t g_sensor_list[] { /* ... 其他 sensor 项例如 ov2640、gc0308 等 */ { hi253, /* 驱动名log 里能看到 */ HI253_SENSOR_ID, /* 这颗 sensor 的 ID 值 */ HI253_SENSOR_DRV_FUNC, /* 驱动结构体指针 */ }, };这段代码的逻辑就是“登记”。相机服务不关心你的 sensor 内部怎么工作它只按表里的名字和 ID 来找人。HI253_SENSOR_ID 这个宏定义在 hi253_Sensor.h 里值必须和模组规格书给的 ID 完全一致差一个 bit 都会导致探测失败。HI253_SENSOR_DRV_FUNC 指向驱动暴露出来的函数集老平台常见的做法是一个结构体里面挂着 init、preview、capture 等回调。如果编译完发现 log 里根本没人提到 hi253先回来查这张表有没有被编译进去而不是急着看驱动内部。/* hi253_Sensor.c - 初始化入口框架先调这里做 ID 探测 */ static kal_bool hi253_get_sensor_id(kal_uint32 *sensor_id) { kal_uint32 id 0; /* 读 HI253 的 ID 寄存器。寄存器地址和掩码必须来自模组规格书 不同批次可能不同黑盒时先打这串 log */ id (read_i2c_byte(0x00) 8) | read_i2c_byte(0x01); *sensor_id id; return KAL_TRUE; } kal_bool SensorInit(MSDK_SENSOR_INIT_STRUCT *pSensorInit) { kal_uint32 id 0; hi253_get_sensor_id(id); if (id ! HI253_SENSOR_ID) { /* ID 对不上不会直接报错只是这颗 sensor 被跳过 */ return KAL_FALSE; } pSensorInit-sensor_id id; pSensorInit-sensor_drv_name (char *)hi253; return KAL_TRUE; }这里最容易被新手误判的是返回值。SensorInit 返回 KAL_FALSE 时平台通常不会拉警报而是默默跳过这一项继续探测下一个 sensor。所以调试时要养成习惯先在 log 里搜 “mclk”、“camera” 或 sensor 驱动的名字确认框架到底有没有调到你的初始化函数。如果 log 里什么都没有问题往往在注册表或编译环节而不是在 I2C 通信上。ID 探测的另一层意义是区分模组同一个 HDI 板上可能既有 HI253 也有其他兼容 sensor靠 ID 在运行时动态决定启用哪套配置这是老平台非常标准的做法。2.2 上电与 I2C 时序为什么硬件波形比代码更值得先查sensor 能被探测到的前提是硬件时序先把电供上。MT6236 平台的 camera power 通常由 PMIC 或 GPIO 控制分 AVDD、DOVDD、DVDD 三个电源域上电顺序不能反MCLK 要等电压稳定之后才给然后还要等 sensor 内部完成复位和启动。我刚接触这类平台时遇到“检测不到 sensor”的第一反应是查 I2C 代码后来发现十个里有七个是电源时序问题示波器一量就穿帮。/* hi253_Sensor.c - 上电函数时序顺序是关键 */ static void hi253_power_on(void) { /* 步骤 1先开模拟电源和 IO 电源让 sensor 内部 LDO 稳定 */ pmic_set_avdd(2800000); /* AVDD 2.8V函数名以本 BSP 为准 */ pmic_set_dovdd(1800000); /* DOVDD 1.8V决定 I2C 上拉电平 */ /* 步骤 2给主时钟让 sensor 内部 PLL 起振。 不能和电源同时给否则 sensor 可能锁死在异常状态 */ mclk_enable(24000000); /* HI253 常见 24M按模组规格书确认 */ /* 步骤 3等 sensor 完全稳定。这个 delay 绝不能省 规格书里会写最小上电时间 */ delay_ms(10); }这段代码的参数说明里最有价值的是 delay_ms(10)。很多“预览黑屏但偶尔能出图”的故障就是 delay 太短sensor 还没准备好就被发了 I2C 配置流。另外注意 AVDD 和 DOVDD 的顺序通常模拟电源先上IO 电源后上因为 DOVDD 同时决定了 I2C 总线的上拉电平如果 IO 电源没稳定就发 I2C第一帧数据大概率是坏的。MCLK 的频率也容易被忽略HI253 用 24M 还是 26M 取决于模组设计平台晶振可能只有 26M这时要通过时钟分频配出 sensor 要的值配错了就会出现后面章节要讲的花屏问题。3. 把 hi253sensordriver 移植进工程三个必改文件与一套初始化序列3.1 文件放对位置编译入口才认你这颗 sensor一份 MT6236(11A) 的 sensor 驱动源码往下拆通常就是一对源文件hi253_Sensor.c 和 hi253_Sensor.h。头文件里放寄存器地址宏、ID 宏、驱动函数声明源文件里放 ID 探测、上电、初始化序列、预览和拍照模式切换。把它们拷贝到当前工程的 camera sensor 目录下路径在不同 BSP 里不一样有的在 custom/common/camera/src/有的在 hal/imgsensor/ 下以手头工程为准。放对文件之后还要确认编译系统真的把它编进去了否则一切白搭。# 编译配置示例把 HI253 驱动源文件加入 camera sensor 的编译列表 C_SOURCES \ ./custom/common/camera/src/hi253_Sensor.c \ ./custom/common/camera/src/hi253_Sensor.h这段配置的要点在于老平台的编译系统版本很多有些是手写 makefile有些是自动扫描目录并不是所有工程都需要手动加这一行。判断方法很简单编译一次在 log 里搜 hi253_Sensor如果能搜到编译命令就说明文件被纳入搜不到就检查路径和编译列表语法。另一个常见的做法是同时改 sensorlist 文件和编译列表两处不一致时以编译 log 为准。千万别漏了头文件的路径有的工程头文件也要单独加搜索路径漏了会报 “file not found”那种错误还好处理就怕头文件路径没加但编译器找到了同名的另一个版本那才叫踩坑。3.2 用寄存器表说话从规格书到 init/preview/capture 三张表HI253 这类 sensor 的驱动核心内容其实是几张寄存器表。模组规格书里会给一串“地址-值”的初始化序列驱动的工作就是把这些序列按模式组织起来一张 init 表用于开机和复位后的基础配置一张 preview 表用于预览模式一张 capture 表用于拍照模式。MTK 老平台的驱动风格非常直接就是把这些表定义成静态数组需要用哪个模式就逐条下发。/* hi253_Sensor.c - 寄存器表地址:值 对。 表中每一项要从模组规格书逐条抄录不要自己发明值 */ static const struct hi253_reg_setting hi253_init_settings[] { {0x12, 0x40}, /* 输出格式示例YUV 输出按规格书核对 */ {0x11, 0x01}, /* 时钟分频示例影响帧率和内部 PLL */ {0x09, 0x10}, /* 示例寄存器增益相关 */ {0xff, 0xff} /* 结束标志下发表时遇到它停止 */ }; static const struct hi253_reg_setting hi253_preview_settings[] { {0x12, 0x40}, {0x11, 0x02}, /* 预览模式的分频和拍照不同 */ {0x0d, 0x41}, {0xff, 0xff} }; static const struct hi253_reg_setting hi253_capture_settings[] { {0x12, 0x40}, {0x11, 0x00}, /* 拍照模式用最大分辨率输出 */ {0x0d, 0x51}, {0xff, 0xff} };下发的逻辑通常是一个循环逐条走 I2C 写寄存器遇到 0xff 结束就停。这里有个真实坑有些 sensor 的寄存器地址本身就包含 0xff写了某个值为 0xff 的寄存器结果被下发函数当成结束标志后面的配置全部丢失。标准做法是把结束标志改成表长度遍历或者约定一个不可能用到的哨兵值。另外init 表和 preview 表的关系要理清每次上电先下发 init再下发 preview从拍照切回预览时部分 sensor 要求先下发 init 再下发 preview不能直接切。驱动跑不稳、出现“第一次开机有图、第二次黑屏”这类问题先检查模式切换时表有没有发全。3.3 版本命名与维护习惯V1[1].2 和 Jerry 留下的是什么看到“V1[1].2”这个写法老工程师一眼就明白这是 Windows 文件系统把 “V1.2” 里的点处理成方括号后的结果本质上就是一个 V1.2 版本。Jerry 是维护者代号。这类带人名带版本号的源码在方案公司里再常见不过——一个工程师负责把新模组调试到能量产驱动在几个月里演进十几个版本每一版都对应一次模组改版或一个 bug 的修复。文件名里的版本号多乱都不如文件头里的注释清楚。/* hi253_Sensor.c * HI253 sensor driver for MTK MT6236(11A) * Maintainer : Jerry * * V1.0 2011-09-01: initial version for HI253 2M module * V1.1 2011-09-08: fix preview dark - modify power_on delay to 10ms * V1.2 2011-09-15: change I2C address bit for module rev.B */这段代码没有运行逻辑但它是最值钱的“元数据”。我自己的习惯是每次改动必须在这份注释里加一行哪怕只是改了一个延时。因为三个月后你会完全忘记当初为什么把 delay 从 5ms 改成 10ms半年后模组厂换批次预览暗了你翻到 V1.1 那行少走一夜的弯路。版本号的意义不在于数字而在于它把每一次修改的原因锚定下来。多模组项目里另一个常用手段是用宏区分模组版本例如#define HI253_MODULE_REV_B把同一颗 sensor 不同批次差异包在条件编译里而不是复制整份文件改名。4. HI253 驱动必调参数表I2C 地址、Sensor ID、MCLK 与电源域4.1 从黑匣子到点亮ID 读取函数怎么写、失败时看哪里ID 探测是驱动点亮的第一道关卡也是新手最容易对着黑匣子瞎猜的地方。实际调试中我一般会在 ID 读取函数里加一行临时 log把读到的原始值直接打出来再和预期值比对。很多工程师先怀疑 I2C再怀疑代码其实看一眼打印的原始 ID问题就定位了如果打出来是 0xFFFF 或 0x00基本是总线没通如果值稳定但不是预期值可能是寄存器地址或掩码问题如果值一会儿对一会儿不对大概率是上电时序在捣乱。/* hi253_Sensor.c - 加强版 ID 读取带原始值打印 */ static kal_bool hi253_get_sensor_id(kal_uint32 *sensor_id) { kal_uint32 id 0; /* 示例HI253 常见 ID 存放在 0x00-0x01 两个连续寄存器 高字节在前你的模组可能不同以规格书为准 */ id read_i2c_byte(0x00) 8; id | read_i2c_byte(0x01); /* 临时调试把原始值打出来别用 log 工具过滤掉 */ mtk_log([HI253] raw sensor id 0x%04x, expect 0x%04x, id, HI253_SENSOR_ID); *sensor_id id; return KAL_TRUE; }这段代码的逻辑说明就一句话先确认总线通不通再谈配置对不对。打印原始值这个习惯能帮你把“黑匣子”拆开一个口子。参数说明里有两个重点一个是 I2C 读函数在平台里通常有超时机制如果 SDA 一直被拉低或没有 ACK返回值会是全 0xFF别把这当成 sensor 的 ID另一个是 ID 寄存器可能不止两个字节有的 sensor 用三个字节甚至带版本位掩码要按规格书来否则把版本号也算进 ID永远匹配不上。4.2 参数速查表与参数错误的典型症状驱动能点亮之后剩下的大多数问题都集中在一组固定参数上。下面这张表是我做 HI253 这类 sensor 时必查的清单典型值不是标准答案模组规格书才是最终依据。参数常见典型值出错时的症状Sensor ID 寄存器地址0x00-0x01 示例探测不到 sensor或被当成另一颗型号I2C 从机地址常见 8bit 0x40 / 7bit 0x20写读全部失败ID 读出 0xFFFFMCLK 频率24M 或 26M按模组花屏、彩条、无时钟时全黑AVDD 电压2.8V 常见预览偏暗、噪点多、四角发红DOVDD 电压1.8V 常见I2C 电平异常随机读写失败DVDD 电压1.2V 或 1.8V 视模组上电后 sensor 不启动电流异常I2C 速率100k 或 400k 常见调太快导致偶发读写失败、图像撕裂这张表的使用方法是逐行核对而不是只改你怀疑的那一项。AVDD 偏低时很多 sensor 不是完全不工作而是图像暗、颜色偏这种问题最容易让人去调 ISP 而不是查电源I2C 速率偏高时预览可能出现随机花行单独看又不像总线错误。我碰到过一台样机预览偶尔上半屏花查了三天最后是 DVDD 用错电压sensor 内部逻辑偶尔锁死。所以这一项的教训是参数表要整体过一遍别凭感觉选一个就调。4.3 驱动点亮只是开始后面还有 PD tuning 这只坑驱动能出图和拍照好看是两个维度。MTK 平台在 sensor 驱动跑通之后画质调整那一步是单独的流程平台自带一套 PD tuning 的文档和工具专门调亮度、对比度、饱和度、降噪这些 ISP 层面的东西。很多刚接触这个领域的工程师会被“驱动不就是点个亮吗”带偏实际项目中驱动只负责让 sensor 正确输出 YUV 或 RAW 数据后续每个亮度档位、每个预览分辨率的画质都要在 tuning 环节里打磨。所以如果你拿到这份 hi253sensordriver 源码第一目标应该是“点亮并稳定出图”而不是追求画质。画质问题比如偏红、偏暗、噪点重先检查电源和寄存器表里的增益、曝光相关项确定驱动侧没有问题了再打开平台的 PD tuning 文档按流程调 ISP 参数。把画质问题全部归咎于驱动里加几条寄存器是这条路线上最容易走偏的地方。驱动侧是确定性逻辑tuning 侧是经验参数两者混在一起调试会变成灾难。5. HI253 驱动排错避坑5 个常见的翻车现场与血泪解决5.1 预览全黑但拍照偶尔出图先量 power_on 时序现象开机进预览屏幕全黑但偶尔按拍照能出一张全黑的图或者过很久才出现一帧。原因power_on 函数里 delay 太短MCLK 还没有稳定输出sensor 内部 PLL 没锁住I2C 配置流就发出去了。解决用示波器同时量 AVDD、DOVDD、MCLK 三路确认 MCLK 稳定起振后再数上电时间把 delay_ms(10) 改到 20ms 甚至 30ms 再试。注意很多平台的 MCLK 是从主芯片输出的log 里说“MCLK enabled”不代表波形的幅值和频率已经好必须量。5.2 花屏/彩条MCLK 频率与像素时钟没配对现象预览画面是彩色条纹完全看不出主体或者横向撕裂。原因MCLK 频率和 sensor 内部 PLL 分频不匹配输出的像素时钟超出了平台接收范围。解决核对规格书里 MCLK 的范围确认 24M 还是 26M再检查平台侧时钟配置寄存器MT6236 这类平台的 camera 时钟源分频系数要单独设。另一个快速验证手段是切到小分辨率预览640x480 正常而 1600x1200 花屏说明是带宽和时钟问题而不是 sensor 配置问题反过来小分辨率花屏大分辨率正常就要怀疑预览表和 capture 表互相污染了。5.3 检测不到 sensorI2C 地址 bit 与 ID 掩码的坑现象log 里看不到 HI253 的 ID 打印或者打印出 0xFFFF。原因I2C 地址的 7bit 和 8bit 表示法搞混。很多 sensor 规格书写的是 8bit 地址 0x40驱动代码里传的却是 7bit 0x20或者反过来另外 ID 寄存器高位掩码没做把版本位也算进去了。解决先用平台自带或自写的 I2C 扫描工具从 0x00 到 0x7F 逐个地址读一遍确认 sensor 到底响应在哪个地址再把读到的原始 ID 和规格书逐 bit 对必要时把掩码打印出来。还有一个没想到的原因I2C 上拉电阻没贴或贴错阻值也会让 ID 读出全 F这个用万用表量板子就能发现。5.4 MTK 平台无法连接设备别急着怀疑驱动先回下载工具现象代码编了一大堆准备下载固件时工具报错设备死活连不上很多人第一反应是“是不是我改驱动把系统搞挂了”。原因大多数时候和驱动代码无关是串口或 USB 枚举环节的问题比如端口被占用、驱动没装好、设备没进入下载模式。解决回到最基础的 MTK 下载工具把设备重新插拔、手动进入下载模式、检查端口管理器里有没有正确枚举。我自己也干过蠢事改完 sensorlist 编译下载失败急得在代码里翻半天最后发现是笔记本电脑的 USB 口供电不足。这个环节的教训是先固定变量确认工具链路和硬件链路没问题再怀疑代码。为此我一般手边放一份下载工具加串口工具的合装很多人管这叫 mtk 工具箱它在排查这类问题时比 log 更有用。5.5 拍照后回预览花屏模式切换时寄存器没复位现象第一次预览正常按拍照快门能出图但回到预览后画面花屏再次预览黑屏重启又好。原因capture 模式下的寄存器表和 preview 表差异较大从拍照切回预览时驱动直接下发 preview 表而 sensor 内部还停在拍照模式的状态部分寄存器被锁定。解决在切换流程里先下发 init 表让 sensor 回到已知状态再下发 preview 表两表之间留足够延时。/* hi253_Sensor.c - 从拍照切回预览的推荐流程 */ static void hi253_capture_to_preview(void) { /* 先软复位 sensor回到已知状态。 0x12 是示例寄存器复位位以规格书为准 */ write_reg(0x12, 0x00); delay_ms(5); /* 再下发 init 表最后下发 preview 表。 别跳步否则部分寄存器处于 capture 状态 */ apply_setting(hi253_init_settings); apply_setting(hi253_preview_settings); }这段代码的逻辑是“先复位再重新建场”。apply_setting 函数内部就是循环写寄存器表前面加一个软复位代价是几毫秒换来的是切换稳定性。参数说明里要注意软复位的寄存器不是所有 sensor 都有没有的话就跳过这一步直接连续下发 init 和 preview 表但两表之间不要共用静态缓冲区有的工程师复用同一块缓存preview 表把 init 表还没写完的数据覆盖了这种 bug 最隐蔽。6. 让 V1.2 真正落地怎么验证这版驱动没有玄学成分驱动点亮之后我习惯做三个验证动作全过了才敢说这版可以交到产线。第一个是白墙和灰阶卡测试预览画面没有固定条纹、没有四角暗角、颜色没有明显的偏绿偏红拍照模式连拍十张每张的亮度和色彩一致性要好不能出现第一张正常第二张偏暗的情况。第二个是模式切换压力测试写一个循环脚本每三秒做一次 preview 到 capture 再回 preview连续跑二十分钟记录是否有复现的死机、花屏、黑屏把次数和 log 对应上。第三个是读回关键寄存器把初始化序列里改动过的几个关键寄存器读出来比对确认写入的确实生效了这一步能挡住很多“我看代码没问题”的错觉。这三个动作里第三个最容易被跳过也最值得做。你自己改了哪些寄存器用一个小函数把它们统一读回来打印和期望值逐项核对比人眼盯着屏幕靠谱得多。这个习惯的内核是把玄学排除掉让每一次改动都有明确的验证结果。至于 PD tuning 这边的画质打磨那是在驱动稳定之后再进入的流程V1.2 这份驱动能做到模式切换稳定、出图不花不黑、ID 探测可靠就已经完成了它在项目里的使命。在我经手的老平台项目里驱动从来不追求漂亮追求的是确定性。把每一次改动的寄存器 diff、延时调整都写进文件头的 changelog版本号才有意义不然三个月后你自己也分不清 V1.2 和 V1.1 到底差在哪。希望这篇笔记能帮你少走一点这种弯路让手头的 HI253 早点稳定出图。本文还有配套的精品资源点击获取