
MTK平台搞sensor开发说难不难说简单也真不简单。我接手过的项目从入门级到旗舰级都有说实话MTK这套东西最大的特点就是流程固定但细节极多很多时候你以为搞定了驱动就能交付结果工厂校准、功耗、防误触、产测老化层出不穷。这篇就把我从驱动到HAL、从调试到量产积累的东西捋一遍全部是实际项目里用得上的技能点不是那种概念科普。1. 先从整体架构说起一条sensor数据要经过几道关卡很多新人拿到MTK平台第一件事就是翻代码结果一头扎进kernel/drivers/misc/mediatek/sensor里头越看越晕。我建议先顺着数据流走一遍知道sensor数据从物理世界到应用层的完整路径后面定位问题会快很多。MTK sensor的数据链路大致是物理sensor芯片 - I2C/SPI总线 - kernel驱动注册iio或input设备 - sensor hub在某些平台是独立协处理器在非独立平台是kernel里的hwbinder服务 - Android的sensors HAL层 - sensorservice - 上层应用。这里有个MTK特色要注意老一点的平台比如Android 9以前的很多项目依赖SensorHub这颗协处理器做数据融合和低功耗处理很多sensor不是直接挂在AP上的而是挂在hub上由hub通过spi/i2c和AP通信。新平台更多走qupv3或者geni这类通用接口但底层逻辑没变。MTK和Qualcomm在sensor这层最大的差异也在这里——高通的sensor驱动框架更统一几乎全是msm_sensor加iio的思路MTK则是各平台各有各的改法从mediatek/mt6765到mt6983同一份驱动代码经常要根据平台宏做适配。所以做MTK sensor第一课就是接受碎片化别指望一份代码通吃所有平台。实际操作中我拿到一个新平台一般先干三件事确认硬件接的是哪条I2C总线供电是LDO还是直接PMIC输出中断脚是哪个GPIO。在dws或dts里把I2C、GPIO、供电的配置对上。先写一个最简的“读ID”驱动确认I2C通信正常再往下扩展。这三步走完这台机器的sensor通路基本就通了一半剩下的都是业务逻辑和调参问题。2. 内核态驱动开发dws/dts配置与I2C通信的几个决定性细节MTK的硬件配置有两种风格老项目用DWS工具生成的dws文件编译时转成code新项目直接用设备树dts/dtsi。这个切换坑了不少人因为很多老工程师习惯了改dws新项目里找不到入口。我的经验是先问清楚平台用的是哪一种再动手改。2.1 I2C拓扑别忽略地址冲突和总线频率Sensor基本都是I2C从设备7位地址一般在0x18到0x77之间。MTK平台的I2C总线编号和dts里的i2c0到i2c7对齐但要注意有些平台会把sensor挂在sspm或scp管理的一路虚拟I2C上这路I2C的寄存器基址和普通I2C不一样写驱动时要用专用的mtk_i2c接口而不是标准i2c_transfer。总线频率这块我的建议是加速度计和陀螺仪这类高频数据sensor用400kHz磁力计和部分光感用100kHz就够了有些光线传感器对时序敏感高了反而容易读错数据。这个不是越.快越好之前踩过一回把光感总线上到400k结果数据偶发跳变降回100k就稳了。另一个容易出问题的是同一总线上多设备地址碰撞。我遇到过一款气压计和一颗磁力计共享总线磁力计地址0x0C气压计地址0x0C两个驱动都去probe结果总线上地址冲突一个起来了另一个必然挂。解决办法是让硬件改地址pin电平或者在dts里通过status节点控制加载顺序但根本解法还是硬件错开地址。2.2 GPIO中断与唤醒模式加速度计和接近光感这类设备一般都会有中断引脚。MTK平台配置中断有两个注意点一是dts里的interrupt-parent要指向pio二是中断触发类型尽量设IRQ_TYPE_LEVEL_LOW或IRQ_TYPE_LEVEL_HIGH不要用边沿触发因为sensor中断大多是电平型边沿触发容易丢中断。还有如果sensor需要支持系统休眠唤醒需要注册irq_set_irq_wake或者在驱动的suspend/resume里做处理。MTK平台有个比较坑的地方是某些Android版本里sensor中断唤醒需要额外调用mt_eint_registration这类老接口新平台已经改成标准request_threaded_irq了。如果发现中断进不来可以先搜一下平台代码里sensor相关的eint注册方式确认是不是有封装接口。2.3 供电与复位时序sensor的供电一般有vdd1.8V或2.8V、vio1.8V I/O电源。MTK dts里通过vdd-supply和vio-supply引用PMIC的regulator。有一个常见错误是只配了vdd没配vio结果I2C通信正常但寄存器读出来全0xff或者是芯片ID能读到但数据不更新。复位时序这块有些sensor需要上电后等20ms再访问I2C有些需要拉低复位脚再释放。我在驱动probe里一般会加一个msleep(20)再读ID稳妥一些。如果sensor有独立的复位GPIO建议在probe开始阶段做一次硬件复位确保芯片处于确定状态。3. Sensor HAL层从hwmodule到sensors_event_t的完整链路MTK平台的Sensor HAL路径一般在hardware/mediatek/sensor或vendor/mediatek/proprietary/android/hardware/sensor。这一层是把kernel上报的event转换成Android标准的sensors_event_t数据结构的关键。3.1 HAL的挂载机制MTK的HAL有一个hwmodule的概念每个sensor在HAL里对应一个struct sensor_t描述信息名称、厂商、type、maxRange、resolution、power等然后通过一个load_calibration是否支持、enable/disable是否能暂停等回调注册进sensorservice。新手容易搞混的是sensor_t里的type字段决定上层看到的是加速度计还是陀螺仪而不是靠名字。type写错了就算名字叫accel上层fling手势也能乱掉。之前一个项目加速计和线性加速度的type搞反了结果屏幕旋转正常但计步器不工作排查了半天发现是HAL里type映射错误。3.2 batch与功耗一个常被忽略的调优点Android从4.4开始就有batch机制到P之后全面要求sensor支持flush和batch。MTK HAL里sensor的batch实现最终会换算成kernel驱动的采样率和最大延迟。实际项目里功耗问题多半出在这上层应用以SENSOR_DELAY_FASTEST(0ms)申请加速度计数据HAL如果没做限制底层就以最大采样率一直上报功耗直接拉满。这个场景我在计步应用上遇到过解决方案是在HAL层做一次事件过滤或降频——当应用申请高频率时底层仍然保持合理频率HAL通过复制或插值满足应用需求但这要求HAL实现足够贴近原厂参考设计。3.3 prox与als的特殊处理接近传感器(proximity)和光线传感器(light)在HAL层有两个MTK特色的处理prox的防误触策略。通话时屏幕需要熄灭但如果prox只靠中断有时候异常场景下中断没触发屏幕一直亮着。MTK HAL里通常会有一个proximity的enable去联动lcm亮度调节或者上层通话状态。prox数据必须做平滑或迟滞避免临界值抖动导致屏幕闪烁。als的lux计算。光感sensor的原生数据是ADC值要转换成lux得用dts里的coef或HAL里的als_calibration系数。这个系数每个项目都得实际测——用照度计在标准光源下标定不能挪用其他项目的值。特别是OLED屏幕和LCD屏幕对光感的响应差异很大用LCD的系数套OLED自动亮度会明显偏暗。3.4 sensor校准数据的管理磁力计和陀螺仪这类需要出厂校准的sensor校准数据一般存在/mnt/vendor/protect或/data/misc/sensor这类分区。MTK平台的校准逻辑通常在工厂模式下触发校准后的参数通过ioctl或者HAL内部接口写入sensor驱动或保存在文件里。有一个常见的坑量产后发现校准数据丢往往是文件权限或分区挂载时机的问题。比如sensor HAL起来的时候/data还没解密挂载写进去的数据最终丢失。这个在早期Android版本上特别常见解决办法是把校准文件的读写放在on late-start阶段之后或者干脆存到persist分区。4. 工厂模式与产测校准这些场景决定了你能不能顺利量产搞sensor的人很大一部分时间其实是在配合工厂做产测。MTK平台有一套factory模式sensor测试工具在工程模式下可以单测每颗sensor。作为驱动工程师必须确保在工厂测试界面里能正确读到sensor数据。4.1 硬件单板测试板测阶段经常出现的问题是sensor器件的I2C通信不稳定测试界面读到的数据是固定值或0。这个不一定是驱动问题很多时候是焊接不良或者供电纹波太大。我在产线上排查过一颗磁力计15%的板子读数异常最后发现是磁力计的供电脚虚焊贴片炉温曲线导致锡膏没完全融化。如果软件层面实在无法区分是芯片异常还是总线异常可以做一个寄存器回读测试写一个寄存器回读校验。如果写读不一致基本可以判定是芯片或总线硬件问题。有的项目把这套逻辑做成产测工具的一部分非常实用。4.2 校准数据写入与校验流程MTK平台的产测流程中sensor部分的校准一般包括加速度计的offset校准通过静态六面放置保证三个轴分别对准±1g陀螺仪的bias静态校准静止时读取bias并保存磁力计的软硬磁校准画8字或旋转台校准后的数据会写入一个统一的校准分区。驱动里需要提供对应的ioctl命令让工厂工具可以写入和读取校准数据。这里有个细节校准数据的校验和很关键。如果校准数据格式定义了CRC或checksum即使数据被异常擦除驱动也能发现并回退到默认值避免用脏数据导致sensor完全不准。4.3 异常场景ERENCE档位切换和温度漂移产线和用户场景差异很大有些结构件带磁性的夹具会导致磁力计校准偏差很大。我遇到过工厂夹具用了含磁性的螺丝导致每台机器的磁力计校准都失败换非磁性螺丝后问题消失。所以如果磁力计校准良率突然下降建议先建议硬件产线检查一下夹具和周边环境磁场。另外一个容易忽略的是温度漂移。陀螺仪的zero-rate offset随温度变化很明显有的项目在HAL里实现了多温度点校准把不同温度下的bias都存在校准分区运行时根据实时温度插值。这个技术MTK平台上的参考代码里有但默认不一定开启需要按项目需求适配。5. 功耗与性能优化MTK平台sensor待机电流和CPU唤醒的博弈功耗是sensor开发的永恒话题。一块手机待机电流从3mA涨到10mA排查多半天最后发现是sensor不休眠这种事我遇到不少于三次。5.1 内核态sensor的suspend/resume策略在MTK平台上sensor驱动一定要实现suspend/resume或类似机制。很多sensor支持自己的低功耗模式特别是加速度计支持fifo加中断芯片级让CPU处于idle状态。实现suspend时通常会把sensor切换到低功耗模式关闭不必要的中断但保留用于唤醒的sensor中断。排查方案如果待机电流异常偏大先把/sys/bus/i2c/devices/下所有sensor的enable节点全部关掉看电流是否下降。如果还是大再看CPU的idle状态是否被中断频率锁死可以用cat /proc/interrupts看sensor相关中断次数如果几十秒内增长几百次那就是中断异常多半是sensor在睡眠后被误触发。5.2 非唤醒sensor和唤醒sensor的区别在Android体系里sensor分为wake-up和non-wake-up两类。加速度计一般是非唤醒的AP睡眠时不要求它工作而proximity和significant motion这类通常是唤醒的。在MTK HAL的sensor_t里用flags字段标识是否WAKE_UP型。这个配置错了影响很大如果把加速度计配置成唤醒型AP睡着之后加速度计每次数据上报都会唤醒CPU待机电流直接爆炸。反过来如果把proximity配成非唤醒型通话时屏幕熄灭功能就废了来电话放耳边屏幕还亮着脸一贴就乱触。所以flags这个字段一定要按sensor类型来不能想当然。5.3 data path的带宽优化磁力计加速度计陀螺仪同时以100Hz上报每秒就是300个事件每个事件sizeof(sensors_event_t)大概80字节每秒24KB的数据量。看起来不大但如果上层有一个应用在用这些数据做算法再加上CPU唤醒和IPC损耗功耗就会上去了。优化思路是让HAL做batch合并把数据攒到buffer共同上报减少AP唤醒次数。MTK的HAL参考代码里已经有batch的支持默认的batch参数直接能用但如果自己改过HAL建议用batch的timeout参数做一次事件聚合验证。6. 调试三板斧log、节点、对照实验定位sensor问题我个人的调试工具箱基本就三样log、debugfs/sysfs节点、对照实验。只要这三样用好90%的问题都能在一个小时内收敛。6.1 log的分层打点打log时要分层内核态pr_debug或pr_info在I2C读写、中断触发、suspend/resume这几个关键点打点。开发早期直接用pr_info上线前再改成pr_debug和dynamic_debug避免量产机器的log刷屏。HAL层在sensors_open、sensors_activate、sensors_poll以及校准数据读取这几个函数里加log。上层验证用getevent或dumpsys sensorservice来验证。dumpsys sensorservice这个命令是Android自带的能看到所有sensor的当前状态、连接的client数量、事件队列长度。如果sensor数据不上报先看这里有没有client申请数据如果没有就是上层的问题别在底层浪费时间。6.2 sysfs节点让数据开口说话在驱动的sysfs里创建一个read_data节点每次cat这个节点就主动触发一次sensor读取并打印原始数据。这个方法在硬件调试阶段极其有用不用依赖上层Sensor管理框架直接看底层有没有有效数据输出。另一个有用的节点是enable手动echo 1或0控制sensor开关排查HAL层的enable调用是否正常下发到驱动。这类节点建议在驱动目录下创建比如/sys/devices/platform/xxxx-i2c/i2c-0/0-0048/read_data即使系统上层的sensorservice挂了也能通过它确认硬件和驱动是否正常。产线维修时维修人员也很依赖这个节点做单板诊断。6.3 对照实验隔离变量遇到sensor数据诡异比如加速度计静止时读数不是1g先不要急着改滤波算法先做对照实验拿一颗确定正常的sensor板子放在同样的环境下对比读数。如果两颗读数都偏离1g问题可能在供电或硬件layout而不是驱动。如果好板子正常、坏板子异常那基本锁定在硬件或校准上。温度类问题同样适用对照实验怀疑温度漂移可以拿热风枪局部加热传感器区域同时观察原始数据的变化曲线。如果数据呈线性漂移那基本确定是温度影响需要做温度补偿或者检查硬件结构是否把发热源放在了sensor附近。7. 我踩过的几个经典坑每个都值得长记性这一节纯粹是经验清单不按章节展开直接记录我实际项目中遇到过的几个最有代表性的sensor问题。坑一I2C通信正常但数据全为0x00。现象加速度计ID能读对但数据寄存器读出来全是0。排查发现是sensor没退出power-down模式部分型号的sensor上电后默认是低功耗模式需要往控制寄存器写特定值才能切到正常测量模式。这类问题看datasheet就能避免但新手很容易忽略。坑二proximity偶尔误触发导致通话中屏幕乱闪。排查过程中断触发正常但阈值的迟滞回差太小。接近sensor的原始值在阈值附近抖动导致频繁切换远近状态。最终把HAL的远/近阈值拉开比如远阈值调到近阈值的1.5倍误触发几乎消失。坑三屏幕自动亮度忽明忽暗。现象als上报的lux在50到80之间反复跳。原因是背后有脏污或玻璃透光不均匀导致sensor不同角度读到的值差异大。处理方式是加长als事件上报的平滑窗口以及提高环境光稳定的判断条件。还有一种可操作方案是sensor前面板做遮光泡棉让进光角度更均匀。坑四工厂校准后陀螺仪温度漂移严重。现象校准完常温静止时bias是0.03°/s但手机发烫后涨到0.5°/s。查下来是这个陀螺仪对温度本来就有较大offset漂移而校准流程只校了常温点。解决方案是增加温度补偿表和高温校准标定效果改善明显。坑五electron里调用sensor API空白小众但真实。如果你们的产品形态是类似嵌入式浏览器的客户端调用web sensor API但又拿不到数据大概率是Android WebView还没拿到sensor权限或者系统级没有授予网页权限。这类问题通常和驱动无关但在项目集成时是真实的工作内容列入排查范围。8. 从驱动到产测的完整工作流清单最后我整理一份我每接手一个MTK sensor项目时用的工作流按这个顺序走基本不会漏东西。硬件确认确认I2C地址、中断引脚、供电配置、reset引脚是否接好用万用表确认硬件通路。dws/dts配置配好I2C总线、GPIO中断、供电regulator编译烧录。最小驱动验证驱动里先做probe和读ID确认软件通路OK。数据上报验证创建sysfs节点或临时开一个dev/input事件确认数据不报错、更新频率正常。HAL集成在HAL添加sensor描述确认sensorservice能看到并正常使能。校准验证跑通工厂校准流程确认校准数据写入和读取都在正确分区。功耗验证待机电流测试确认sensor在AP睡眠时不搞事。稳定性验证长时间跑压力测试确认无异常中断、无数据卡死。产测配合给产线输出测试步骤和判定标准确认产线良率和不良品诊断逻辑。量产问题跟踪收集量产后的异常case特别是温度、磁场等环境类问题。这个流程看着简单但每一步都可能在细节上卡住一两天。尤其是dws/dts配置和HAL集成这两块平台差异大参考文档又喜欢藏得深多花点时间把基础打牢后面会顺畅很多。MTK平台sensor开发的经验很难用一篇文章讲完毕竟平台版本、芯片型号、Android版本的差异非常大。但基本方法论是通的先把数据链路摸清再逐层调试最后做好校准和产测。按这个思路走下来你也能少走很多弯路。