1. 初识MTK Sensor驱动调试AP与SCP的分工大家好我是老张在智能硬件和驱动开发这行摸爬滚打了十几年经手的MTK平台项目少说也有几十个。今天想和大家聊聊一个非常具体、但又让很多新手工程师头疼的话题MTK Sensor驱动的调试。如果你刚接触MTK平台听到“AP侧”、“SCP侧”、“Sensor 1.0”、“Tinysys”这些词可能一头雾水别急我会用最直白的方式给你讲明白。简单来说MTK平台处理传感器数据就像一家公司有两个核心部门。APApplication Processor是主芯片好比公司的“大脑”和“决策中心”性能强大负责运行安卓系统和大部分复杂应用。而SCPSensor Control Processor是一个独立的协处理器你可以把它理解为一个专门处理传感器和音频等低功耗任务的“特种小分队”。这个小分队独立于主系统运行最大的好处就是省电。比如手机息屏时计步、拾起亮屏这些功能就可以交给SCP来处理AP这个大CPU可以安心睡觉从而大幅提升续航。所以一个传感器比如加速度计、光感、距离传感器到底是挂在AP侧还是SCP侧取决于它的功能特性和功耗要求。通常需要实时、高频处理或者与复杂应用强相关的传感器会放在AP侧而那些需要持续监听、对功耗极其敏感的传感器则会放在SCP侧。调试的第一步就是和你的硬件同事或原厂确认清楚这颗Sensor到底归谁管这个定不下来后面所有工作都可能白费。我见过不少项目前期没沟通清楚代码都调通了才发现传感器放错了边导致功耗不达标或者响应延迟不得不推倒重来非常折腾。所以拿到原理图和芯片规格书后先别急着敲代码花十分钟把架构理清楚能省下后面好几天的功夫。2. AP侧Sensor驱动调试从零到一的点亮之旅当确定传感器挂在AP侧后我们的调试工作就正式开始了。这个过程就像拼装一个精密模型步骤环环相扣一步错可能就点不亮。下面我结合自己踩过的坑把每一步掰开揉碎了讲。2.1 环境准备与驱动代码获取首先你得有一个可以编译的MTK平台代码环境。MTK的内核版本迭代很快我当年调试时用的是Kernel-4.19和Sensor HAL 1.0的框架虽然现在可能有更新版本但核心思想和流程是相通的。关键是要和你手上的代码版本对应上千万别拿A版本的驱动往B版本的框架里硬塞那基本是编译不过的。驱动代码一般由传感器原厂比如Bosch、ST、AMS提供。找原厂要驱动时信息一定要给全MTK主芯片型号如MT6771、MT6877、内核版本、Sensor HAL版本。原厂给的通常是一个压缩包里面包含.c、.h源文件有时还会有一份简单的集成说明文档。我建议你先快速浏览一遍代码结构重点关注probe函数、i2c通信函数和电源管理部分这能帮你对这颗传感器有个初步了解。2.2 核心步骤详解DTS、Kconfig与编译拿到驱动代码后我们按顺序来操作。这里我以调试一个光线ALS和接近PS传感器为例。第一步配置设备树DTS这是硬件连接在软件上的“地图”绝对不能错。根据原理图找到传感器连接的I2C总线编号比如i2c2、设备地址如0x38、以及用到的GPIO比如中断引脚、复位引脚。然后在对应的DTS文件路径通常是kernel-4.19/arch/arm64/boot/dts/mediatek/下你项目的.dts文件中添加节点。i2c2 { status okay; clock-frequency 400000; alsps38 { compatible vendor,alsps; // 务必与原厂驱动中的compatible字符串一致 reg 0x38; interrupt-parent pio; interrupts 6 IRQ_TYPE_EDGE_FALLING 6 0; // 根据实际GPIO修改 irq-gpios pio 6 0; reset-gpios pio 7 0; // 其他供应商特定参数 vendor,param1 1; }; };这里最容易出问题的是interrupts和gpio的配置一个数字填错中断就无法触发。我建议用cat /proc/interrupts命令在系统启动后验证中断是否成功注册。第二步放置驱动代码并配置编译选项将原厂提供的驱动文件例如vendor_alsps.c放到内核驱动目录下比如kernel-4.19/drivers/misc/mediatek/sensors-1.0/alsps/。然后在这个新目录下创建两个关键文件Kconfig用于在make menuconfig时显示配置选项。config MTK_ALSPS_VENDOR bool Vendor ALSPS Sensor default n help Say Y here if you have this sensor.Makefile告诉编译器如何编译你的代码。obj-$(CONFIG_MTK_ALSPS_VENDOR) vendor_alsps.o第三步启用内核配置光把代码放进去还不够你得告诉内核“嘿请把我这个模块编译进去。” 你需要修改你项目对应的defconfig文件路径如kernel-4.19/arch/arm64/configs/xxx_defconfig增加两行CONFIG_CUSTOM_KERNEL_ALSPSy CONFIG_MTK_ALSPS_VENDORy第一行是启用MTK平台ALSPS类型的传感器框架支持第二行才是启用你具体的传感器驱动。很多新手只加了第二行结果编译时发现驱动根本没被编译就是因为漏了第一行这个“总开关”。第四步修改ProjectConfig.mk除了内核配置MTK的构建系统还有一个顶层配置。需要修改device/mediatek/{project_name}/ProjectConfig.mk文件确保以下配置为yesCUSTOM_KERNEL_ALSPS yes这个文件里的配置会直接影响整个Android系统构建时是否包含该传感器的HAL层支持。2.3 编译、烧录与初步验证完成以上配置后就可以开始编译内核了。在代码根目录执行./mk -t {project_name} kernel或相应的编译命令。第一次编译大概率会遇到各种错误比如头文件找不到、函数未定义、类型不匹配等。这时候需要耐心根据错误信息可能是路径问题也可能是内核API版本差异原厂驱动可能是针对老内核写的需要你做一些适配修改。编译通过后会生成新的boot.img。通过fastboot将其烧录到设备fastboot flash boot boot.img fastboot reboot设备重启时立刻通过adb logcat -b kernel或adb shell dmesg抓取内核日志。你要像侦探一样在日志里搜寻你的传感器“踪迹”。成功的标志是能看到类似这样的日志[ 2.345678] alsps probe start [ 2.345690] i2c i2c-2: vendor alsps detected at address 0x38 [ 2.345800] input: vendor-alsps as /devices/platform/soc/11009000.i2c/i2c-2/2-0038/input/input5 [ 2.345900] sensorlist_register_deviceinfo: namevendor-alsps, type1如果你看到了probe成功的日志甚至看到了sensorlist_register_deviceinfo的调用恭喜你驱动已经成功加载到内核了但是别高兴太早这离真正能用还差一步。2.4 应用层验证与常见“坑点”驱动加载了不代表应用层就能看到。你需要安装一个能读取传感器列表的APP比如Sensor Test或Device Info HW或者在命令行输入adb shell dumpsys sensorservice来查看传感器列表。最让人郁闷的情况就是内核日志明明显示probe成功了但应用层就是找不到这个传感器我踩过这个坑花了半天时间排查。问题根源往往出在驱动代码的probe函数里。很多原厂提供的示例驱动可能不完整漏掉了向MTK传感器框架注册设备信息的步骤。你必须确保在probe函数的最后调用了sensorlist_register_deviceinfo这个函数。这个函数的作用就是向系统的传感器服务“报到”告诉上层“我这儿有个新传感器类型是XX名字是YY可以来用了。” 如果没有这个“报到”流程传感器服务就不知道它的存在应用层自然也就找不到。另一个常见“坑”是DTS中的GPIO配置。如果中断引脚配置错误或者没有正确配置上拉/下拉可能导致传感器初始化失败或者无法产生中断。你可以用万用表量一下硬件引脚电压或者在驱动里添加更多调试打印来确认硬件状态。3. SCP侧Sensor驱动调试深入Tinysys的微型世界当传感器挂在SCP侧时调试的战场就从Linux内核转移到了FreeRTOS这个实时操作系统上。SCPMTK内部也叫Tinysys是一个独立的小系统专门处理低功耗任务。调试SCP侧驱动感觉更像在开发一个嵌入式单片机项目。3.1 SCP驱动框架与代码集成SCP侧的驱动代码路径和AP侧完全不同。你需要找到vendor/mediatek/proprietary/tinysys/freertos/这个目录这里是SCP系统的“大本营”。我以添加一个SCP侧的加速度计G-sensor为例。第一步放置驱动文件将原厂提供的SCP侧驱动文件通常是一些.c和.h文件名可能带nanohub或chre字样放到source/middleware/contexthub/MEMS_Driver/accGyro/目录下。注意SCP的代码结构比较固定一定要放到对应的传感器类型目录里。第二步修改ProjectConfig.mkSCP有自己的编译配置。你需要修改source/project/CM4_A/project/CustomerProject/ProjectConfig.mk文件添加一个开关来启用你的传感器例如CFG_VENDOR_GSENSOR_SUPPORT yes这个宏定义的名字最好有规律后面几步都会用到它。第三步控制编译链光定义了开关还不够需要让它真正控制代码的编译。修改source/project/CM4_A/project/platform/feature_config/chre.mk文件添加条件判断ifeq ($(CFG_VENDOR_GSENSOR_SUPPORT), yes) CHRE_APP_FILES $(MIDDLEWARE_PREBUILT)/contexthub/MEMS_Driver/accGyro/vendor_gsensor.chre endif这里假设原厂提供的是预编译的.chre二进制文件。如果是源码则需要将源文件添加到编译列表中。3.2 关键的Overlay配置与I2C设置SCP系统使用一种叫Overlay的机制来管理不同的传感器驱动。你可以把它理解为一个“驱动映射表”系统根据这个表来决定加载哪个具体的驱动文件。第四步修改Overlay头文件找到source/project/CM4_A/project/customerproject/inc/overlay_sensor.h。在OVERLAY_SECTION_ACCGYRO这个枚举中为你的新传感器添加一个IDtypedef enum { ... OVERLAY_ACCGYRO_VENDOR_GXYZ, // 你新增的传感器ID OVERLAY_ACCGYRO_NUM, } overlay_section_accgyro_t;第五步修改Overlay映射表接着修改source/project/CM4_A/project/customerproject/cust/overlay/overlay.c文件。找到accGyroOverlayRemap这个数组在里面添加一项将你刚才定义的枚举ID映射到具体的驱动文件名或二进制名const struct overlay_target accGyroOverlayRemap[] { ... {OVERLAY_ACCGYRO_VENDOR_GXYZ, vendor_gsensor}, // 映射关系 {OVERLAY_NONE, NULL}, };第六步配置I2C引脚这是硬件连接的关键一步。根据原理图修改source/project/CM4_A/project/customerproject/cust/accGyro/cust_accGyro.c文件。你需要找到类似cust_accGyro_hw的结构体数组在里面添加你传感器的I2C配置包括总线、地址、引脚等const struct accGyro_hw cust_accGyro_hw[CUST_ACCGYRO_NUM] { ... { .name vendor-gsensor, .i2c_num 2, // I2C总线号 .i2c_addr {0x68, 0xFF}, // 设备地址 .direction 0, // 传感器方向 .power_id SENSOR_POWER_NONE, // 电源管理 .power_vol VOL_DEFAULT, .firlen 0, .is_batch_supported false, }, };3.3 编译问题与日志调试配置完成后就可以编译SCP镜像了。命令通常是./mk -t {project_name} tinysys或scp。SCP的编译环境更敏感经常遇到两个经典问题问题一内存溢出Out of MemorySCP的RAM和Flash资源非常紧张。添加新驱动后很容易就爆了。错误信息会明确告诉你哪个段如IRAM、DRAM超了。你需要打开source/project/CM4_A/project/platform/Setting.ini文件调整对应内存区域的大小。但这就像玩跷跷板加大了这里可能就得减小那里需要反复权衡。有时你还需要优化驱动代码本身减少全局变量或大的缓冲区。问题二I2C通信失败这是最常遇到的问题。SCP日志通过adb logcat -b tinysys查看可能会显示I2C读写错误。除了检查DTS和cust_accGyro.c中的引脚配置是否正确外还有一个高级选项I2C DMA支持。如果传感器需要传输的数据块比较大或者频率要求高可以尝试在ProjectConfig.mk中开启DMA支持CFG_I2C_CH0_DMA_SUPPORT yes CFG_I2C_CH1_DMA_SUPPORT yes这能减轻CPU负担提高传输稳定性。我就遇到过一例不开DMA时数据偶尔出错开启后问题立刻消失。编译成功后会生成scp.img烧录并重启设备。重点观察SCP日志搜索你的传感器名字或I2C地址看到成功的加载和初始化日志才算SCP侧驱动真正落地。4. 调试进阶问题排查与传感器校准驱动点亮只是万里长征第一步让传感器稳定、准确地工作才是更大的挑战。这里分享几个我实战中遇到的棘手问题和解决思路。4.1 驱动加载失败与索引冲突在SCP侧我遇到过一种诡异情况日志显示它在尝试加载一个同类型的旧传感器驱动而不是我新加的结果当然是失败。排查后发现是传感器索引Index管理出了问题。SCP的驱动加载器有时会根据一个固定的索引顺序去加载驱动。如果上一个索引位置的驱动加载失败比如硬件不存在并且没有正确清理状态可能会导致后续索引的驱动加载流程被阻塞或错乱。解决方法是在驱动初始化函数中做好严格的错误检查。如果探测probe失败必须释放所有已申请的资源如I2C设备句柄、内存并将自己的索引状态复位确保不影响后续驱动的加载流程。有时还需要去查看SCP框架中关于传感器枚举的源代码理解其加载逻辑。4.2 中断唤醒与休眠问题这是传感器调试中最经典的功耗相关问题尤其在SAR接近感应传感器上。现象是手机亮屏时一切正常但灭屏一段时间后SAR传感器就失灵了无法唤醒屏幕。根本原因是系统进入深度休眠Suspend时AP侧可能关闭了某些电源域或时钟而传感器配置的中断引脚或供电线路恰好被关闭了。或者驱动中没有正确配置中断的唤醒能力。排查思路检查DTS确认中断引脚配置了正确的唤醒属性例如wakeup-source。检查驱动电源管理实现pm相关的回调函数如suspend,resume确保在系统休眠和唤醒时能正确配置传感器的低功耗模式并保持中断唤醒功能有效。使用工具验证通过cat /sys/kernel/debug/wakeup_sources命令查看你的传感器中断是否被记录为有效的唤醒源。4.3 传感器数据校准实战驱动通了数据能上报但数据不准这太常见了。像加速度计、陀螺仪、磁力计几乎没有不校准就能直接商用的。校准分为工厂校准和在线校准。工厂校准FAC是在产线上将设备放在精密治具上写入一组校准参数如偏移量Offset、灵敏度Scale到设备的持久化存储如NVRAM或特定寄存器。驱动在初始化时需要去读取这组参数并对原始数据(raw data)进行补偿。代码逻辑大致如下static void vendor_gsensor_calibrate_data(struct data_unit_t *data) { // 从NVRAM或文件读取校准参数 long long offset_x read_calibration_offset(X_AXIS); float scale_x read_calibration_scale(X_AXIS); // 应用补偿: 校准后数据 (原始数据 - 偏移量) * 灵敏度比例因子 >